Panda3D
Loading...
Searching...
No Matches
shaderGenerator.cxx
Go to the documentation of this file.
1/**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file shaderGenerator.cxx
10 * @author jyelon
11 * @date 2007-12-15
12 * @author weifengh, PandaSE
13 * @date 2010-04-15
14 * @author agartner, PandaSE
15 * @date 2010-04-16
16 * TextureStage::M_modulate (before this, separate textures formatted as
17 * alpha wiped color off resulting rgb)
18 */
19
20#include "shaderGenerator.h"
21
22#include "renderState.h"
23#include "shaderAttrib.h"
24#include "auxBitplaneAttrib.h"
25#include "alphaTestAttrib.h"
26#include "colorBlendAttrib.h"
27#include "transparencyAttrib.h"
28#include "textureAttrib.h"
29#include "colorAttrib.h"
30#include "lightAttrib.h"
31#include "materialAttrib.h"
32#include "lightRampAttrib.h"
33#include "texMatrixAttrib.h"
34#include "texGenAttrib.h"
35#include "colorScaleAttrib.h"
36#include "clipPlaneAttrib.h"
37#include "fogAttrib.h"
38#include "texture.h"
39#include "ambientLight.h"
40#include "directionalLight.h"
41#include "renderModeAttrib.h"
42#include "rescaleNormalAttrib.h"
43#include "pointLight.h"
44#include "sphereLight.h"
45#include "spotlight.h"
46#include "lightLensNode.h"
47#include "lvector4.h"
48#include "config_pgraphnodes.h"
49#include "pStatTimer.h"
50
51using std::string;
52
53TypeHandle ShaderGenerator::_type_handle;
54
55#ifdef HAVE_CG
56
57#define UNPACK_COMBINE_SRC(from, n) (TextureStage::CombineSource)((from >> ((uint16_t)n * 5u)) & 7u)
58#define UNPACK_COMBINE_OP(from, n) (TextureStage::CombineOperand)(((from >> (((uint16_t)n * 5u) + 3u)) & 3u) + 1u)
59
60static inline uint16_t
61pack_combine(TextureStage::CombineSource src0, TextureStage::CombineOperand op0,
62 TextureStage::CombineSource src1, TextureStage::CombineOperand op1,
63 TextureStage::CombineSource src2, TextureStage::CombineOperand op2) {
64 if (op0 == TextureStage::CO_undefined) op0 = TextureStage::CO_src_alpha;
65 if (op1 == TextureStage::CO_undefined) op1 = TextureStage::CO_src_alpha;
66 if (op2 == TextureStage::CO_undefined) op2 = TextureStage::CO_src_alpha;
67
68 return ((uint16_t)src0) | ((((uint16_t)op0 - 1u) & 3u) << 3u) |
69 ((uint16_t)src1 << 5u) | ((((uint16_t)op1 - 1u) & 3u) << 8u) |
70 ((uint16_t)src2 << 10u) | ((((uint16_t)op2 - 1u) & 3u) << 13u);
71}
72
73static PStatCollector lookup_collector("*:Munge:ShaderGen:Lookup");
74static PStatCollector synthesize_collector("*:Munge:ShaderGen:Synthesize");
75
76/**
77 * Create a ShaderGenerator. This has no state, except possibly to cache
78 * certain results. The given parameters contain information about the
79 * capabilities of the target for which the shader will be compiled.
80 */
81ShaderGenerator::
82ShaderGenerator(const Shader::ShaderCaps &caps, bool use_shadow_filter) :
83 _use_shadow_filter(use_shadow_filter) {
84
85#ifdef _WIN32
86 // Check matches all OpenGL profiles
87 _use_generic_attr = (caps._active_vprofile <= 6151 || caps._active_vprofile >= 7000);
88#else
89 _use_generic_attr = true;
90#endif
91
92 // Matches gp5fp and glslf profiles
93 _use_pointcoord = (caps._active_fprofile == 7017 || caps._active_fprofile == 7008);
94}
95
96/**
97 * Create a ShaderGenerator. This has no state, except possibly to cache
98 * certain results. The parameter that must be passed is the GSG to which the
99 * shader generator belongs.
100 */
101ShaderGenerator::
102ShaderGenerator(const GraphicsStateGuardianBase *gsg) : _use_pointcoord(false) {
103 // The ATTR# input semantics seem to map to generic vertex attributes in
104 // both arbvp1 and glslv, which behave more consistently. However, they
105 // don't exist in Direct3D 9. Use this silly little check for now.
106#ifdef _WIN32
107 _use_generic_attr = !gsg->get_supports_hlsl();
108#else
109 _use_generic_attr = true;
110#endif
111
112 // Do we want to use the ARB_shadow extension? This also allows us to use
113 // hardware shadows PCF.
114 _use_shadow_filter = gsg->get_supports_shadow_filter();
115}
116
117/**
118 * Destroy a ShaderGenerator.
119 */
120ShaderGenerator::
121~ShaderGenerator() {
122}
123
124/**
125 * Clears the register allocator. Initially, the pool of available registers
126 * is empty. You have to add some if you want there to be any.
127 */
128void ShaderGenerator::
129reset_register_allocator() {
130 _vtregs_used = 0;
131 _vcregs_used = 0;
132 _ftregs_used = 0;
133 _fcregs_used = 0;
134}
135
136/**
137 * Allocate a vreg.
138 */
139const char *ShaderGenerator::
140alloc_vreg() {
141 if (_use_generic_attr) {
142 // OpenGL case.
143 switch (_vtregs_used) {
144 case 0: _vtregs_used += 1; return "ATTR8";
145 case 1: _vtregs_used += 1; return "ATTR9";
146 case 2: _vtregs_used += 1; return "ATTR10";
147 case 3: _vtregs_used += 1; return "ATTR11";
148 case 4: _vtregs_used += 1; return "ATTR12";
149 case 5: _vtregs_used += 1; return "ATTR13";
150 case 6: _vtregs_used += 1; return "ATTR14";
151 case 7: _vtregs_used += 1; return "ATTR15";
152 }
153 switch (_vcregs_used) {
154 case 0: _vcregs_used += 1; return "ATTR3";
155 case 1: _vcregs_used += 1; return "ATTR4";
156 case 2: _vcregs_used += 1; return "ATTR5";
157 case 3: _vcregs_used += 1; return "ATTR6";
158 case 4: _vcregs_used += 1; return "ATTR7";
159 case 5: _vcregs_used += 1; return "ATTR1";
160 }
161 } else {
162 // DirectX 9 case.
163 switch (_vtregs_used) {
164 case 0: _vtregs_used += 1; return "TEXCOORD0";
165 case 1: _vtregs_used += 1; return "TEXCOORD1";
166 case 2: _vtregs_used += 1; return "TEXCOORD2";
167 case 3: _vtregs_used += 1; return "TEXCOORD3";
168 case 4: _vtregs_used += 1; return "TEXCOORD4";
169 case 5: _vtregs_used += 1; return "TEXCOORD5";
170 case 6: _vtregs_used += 1; return "TEXCOORD6";
171 case 7: _vtregs_used += 1; return "TEXCOORD7";
172 }
173 switch (_vcregs_used) {
174 case 0: _vcregs_used += 1; return "COLOR0";
175 case 1: _vcregs_used += 1; return "COLOR1";
176 }
177 }
178 // These don't exist in arbvp1, though they're reportedly supported by other
179 // profiles.
180 switch (_vtregs_used) {
181 case 8: _vtregs_used += 1; return "TEXCOORD8";
182 case 9: _vtregs_used += 1; return "TEXCOORD9";
183 case 10: _vtregs_used += 1; return "TEXCOORD10";
184 case 11: _vtregs_used += 1; return "TEXCOORD11";
185 case 12: _vtregs_used += 1; return "TEXCOORD12";
186 case 13: _vtregs_used += 1; return "TEXCOORD13";
187 case 14: _vtregs_used += 1; return "TEXCOORD14";
188 case 15: _vtregs_used += 1; return "TEXCOORD15";
189 }
190 return "UNKNOWN";
191}
192
193/**
194 * Allocate a freg.
195 */
196const char *ShaderGenerator::
197alloc_freg() {
198 switch (_ftregs_used) {
199 case 0: _ftregs_used += 1; return "TEXCOORD0";
200 case 1: _ftregs_used += 1; return "TEXCOORD1";
201 case 2: _ftregs_used += 1; return "TEXCOORD2";
202 case 3: _ftregs_used += 1; return "TEXCOORD3";
203 case 4: _ftregs_used += 1; return "TEXCOORD4";
204 case 5: _ftregs_used += 1; return "TEXCOORD5";
205 case 6: _ftregs_used += 1; return "TEXCOORD6";
206 case 7: _ftregs_used += 1; return "TEXCOORD7";
207 }
208 // NB. We really shouldn't use the COLOR fregs, since the clamping can have
209 // unexpected side-effects.
210 switch (_ftregs_used) {
211 case 8: _ftregs_used += 1; return "TEXCOORD8";
212 case 9: _ftregs_used += 1; return "TEXCOORD9";
213 case 10: _ftregs_used += 1; return "TEXCOORD10";
214 case 11: _ftregs_used += 1; return "TEXCOORD11";
215 case 12: _ftregs_used += 1; return "TEXCOORD12";
216 case 13: _ftregs_used += 1; return "TEXCOORD13";
217 case 14: _ftregs_used += 1; return "TEXCOORD14";
218 case 15: _ftregs_used += 1; return "TEXCOORD15";
219 }
220 return "UNKNOWN";
221}
222
223/**
224 * Analyzes the RenderState prior to shader generation. The results of the
225 * analysis are stored in instance variables of the Shader Generator.
226 */
227void ShaderGenerator::
228analyze_renderstate(ShaderKey &key, const RenderState *rs) {
229 const ShaderAttrib *shader_attrib;
230 rs->get_attrib_def(shader_attrib);
231 nassertv(shader_attrib->auto_shader());
232
233 // verify_enforce_attrib_lock();
234 const AuxBitplaneAttrib *aux_bitplane;
235 rs->get_attrib_def(aux_bitplane);
236 key._outputs = aux_bitplane->get_outputs();
237
238 // Decide whether or not we need alpha testing or alpha blending.
239 bool have_alpha_test = false;
240 bool have_alpha_blend = false;
241 const AlphaTestAttrib *alpha_test;
242 rs->get_attrib_def(alpha_test);
243 if (alpha_test->get_mode() != RenderAttrib::M_none &&
244 alpha_test->get_mode() != RenderAttrib::M_always) {
245 have_alpha_test = true;
246 }
247 const ColorBlendAttrib *color_blend;
248 rs->get_attrib_def(color_blend);
249 if (color_blend->get_mode() != ColorBlendAttrib::M_none) {
250 have_alpha_blend = true;
251 }
252 const TransparencyAttrib *transparency;
253 rs->get_attrib_def(transparency);
254 if (transparency->get_mode() == TransparencyAttrib::M_alpha ||
255 transparency->get_mode() == TransparencyAttrib::M_premultiplied_alpha ||
256 transparency->get_mode() == TransparencyAttrib::M_dual) {
257 have_alpha_blend = true;
258 }
259
260 // Decide what to send to the framebuffer alpha, if anything.
261 if (key._outputs & AuxBitplaneAttrib::ABO_glow) {
262 if (have_alpha_blend) {
263 key._outputs &= ~AuxBitplaneAttrib::ABO_glow;
264 key._disable_alpha_write = true;
265 } else if (have_alpha_test) {
266 // Subsume the alpha test in our shader.
267 key._alpha_test_mode = alpha_test->get_mode();
268 key._alpha_test_ref = alpha_test->get_reference_alpha();
269 }
270 }
271
272 if (have_alpha_blend || have_alpha_test) {
273 key._calc_primary_alpha = true;
274 }
275
276 // Determine whether or not vertex colors or flat colors are present.
277 const ColorAttrib *color;
278 rs->get_attrib_def(color);
279 key._color_type = color->get_color_type();
280
281 // Store the material flags (not the material values itself).
282 const MaterialAttrib *material;
283 rs->get_attrib_def(material);
284 Material *mat = material->get_material();
285 if (mat != nullptr) {
286 // The next time the Material flags change, the Material should cause the
287 // states to be rehashed.
288 mat->mark_used_by_auto_shader();
289 key._material_flags = mat->get_flags();
290
291 if ((key._material_flags & Material::F_base_color) != 0) {
292 key._material_flags |= (Material::F_diffuse | Material::F_specular | Material::F_ambient);
293 key._material_flags &= ~Material::F_base_color;
294 }
295 }
296
297 // Break out the lights by type.
298 const LightAttrib *la;
299 rs->get_attrib_def(la);
300 bool have_ambient = false;
301
302 for (size_t i = 0; i < la->get_num_on_lights(); ++i) {
303 NodePath np = la->get_on_light(i);
304 nassertv(!np.is_empty());
305 PandaNode *node = np.node();
306 nassertv(node != nullptr);
307
308 if (node->is_ambient_light()) {
309 have_ambient = true;
310 key._lighting = true;
311 } else {
312 ShaderKey::LightInfo info;
313 info._type = node->get_type();
314 info._flags = 0;
315
316 if (node->is_of_type(LightLensNode::get_class_type())) {
317 const LightLensNode *llnode = (const LightLensNode *)node;
318 if (shader_attrib->auto_shadow_on()) {
319 if (llnode->is_shadow_caster()) {
320 info._flags |= ShaderKey::LF_has_shadows;
321 }
322
323 // Make sure that the next time the shadows are toggled on this
324 // light, it triggers a state rehash.
325 llnode->mark_used_by_auto_shader();
326 }
327 if (llnode->has_specular_color()) {
328 info._flags |= ShaderKey::LF_has_specular_color;
329 }
330 }
331
332 key._lights.push_back(info);
333 key._lighting = true;
334 }
335 }
336
337 // See if there is a normal map, height map, gloss map, or glow map. Also
338 // check if anything has TexGen.
339
340 const TextureAttrib *texture;
341 rs->get_attrib_def(texture);
342 const TexGenAttrib *tex_gen;
343 rs->get_attrib_def(tex_gen);
344 const TexMatrixAttrib *tex_matrix;
345 rs->get_attrib_def(tex_matrix);
346
347 size_t num_textures = texture->get_num_on_stages();
348 for (size_t i = 0; i < num_textures; ++i) {
349 TextureStage *stage = texture->get_on_stage(i);
350 Texture *tex = texture->get_on_texture(stage);
351 nassertd(tex != nullptr) continue;
352
353 // Mark this TextureStage as having been used by the shader generator, so
354 // that the next time its properties change, it will cause the state to be
355 // rehashed to ensure that the shader is regenerated if needed.
356 stage->mark_used_by_auto_shader();
357
358 ShaderKey::TextureInfo info;
359 info._type = tex->get_texture_type();
360 info._mode = stage->get_mode();
361 info._flags = 0;
362 info._combine_rgb = 0u;
363 info._combine_alpha = 0u;
364
365 // While we look at the mode, determine whether we need to change the mode
366 // in order to reflect disabled features.
367 switch (info._mode) {
368 case TextureStage::M_modulate:
369 {
370 Texture::Format format = tex->get_format();
371 if (format != Texture::F_alpha) {
372 info._flags |= ShaderKey::TF_has_rgb;
373 }
374 if (Texture::has_alpha(format)) {
375 info._flags |= ShaderKey::TF_has_alpha;
376 }
377 }
378 break;
379
380 case TextureStage::M_modulate_glow:
381 if (shader_attrib->auto_glow_on()) {
382 info._flags = ShaderKey::TF_map_glow;
383 } else {
384 info._mode = TextureStage::M_modulate;
385 info._flags = ShaderKey::TF_has_rgb;
386 }
387 break;
388
389 case TextureStage::M_modulate_gloss:
390 if (shader_attrib->auto_gloss_on()) {
391 info._flags = ShaderKey::TF_map_gloss;
392 } else {
393 info._mode = TextureStage::M_modulate;
394 info._flags = ShaderKey::TF_has_rgb;
395 }
396 break;
397
398 case TextureStage::M_normal_height:
399 if (parallax_mapping_samples == 0) {
400 info._mode = TextureStage::M_normal;
401 } else if (!shader_attrib->auto_normal_on() ||
402 (key._lights.empty() && (key._outputs & AuxBitplaneAttrib::ABO_aux_normal) == 0)) {
403 info._mode = TextureStage::M_height;
404 info._flags = ShaderKey::TF_has_alpha;
405 } else {
406 info._flags = ShaderKey::TF_map_normal | ShaderKey::TF_map_height;
407 }
408 break;
409
410 case TextureStage::M_normal_gloss:
411 if (!shader_attrib->auto_gloss_on() || key._lights.empty()) {
412 info._mode = TextureStage::M_normal;
413 } else if (!shader_attrib->auto_normal_on()) {
414 info._mode = TextureStage::M_gloss;
415 } else {
416 info._flags = ShaderKey::TF_map_normal | ShaderKey::TF_map_gloss;
417 }
418 break;
419
420 case TextureStage::M_combine:
421 // If we have this rare, special mode, we encode all these extra
422 // parameters as flags to prevent bloating the shader key.
423 info._flags |= (uint32_t)stage->get_combine_rgb_mode() << ShaderKey::TF_COMBINE_RGB_MODE_SHIFT;
424 info._flags |= (uint32_t)stage->get_combine_alpha_mode() << ShaderKey::TF_COMBINE_ALPHA_MODE_SHIFT;
425 if (stage->get_rgb_scale() == 2) {
426 info._flags |= ShaderKey::TF_rgb_scale_2;
427 }
428 if (stage->get_rgb_scale() == 4) {
429 info._flags |= ShaderKey::TF_rgb_scale_4;
430 }
431 if (stage->get_alpha_scale() == 2) {
432 info._flags |= ShaderKey::TF_alpha_scale_2;
433 }
434 if (stage->get_alpha_scale() == 4) {
435 info._flags |= ShaderKey::TF_alpha_scale_4;
436 }
437
438 info._combine_rgb = pack_combine(
442 info._combine_alpha = pack_combine(
446
447 if (stage->uses_primary_color()) {
448 info._flags |= ShaderKey::TF_uses_primary_color;
449 }
450 if (stage->uses_last_saved_result()) {
451 info._flags |= ShaderKey::TF_uses_last_saved_result;
452 }
453 break;
454
455 default:
456 break;
457 }
458
459 // In fact, perhaps this stage should be disabled altogether?
460 bool skip = false;
461 switch (info._mode) {
462 case TextureStage::M_normal:
463 if (!shader_attrib->auto_normal_on() ||
464 (key._lights.empty() && (key._outputs & AuxBitplaneAttrib::ABO_aux_normal) == 0)) {
465 skip = true;
466 } else {
467 info._flags = ShaderKey::TF_map_normal;
468 }
469 break;
470 case TextureStage::M_glow:
471 if (shader_attrib->auto_glow_on()) {
472 info._flags = ShaderKey::TF_map_glow;
473 } else {
474 skip = true;
475 }
476 break;
477 case TextureStage::M_gloss:
478 if (!key._lights.empty() && shader_attrib->auto_gloss_on()) {
479 info._flags = ShaderKey::TF_map_gloss;
480 } else {
481 skip = true;
482 }
483 break;
484 case TextureStage::M_height:
485 if (parallax_mapping_samples > 0) {
486 info._flags = ShaderKey::TF_map_height;
487 } else {
488 skip = true;
489 }
490 break;
491 case TextureStage::M_emission:
492 info._flags = ShaderKey::TF_map_emission;
493 break;
494 default:
495 break;
496 }
497 // We can't just drop a disabled slot from the list, since then the
498 // indices for the texture stages will no longer match up. So we keep it,
499 // but set it to a noop state to indicate that it should be skipped.
500 if (skip) {
501 info._type = Texture::TT_1d_texture;
502 info._mode = TextureStage::M_modulate;
503 info._flags = 0;
504 key._textures.push_back(info);
505 continue;
506 }
507
508 // Check if this state has a texture matrix to transform the texture
509 // coordinates.
510 if (tex_matrix->has_stage(stage)) {
511 CPT(TransformState) transform = tex_matrix->get_transform(stage);
512 if (!transform->is_identity()) {
513 // Optimize for common case: if we only have a scale component, we
514 // can get away with fewer shader inputs and operations.
515 if (transform->has_components() && !transform->has_nonzero_shear() &&
516 transform->get_pos() == LPoint3::zero() &&
517 transform->get_hpr() == LVecBase3::zero()) {
518 info._flags |= ShaderKey::TF_has_texscale;
519 } else {
520 info._flags |= ShaderKey::TF_has_texmat;
521 }
522 }
523 }
524
525 if (tex_gen->has_stage(stage)) {
526 info._texcoord_name = nullptr;
527 info._gen_mode = tex_gen->get_mode(stage);
528 } else {
529 info._texcoord_name = stage->get_texcoord_name();
530 info._gen_mode = TexGenAttrib::M_off;
531 }
532
533 // Does this stage require saving its result?
534 if (stage->get_saved_result()) {
535 info._flags |= ShaderKey::TF_saved_result;
536 }
537
538 // Does this stage need a texcolor_# input?
539 if (stage->uses_color()) {
540 info._flags |= ShaderKey::TF_uses_color;
541 }
542
543 key._textures.push_back(info);
544 key._texture_flags |= info._flags;
545 }
546
547 // Does nothing use the saved result? If so, don't bother saving it.
548 if ((key._texture_flags & ShaderKey::TF_uses_last_saved_result) == 0 &&
549 (key._texture_flags & ShaderKey::TF_saved_result) != 0) {
550
552 for (it = key._textures.begin(); it != key._textures.end(); ++it) {
553 (*it)._flags &= ~ShaderKey::TF_saved_result;
554 }
555 key._texture_flags &= ~ShaderKey::TF_saved_result;
556 }
557
558 // Decide whether to separate ambient and diffuse calculations.
559 if (have_ambient) {
560 if (key._material_flags & Material::F_ambient) {
561 key._have_separate_ambient = true;
562 } else {
563 if (key._material_flags & Material::F_diffuse) {
564 key._have_separate_ambient = true;
565 } else {
566 key._have_separate_ambient = false;
567 }
568 }
569 }
570
571 if (shader_attrib->auto_ramp_on()) {
572 const LightRampAttrib *light_ramp;
573 if (rs->get_attrib(light_ramp)) {
574 key._light_ramp = light_ramp;
575 if (key._lighting) {
576 key._have_separate_ambient = true;
577 }
578 }
579 }
580
581 // Check for clip planes.
582 const ClipPlaneAttrib *clip_plane;
583 rs->get_attrib_def(clip_plane);
584 key._num_clip_planes = clip_plane->get_num_on_planes();
585
586 // Check for fog.
587 const FogAttrib *fog;
588 if (rs->get_attrib(fog) && !fog->is_off()) {
589 key._fog_mode = (int)fog->get_fog()->get_mode() + 1;
590 }
591
592 // Hijack this field for the perspective render-mode flag.
593 const RenderModeAttrib *render_mode;
594 if (rs->get_attrib(render_mode) && render_mode->get_perspective()) {
595 key._fog_mode |= 0x10000;
596 }
597}
598
599/**
600 * Rehashes all the states with generated shaders, removing the ones that are
601 * no longer fresh.
602 *
603 * Call this if certain state has changed in such a way as to require a rerun
604 * of the shader generator. This should be rare because in most cases, the
605 * shader generator will automatically regenerate shaders as necessary.
606 *
607 * @since 1.10.0
608 */
609void ShaderGenerator::
610rehash_generated_shaders() {
611 LightReMutexHolder holder(*RenderState::_states_lock);
612
613 // With uniquify-states turned on, we can actually go through all the states
614 // and check whether their generated shader is still OK.
615 size_t size = RenderState::_states->get_num_entries();
616 for (size_t si = 0; si < size; ++si) {
617 const RenderState *state = RenderState::_states->get_key(si);
618
619 if (state->_generated_shader != nullptr) {
620 ShaderKey key;
621 analyze_renderstate(key, state);
622
623 GeneratedShaders::const_iterator si;
624 si = _generated_shaders.find(key);
625 if (si != _generated_shaders.end()) {
626 if (si->second != state->_generated_shader) {
627 state->_generated_shader = si->second;
628 state->_munged_states.clear();
629 }
630 } else {
631 // We have not yet generated a shader for this modified state.
632 state->_generated_shader.clear();
633 state->_munged_states.clear();
634 }
635 }
636 }
637
638 // If we don't have uniquify-states, however, the above list won't contain
639 // all the state. We can change a global seq value to require Panda to
640 // rehash the states the next time it tries to render an object with it.
641 if (!uniquify_states) {
642 GraphicsStateGuardianBase::mark_rehash_generated_shaders();
643 }
644}
645
646/**
647 * Removes all previously generated shaders, requiring all shaders to be
648 * regenerated. Does not clear cache of compiled shaders.
649 *
650 * @since 1.10.0
651 */
652void ShaderGenerator::
653clear_generated_shaders() {
654 LightReMutexHolder holder(*RenderState::_states_lock);
655
656 size_t size = RenderState::_states->get_num_entries();
657 for (size_t si = 0; si < size; ++si) {
658 const RenderState *state = RenderState::_states->get_key(si);
659 state->_generated_shader.clear();
660 }
661
662 _generated_shaders.clear();
663
664 // If we don't have uniquify-states, we can't clear all the ShaderAttribs
665 // that are cached on the states, but we can simulate the effect of that.
666 if (!uniquify_states) {
667 GraphicsStateGuardianBase::mark_rehash_generated_shaders();
668 }
669}
670
671/**
672 * This is the routine that implements the next-gen fixed function pipeline by
673 * synthesizing a shader. It also takes care of setting up any buffers needed
674 * to produce the requested effects.
675 *
676 * Currently supports:
677 * - flat colors
678 * - vertex colors
679 * - lighting
680 * - normal maps, even multiple
681 * - gloss maps, but not multiple
682 * - glow maps, but not multiple
683 * - materials, but not updates to materials
684 * - 2D textures
685 * - all texture stage modes, including combine modes
686 * - color scale attrib
687 * - light ramps (for cartoon shading)
688 * - shadow mapping
689 * - most texgen modes
690 * - texmatrix
691 * - 1D/2D/3D textures, cube textures, 2D tex arrays
692 * - linear/exp/exp2 fog
693 * - animation
694 *
695 * Potential optimizations
696 * - omit attenuation calculations if attenuation off
697 *
698 */
699CPT(ShaderAttrib) ShaderGenerator::
700synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) {
701 ShaderKey key;
702
703 // First look up the state key in the table of already generated shaders.
704 {
705 PStatTimer timer(lookup_collector);
706 key._anim_spec = anim;
707 analyze_renderstate(key, rs);
708
709 GeneratedShaders::const_iterator si;
710 si = _generated_shaders.find(key);
711 if (si != _generated_shaders.end()) {
712 // We've already generated a shader for this state.
713 return si->second;
714 }
715 }
716
717 PStatTimer timer(synthesize_collector);
718
719 reset_register_allocator();
720
721 if (pgraphnodes_cat.is_debug()) {
722 pgraphnodes_cat.debug()
723 << "Generating shader for render state " << rs << ":\n";
724 rs->write(pgraphnodes_cat.debug(false), 2);
725 }
726
727 // These variables will hold the results of register allocation.
728
729 const char *tangent_freg = nullptr;
730 const char *binormal_freg = nullptr;
731 string tangent_input;
732 string binormal_input;
734 pvector<const char *> lightcoord_fregs;
735 const char *world_position_freg = nullptr;
736 const char *world_normal_freg = nullptr;
737 const char *eye_position_freg = nullptr;
738 const char *eye_normal_freg = nullptr;
739 const char *hpos_freg = nullptr;
740 const char *pointcoord_freg = nullptr;
741
742 const char *position_vreg;
743 const char *transform_weight_vreg = nullptr;
744 const char *normal_vreg;
745 const char *color_vreg = nullptr;
746 const char *transform_index_vreg = nullptr;
747
748 if (_use_generic_attr) {
749 position_vreg = "ATTR0";
750 transform_weight_vreg = "ATTR1";
751 normal_vreg = "ATTR2";
752 transform_index_vreg = "ATTR7";
753 } else {
754 position_vreg = "POSITION";
755 normal_vreg = "NORMAL";
756 }
757
758 if (key._color_type == ColorAttrib::T_vertex) {
759 // Reserve COLOR0
760 color_vreg = _use_generic_attr ? "ATTR3" : "COLOR0";
761 _vcregs_used = 1;
762 _fcregs_used = 1;
763 }
764
765 // Generate the shader's text.
766
767 std::ostringstream text;
768
769 text << "//Cg\n";
770
771 text << "/* Generated shader for render state:\n";
772 rs->write(text, 2);
773 text << "*/\n";
774
775 int map_index_glow = -1;
776 int map_index_gloss = -1;
777 int map_index_emission = -1;
778
779 // Figure out whether we need to calculate any of these variables.
780 bool need_world_position = (key._num_clip_planes > 0);
781 bool need_world_normal = false;
782 bool need_eye_position = key._lighting;
783 bool need_eye_normal = !key._lights.empty() || ((key._outputs & AuxBitplaneAttrib::ABO_aux_normal) != 0);
784 bool need_tangents = ((key._texture_flags & ShaderKey::TF_map_normal) != 0);
785 bool need_point_size = (key._fog_mode & 0x10000) != 0;
786 bool need_point_coord = false;
787
788 // If we have binormal/tangent and eye position, we can pack eye normal in
789 // the w channels of the others.
790 bool pack_eye_normal = need_eye_normal && need_tangents && need_eye_position;
791
792 bool have_specular = false;
793 if (key._lighting) {
794 if (key._material_flags & Material::F_specular) {
795 have_specular = true;
796 } else if ((key._texture_flags & ShaderKey::TF_map_gloss) != 0) {
797 have_specular = true;
798 }
799 }
800
801 bool need_color = false;
802 if (key._color_type != ColorAttrib::T_off) {
803 if (key._lighting) {
804 if (((key._material_flags & Material::F_ambient) == 0 && key._have_separate_ambient) ||
805 (key._material_flags & Material::F_diffuse) == 0 ||
806 key._calc_primary_alpha) {
807 need_color = true;
808 }
809 } else {
810 need_color = true;
811 }
812 }
813
814 bool need_eye_reflection = false;
815 bool need_fragment_view_to_world = false;
816
817 text << "void vshader(\n";
818 for (size_t i = 0; i < key._textures.size(); ++i) {
819 const ShaderKey::TextureInfo &tex = key._textures[i];
820
821 switch (tex._gen_mode) {
822 case TexGenAttrib::M_world_cube_map:
823 need_fragment_view_to_world = true;
824 case TexGenAttrib::M_eye_sphere_map:
825 case TexGenAttrib::M_eye_cube_map:
826 need_eye_position = true;
827 need_eye_normal = true;
828 need_eye_reflection = true;
829 break;
830 case TexGenAttrib::M_world_normal:
831 need_world_normal = true;
832 break;
833 case TexGenAttrib::M_eye_normal:
834 need_eye_normal = true;
835 break;
836 case TexGenAttrib::M_world_position:
837 need_world_position = true;
838 break;
839 case TexGenAttrib::M_eye_position:
840 need_eye_position = true;
841 break;
842 case TexGenAttrib::M_point_sprite:
843 need_point_coord = true;
844 break;
845 default:
846 break;
847 }
848
849 if (tex._texcoord_name != nullptr) {
850 if (texcoord_fregs.count(tex._texcoord_name) == 0) {
851 const char *freg = alloc_freg();
852 texcoord_fregs[tex._texcoord_name] = freg;
853
854 string tcname = tex._texcoord_name->join("_");
855 text << "\t in float4 vtx_" << tcname << " : " << alloc_vreg() << ",\n";
856 text << "\t out float4 l_" << tcname << " : " << freg << ",\n";
857 }
858 }
859
860 if (tangent_input.empty() &&
861 (tex._flags & (ShaderKey::TF_map_normal | ShaderKey::TF_map_height)) != 0) {
862 PT(InternalName) tangent_name = InternalName::get_tangent();
863 PT(InternalName) binormal_name = InternalName::get_binormal();
864
865 if (tex._texcoord_name != nullptr &&
866 tex._texcoord_name != InternalName::get_texcoord()) {
867 tangent_name = tangent_name->append(tex._texcoord_name->get_basename());
868 binormal_name = binormal_name->append(tex._texcoord_name->get_basename());
869 }
870
871 tangent_input = tangent_name->join("_");
872 binormal_input = binormal_name->join("_");
873
874 text << "\t in float4 vtx_" << tangent_input << " : " << alloc_vreg() << ",\n";
875 text << "\t in float4 vtx_" << binormal_input << " : " << alloc_vreg() << ",\n";
876 }
877
878 if (tex._flags & ShaderKey::TF_map_glow) {
879 map_index_glow = i;
880 }
881 if (tex._flags & ShaderKey::TF_map_gloss) {
882 map_index_gloss = i;
883 }
884 if (tex._flags & ShaderKey::TF_map_emission) {
885 map_index_emission = i;
886 }
887 }
888 if (need_tangents) {
889 tangent_freg = alloc_freg();
890 binormal_freg = alloc_freg();
891 text << "\t out float4 l_tangent : " << tangent_freg << ",\n";
892 text << "\t out float4 l_binormal : " << binormal_freg << ",\n";
893 }
894 if (need_color && key._color_type == ColorAttrib::T_vertex) {
895 text << "\t in float4 vtx_color : " << color_vreg << ",\n";
896 text << "\t out float4 l_color : COLOR0,\n";
897 }
898 if (need_world_position || need_world_normal) {
899 text << "\t uniform float4x4 trans_model_to_world,\n";
900 }
901 if (need_world_position) {
902 world_position_freg = alloc_freg();
903 text << "\t out float4 l_world_position : " << world_position_freg << ",\n";
904 }
905 if (need_world_normal) {
906 world_normal_freg = alloc_freg();
907 text << "\t out float4 l_world_normal : " << world_normal_freg << ",\n";
908 }
909 if (need_eye_position) {
910 text << "\t uniform float4x4 trans_model_to_view,\n";
911 eye_position_freg = alloc_freg();
912 text << "\t out float4 l_eye_position : " << eye_position_freg << ",\n";
913 }
914 else if (need_tangents || need_point_size) {
915 text << "\t uniform float4x4 trans_model_to_view,\n";
916 }
917 if (need_eye_normal) {
918 text << "\t uniform float4x4 tpose_view_to_model,\n";
919 if (!pack_eye_normal) {
920 eye_normal_freg = alloc_freg();
921 text << "\t out float3 l_eye_normal : " << eye_normal_freg << ",\n";
922 }
923 }
924 if ((key._texture_flags & ShaderKey::TF_map_height) != 0 || need_world_normal || need_eye_normal) {
925 text << "\t in float3 vtx_normal : " << normal_vreg << ",\n";
926 }
927 if (key._texture_flags & ShaderKey::TF_map_height) {
928 text << "\t uniform float4 mspos_view,\n";
929 text << "\t out float3 l_eyevec,\n";
930 }
931 if ((key._fog_mode & 0xffff) != 0) {
932 hpos_freg = alloc_freg();
933 text << "\t out float4 l_hpos : " << hpos_freg << ",\n";
934 }
935 for (size_t i = 0; i < key._lights.size(); ++i) {
936 const ShaderKey::LightInfo &light = key._lights[i];
937 if (light._flags & ShaderKey::LF_has_shadows) {
938 if (_ftregs_used >= 8) {
939 // We ran out of TEXCOORD registers. That means we have to do this
940 // calculation in the fragment shader, which is slower.
941 lightcoord_fregs.push_back(nullptr);
942 } else {
943 lightcoord_fregs.push_back(alloc_freg());
944 text << "\t uniform float4x4 mat_shadow_" << i << ",\n";
945 text << "\t out float4 l_lightcoord" << i << " : " << lightcoord_fregs[i] << ",\n";
946 }
947 } else {
948 lightcoord_fregs.push_back(nullptr);
949 }
950 }
951 if (key._anim_spec.get_animation_type() == GeomEnums::AT_hardware &&
952 key._anim_spec.get_num_transforms() > 0) {
953 int num_transforms;
954 if (key._anim_spec.get_indexed_transforms()) {
955 num_transforms = 120;
956 } else {
957 num_transforms = key._anim_spec.get_num_transforms();
958 }
959 if (transform_weight_vreg == nullptr) {
960 transform_weight_vreg = alloc_vreg();
961 }
962 if (transform_index_vreg == nullptr) {
963 transform_index_vreg = alloc_vreg();
964 }
965 text << "\t uniform float4x4 tbl_transforms[" << num_transforms << "],\n";
966 text << "\t in float4 vtx_transform_weight : " << transform_weight_vreg << ",\n";
967 if (key._anim_spec.get_indexed_transforms()) {
968 text << "\t in uint4 vtx_transform_index : " << transform_index_vreg << ",\n";
969 }
970 }
971 if (need_point_size) {
972 text << "\t uniform float3 attr_pointparams,\n";
973 text << "\t out float l_point_size : PSIZE,\n";
974 }
975 if (need_point_coord && !_use_pointcoord) {
976 if (!need_point_size) {
977 text << "\t uniform float3 attr_pointparams,\n";
978 }
979 pointcoord_freg = alloc_freg();
980 text << "\t out float3 l_pointcoord : " << pointcoord_freg << ",\n";
981 }
982 text << "\t in float4 vtx_position : " << position_vreg << ",\n";
983 text << "\t out float4 l_position : POSITION,\n";
984 text << "\t uniform float4x4 mat_modelproj\n";
985 text << ") {\n";
986
987 if (key._anim_spec.get_animation_type() == GeomEnums::AT_hardware &&
988 key._anim_spec.get_num_transforms() > 0) {
989
990 if (!key._anim_spec.get_indexed_transforms()) {
991 text << "\t const uint4 vtx_transform_index = uint4(0, 1, 2, 3);\n";
992 }
993
994 text << "\t float4x4 matrix = tbl_transforms[vtx_transform_index.x] * vtx_transform_weight.x";
995 if (key._anim_spec.get_num_transforms() > 1) {
996 text << "\n\t + tbl_transforms[vtx_transform_index.y] * vtx_transform_weight.y";
997 }
998 if (key._anim_spec.get_num_transforms() > 2) {
999 text << "\n\t + tbl_transforms[vtx_transform_index.z] * vtx_transform_weight.z";
1000 }
1001 if (key._anim_spec.get_num_transforms() > 3) {
1002 text << "\n\t + tbl_transforms[vtx_transform_index.w] * vtx_transform_weight.w";
1003 }
1004 text << ";\n";
1005
1006 text << "\t vtx_position = mul(matrix, vtx_position);\n";
1007 if (need_world_normal || need_eye_normal) {
1008 text << "\t vtx_normal = mul((float3x3)matrix, vtx_normal);\n";
1009 }
1010 }
1011
1012 text << "\t l_position = mul(mat_modelproj, vtx_position);\n";
1013 if ((key._fog_mode & 0xffff) != 0) {
1014 text << "\t l_hpos = l_position;\n";
1015 }
1016 if (need_world_position) {
1017 text << "\t l_world_position = mul(trans_model_to_world, vtx_position);\n";
1018 }
1019 if (need_world_normal) {
1020 text << "\t l_world_normal = mul(trans_model_to_world, float4(vtx_normal, 0));\n";
1021 }
1022 if (need_eye_position) {
1023 text << "\t l_eye_position = mul(trans_model_to_view, vtx_position);\n";
1024 }
1025 else if (need_point_size) {
1026 text << "\t float4 l_eye_position = mul(trans_model_to_view, vtx_position);\n";
1027 }
1028 if (need_point_size) {
1029 text << "\t l_point_size = attr_pointparams.y + attr_pointparams.z / length(l_eye_position.xyz);\n";
1030 }
1031 if (need_point_coord && !_use_pointcoord) {
1032 if (need_point_size) {
1033 text << "\t l_pointcoord = float3(l_position.xy / (2.0f * l_position.w) + float2(0.5f, 0.5f), 1.0f / l_point_size);\n";
1034 } else {
1035 // Static point size (non-perspective points)
1036 text << "\t l_pointcoord = float3(l_position.xy / (2.0f * l_position.w) + float2(0.5f, 0.5f), 1.0f / attr_pointparams.x);\n";
1037 }
1038 }
1039
1041 for (it = texcoord_fregs.begin(); it != texcoord_fregs.end(); ++it) {
1042 // Pass through all texcoord inputs as-is.
1043 string tcname = it->first->join("_");
1044 text << "\t l_" << tcname << " = vtx_" << tcname << ";\n";
1045 }
1046 if (need_color && key._color_type == ColorAttrib::T_vertex) {
1047 text << "\t l_color = vtx_color;\n";
1048 }
1049 if (need_tangents) {
1050 text << "\t l_tangent.xyz = normalize(mul((float3x3)trans_model_to_view, vtx_" << tangent_input << ".xyz));\n";
1051 text << "\t l_tangent.w = 0;\n";
1052 text << "\t l_binormal.xyz = normalize(mul((float3x3)trans_model_to_view, -vtx_" << binormal_input << ".xyz));\n";
1053 text << "\t l_binormal.w = 0;\n";
1054 }
1055 for (size_t i = 0; i < key._lights.size(); ++i) {
1056 if (key._lights[i]._flags & ShaderKey::LF_has_shadows) {
1057 if (lightcoord_fregs[i] != nullptr) {
1058 text << "\t l_lightcoord" << i << " = mul(mat_shadow_" << i << ", l_eye_position);\n";
1059 }
1060 }
1061 }
1062 if (key._texture_flags & ShaderKey::TF_map_height) {
1063 text << "\t float3 eyedir = mspos_view.xyz - vtx_position.xyz;\n";
1064 text << "\t l_eyevec.x = dot(vtx_" << tangent_input << ".xyz, eyedir);\n";
1065 text << "\t l_eyevec.y = dot(vtx_" << binormal_input << ".xyz, eyedir);\n";
1066 text << "\t l_eyevec.z = dot(vtx_normal, eyedir);\n";
1067 text << "\t l_eyevec = normalize(l_eyevec);\n";
1068 }
1069 if (need_eye_normal) {
1070 if (pack_eye_normal) {
1071 // We can pack the normal into the w channels of these unused varyings.
1072 text << "\t float3 eye_normal = normalize(mul((float3x3)tpose_view_to_model, vtx_normal));\n";
1073 text << "\t l_tangent.w = eye_normal.x;\n";
1074 text << "\t l_binormal.w = eye_normal.y;\n";
1075 text << "\t l_eye_position.w = eye_normal.z;\n";
1076 } else {
1077 text << "\t l_eye_normal = normalize(mul((float3x3)tpose_view_to_model, vtx_normal));\n";
1078 }
1079 }
1080 text << "}\n\n";
1081
1082 // Fragment shader
1083
1084 text << "void fshader(\n";
1085 if ((key._fog_mode & 0xffff) != 0) {
1086 text << "\t in float4 l_hpos : " << hpos_freg << ",\n";
1087 text << "\t in uniform float4 attr_fog,\n";
1088 text << "\t in uniform float4 attr_fogcolor,\n";
1089 }
1090 if (need_point_coord) {
1091 if (_use_pointcoord) {
1092 // In OpenGL, we have point coordinates available in glslf and gp5fp.
1093 text << "\t in float2 l_pointcoord : POINTCOORD,\n";
1094 } else {
1095 // In DirectX 9, we need to calculate them from the fragment coordinates.
1096 text << "\t in float3 l_pointcoord : " << pointcoord_freg << ",\n";
1097 text << "\t in float2 l_fragcoord : WPOS,\n";
1098 text << "\t in uniform float2 sys_windowsize,\n";
1099 }
1100 }
1101 if (need_world_position) {
1102 text << "\t in float4 l_world_position : " << world_position_freg << ",\n";
1103 }
1104 if (need_world_normal) {
1105 text << "\t in float4 l_world_normal : " << world_normal_freg << ",\n";
1106 }
1107 if (need_eye_position) {
1108 text << "\t in float4 l_eye_position : " << eye_position_freg << ",\n";
1109 }
1110 if (need_eye_normal && !pack_eye_normal) {
1111 text << "\t in float3 l_eye_normal : " << eye_normal_freg << ",\n";
1112 }
1113 for (it = texcoord_fregs.begin(); it != texcoord_fregs.end(); ++it) {
1114 text << "\t in float4 l_" << it->first->join("_") << " : " << it->second << ",\n";
1115 }
1116 for (size_t i = 0; i < key._textures.size(); ++i) {
1117 const ShaderKey::TextureInfo &tex = key._textures[i];
1118 if (tex._mode == TextureStage::M_modulate && tex._flags == 0) {
1119 // Skip this stage.
1120 continue;
1121 }
1122
1123 text << "\t uniform sampler" << texture_type_as_string(tex._type) << " tex_" << i << ",\n";
1124
1125 if (tex._flags & ShaderKey::TF_has_texscale) {
1126 text << "\t uniform float3 texscale_" << i << ",\n";
1127 } else if (tex._flags & ShaderKey::TF_has_texmat) {
1128 text << "\t uniform float4x4 texmat_" << i << ",\n";
1129 }
1130
1131 if (tex._flags & ShaderKey::TF_uses_color) {
1132 text << "\t uniform float4 texcolor_" << i << ",\n";
1133 }
1134
1135 if (tex._gen_mode == TexGenAttrib::M_constant) {
1136 text << "\t uniform float4 texconst_" << i << ",\n";
1137 }
1138 }
1139 if (need_fragment_view_to_world) {
1140 text << "\t uniform float3x3 trans_view_to_world,\n";
1141 }
1142 if (need_tangents) {
1143 text << "\t in float4 l_tangent : " << tangent_freg << ",\n";
1144 text << "\t in float4 l_binormal : " << binormal_freg << ",\n";
1145 }
1146 for (size_t i = 0; i < key._lights.size(); ++i) {
1147 text << "\t uniform float4x4 attr_light" << i << ",\n";
1148
1149 const ShaderKey::LightInfo &light = key._lights[i];
1150 if (light._flags & ShaderKey::LF_has_shadows) {
1151 if (light._type.is_derived_from(PointLight::get_class_type())) {
1152 text << "\t uniform samplerCUBE shadow_" << i << ",\n";
1153 } else if (_use_shadow_filter) {
1154 text << "\t uniform sampler2DShadow shadow_" << i << ",\n";
1155 } else {
1156 text << "\t uniform sampler2D shadow_" << i << ",\n";
1157 }
1158 if (lightcoord_fregs[i] != nullptr) {
1159 text << "\t in float4 l_lightcoord" << i << " : " << lightcoord_fregs[i] << ",\n";
1160 } else {
1161 text << "\t uniform float4x4 mat_shadow_" << i << ",\n";
1162 }
1163 }
1164 if (light._flags & ShaderKey::LF_has_specular_color) {
1165 text << "\t uniform float4 attr_lspec" << i << ",\n";
1166 }
1167 }
1168
1169 // Does the shader need material properties as input?
1170 if (key._material_flags & (Material::F_ambient | Material::F_diffuse | Material::F_emission | Material::F_specular)) {
1171 text << "\t uniform float4x4 attr_material,\n";
1172 }
1173 if (key._texture_flags & ShaderKey::TF_map_height) {
1174 text << "\t float3 l_eyevec,\n";
1175 }
1176 if (key._outputs & (AuxBitplaneAttrib::ABO_aux_normal | AuxBitplaneAttrib::ABO_aux_glow)) {
1177 text << "\t out float4 o_aux : COLOR1,\n";
1178 }
1179 text << "\t out float4 o_color : COLOR0,\n";
1180
1181 if (need_color) {
1182 if (key._color_type == ColorAttrib::T_vertex) {
1183 text << "\t in float4 l_color : COLOR0,\n";
1184 } else if (key._color_type == ColorAttrib::T_flat) {
1185 text << "\t uniform float4 attr_color,\n";
1186 }
1187 }
1188
1189 for (int i = 0; i < key._num_clip_planes; ++i) {
1190 text << "\t uniform float4 clipplane_" << i << ",\n";
1191 }
1192
1193 if (key._lighting) {
1194 text << "\t uniform float4 attr_ambient,\n";
1195 }
1196 text << "\t uniform float4 attr_colorscale\n";
1197 text << ") {\n";
1198
1199 // Clipping first!
1200 for (int i = 0; i < key._num_clip_planes; ++i) {
1201 text << "\t if (l_world_position.x * clipplane_" << i << ".x + l_world_position.y ";
1202 text << "* clipplane_" << i << ".y + l_world_position.z * clipplane_" << i << ".z + clipplane_" << i << ".w <= 0) {\n";
1203 text << "\t discard;\n";
1204 text << "\t }\n";
1205 }
1206
1207 // Reconstruct a packed normal vector.
1208 if (need_eye_normal && pack_eye_normal) {
1209 text << "\t float3 l_eye_normal = float3(l_tangent.w, l_binormal.w, l_eye_position.w);\n";
1210 }
1211 if (need_eye_normal) {
1212 text << "\t // Correct the surface normal for interpolation effects\n";
1213 text << "\t l_eye_normal = normalize(l_eye_normal);\n";
1214 }
1215 if (need_eye_reflection ||
1216 (need_eye_position && have_specular && (key._material_flags & Material::F_local) != 0 && !key._lights.empty())) {
1217 text << "\t float3 norm_eye_position = normalize(l_eye_position.xyz);\n";
1218 }
1219 if (need_eye_reflection) {
1220 text << "\t float3 eye_reflection = norm_eye_position - l_eye_normal * 2 * dot(l_eye_normal, norm_eye_position);\n";
1221 }
1222
1223 text << "\t float4 result;\n";
1224 if (key._outputs & (AuxBitplaneAttrib::ABO_aux_normal | AuxBitplaneAttrib::ABO_aux_glow)) {
1225 text << "\t o_aux = float4(0, 0, 0, 0);\n";
1226 }
1227 // Now generate any texture coordinates according to TexGenAttrib. If it
1228 // has a TexMatrixAttrib, also transform them.
1229 for (size_t i = 0; i < key._textures.size(); ++i) {
1230 const ShaderKey::TextureInfo &tex = key._textures[i];
1231 if (tex._mode == TextureStage::M_modulate && tex._flags == 0) {
1232 // Skip this stage.
1233 continue;
1234 }
1235 switch (tex._gen_mode) {
1236 case TexGenAttrib::M_off:
1237 // Cg seems to be able to optimize this temporary away when appropriate.
1238 text << "\t float4 texcoord" << i << " = l_" << tex._texcoord_name->join("_") << ";\n";
1239 break;
1240 case TexGenAttrib::M_eye_sphere_map:
1241 text << "\t float4 texcoord" << i << " = float4(eye_reflection.xz * (1.0f / (2.0f * length(eye_reflection + float3(0, -1, 0)))) + float2(0.5f, 0.5f), 0.0f, 1.0f);\n";
1242 break;
1243 case TexGenAttrib::M_world_cube_map:
1244 text << "\t float4 texcoord" << i << " = float4(mul(trans_view_to_world, eye_reflection), 1.0f);\n";
1245 break;
1246 case TexGenAttrib::M_eye_cube_map:
1247 text << "\t float4 texcoord" << i << " = float4(eye_reflection, 1.0f);\n";
1248 break;
1249 case TexGenAttrib::M_world_normal:
1250 text << "\t float4 texcoord" << i << " = l_world_normal;\n";
1251 break;
1252 case TexGenAttrib::M_eye_normal:
1253 text << "\t float4 texcoord" << i << " = float4(l_eye_normal, 1.0f);\n";
1254 break;
1255 case TexGenAttrib::M_world_position:
1256 text << "\t float4 texcoord" << i << " = l_world_position;\n";
1257 break;
1258 case TexGenAttrib::M_eye_position:
1259 text << "\t float4 texcoord" << i << " = float4(l_eye_position.xyz, 1.0f);\n";
1260 break;
1261 case TexGenAttrib::M_point_sprite:
1262 if (_use_pointcoord) {
1263 text << "\t float4 texcoord" << i << " = float4(l_pointcoord, 0.0f, 1.0f);\n";
1264 } else {
1265 if (!_use_generic_attr) {
1266 text << "\t l_fragcoord.y = sys_windowsize.y - l_fragcoord.y;\n";
1267 }
1268 text << "\t float4 texcoord" << i << " = float4((l_fragcoord - l_pointcoord.xy * sys_windowsize) * l_pointcoord.z * float2(1.0f, -1.0f) - float2(0.5f, 0.5f), 0.0f, 1.0f);\n";
1269 }
1270 break;
1271 case TexGenAttrib::M_constant:
1272 text << "\t float4 texcoord" << i << " = texconst_" << i << ";\n";
1273 break;
1274 default:
1275 text << "\t float4 texcoord" << i << " = float4(0, 0, 0, 0);\n";
1276 pgraphnodes_cat.error()
1277 << "Unsupported TexGenAttrib mode: " << tex._gen_mode << "\n";
1278 }
1279 if (tex._flags & ShaderKey::TF_has_texscale) {
1280 text << "\t texcoord" << i << ".xyz *= texscale_" << i << ";\n";
1281 } else if (tex._flags & ShaderKey::TF_has_texmat) {
1282 text << "\t texcoord" << i << " = mul(texmat_" << i << ", texcoord" << i << ");\n";
1283 text << "\t texcoord" << i << ".xyz /= texcoord" << i << ".w;\n";
1284 }
1285 }
1286 text << "\t // Fetch all textures.\n";
1287 for (size_t i = 0; i < key._textures.size(); ++i) {
1288 const ShaderKey::TextureInfo &tex = key._textures[i];
1289 if ((tex._flags & ShaderKey::TF_map_height) == 0) {
1290 continue;
1291 }
1292
1293 text << "\t float4 tex" << i << " = tex" << texture_type_as_string(tex._type);
1294 text << "(tex_" << i << ", texcoord" << i << ".";
1295 switch (tex._type) {
1296 case Texture::TT_cube_map_array:
1297 text << "xyzw";
1298 break;
1299 case Texture::TT_cube_map:
1300 case Texture::TT_3d_texture:
1301 case Texture::TT_2d_texture_array:
1302 text << "xyz";
1303 break;
1304 case Texture::TT_2d_texture:
1305 case Texture::TT_1d_texture_array:
1306 text << "xy";
1307 break;
1308 case Texture::TT_1d_texture:
1309 case Texture::TT_buffer_texture:
1310 text << "x";
1311 break;
1312 default:
1313 break;
1314 }
1315 text << ");\n\t float3 parallax_offset = l_eyevec.xyz * (tex" << i;
1316 if (tex._mode == TextureStage::M_normal_height ||
1317 (tex._flags & ShaderKey::TF_has_alpha) != 0) {
1318 text << ".aaa";
1319 } else {
1320 text << ".rgb";
1321 }
1322 text << " * 2.0 - 1.0) * " << parallax_mapping_scale << ";\n";
1323 // Additional samples
1324 for (int j = 0; j < parallax_mapping_samples - 1; ++j) {
1325 text << "\t parallax_offset = l_eyevec.xyz * (parallax_offset + (tex" << i;
1326 if (tex._mode == TextureStage::M_normal_height ||
1327 (tex._flags & ShaderKey::TF_has_alpha) != 0) {
1328 text << ".aaa";
1329 } else {
1330 text << ".rgb";
1331 }
1332 text << " * 2.0 - 1.0)) * " << 0.5 * parallax_mapping_scale << ";\n";
1333 }
1334 }
1335 for (size_t i = 0; i < key._textures.size(); ++i) {
1336 ShaderKey::TextureInfo &tex = key._textures[i];
1337 if (tex._mode == TextureStage::M_modulate && tex._flags == 0) {
1338 // Skip this stage.
1339 continue;
1340 }
1341 if ((tex._flags & ShaderKey::TF_map_height) == 0) {
1342 // Parallax mapping pushes the texture coordinates of the other textures
1343 // away from the camera.
1344 if (key._texture_flags & ShaderKey::TF_map_height) {
1345 text << "\t texcoord" << i << ".xyz -= parallax_offset;\n";
1346 }
1347 text << "\t float4 tex" << i << " = tex" << texture_type_as_string(tex._type);
1348 text << "(tex_" << i << ", texcoord" << i << ".";
1349 switch (tex._type) {
1350 case Texture::TT_cube_map_array:
1351 text << "xyzw";
1352 break;
1353 case Texture::TT_cube_map:
1354 case Texture::TT_3d_texture:
1355 case Texture::TT_2d_texture_array:
1356 text << "xyz";
1357 break;
1358 case Texture::TT_2d_texture:
1359 case Texture::TT_1d_texture_array:
1360 text << "xy";
1361 break;
1362 case Texture::TT_1d_texture:
1363 case Texture::TT_buffer_texture:
1364 text << "x";
1365 break;
1366 default:
1367 break;
1368 }
1369 text << ");\n";
1370 }
1371 }
1372 if (need_tangents) {
1373 text << "\t // Translate tangent-space normal in map to view-space.\n";
1374
1375 // Use Reoriented Normal Mapping to blend additional normal maps.
1376 bool is_first = true;
1377 for (size_t i = 0; i < key._textures.size(); ++i) {
1378 const ShaderKey::TextureInfo &tex = key._textures[i];
1379 if (tex._flags & ShaderKey::TF_map_normal) {
1380 if (is_first) {
1381 if (tex._flags & ShaderKey::TF_has_texscale) {
1382 text << "\t float3 tsnormal = normalize(((tex" << i << ".xyz * 2) - 1) * texscale_" << i << ");\n";
1383 } else if (tex._flags & ShaderKey::TF_has_texmat) {
1384 text << "\t float3 tsnormal = normalize(mul(texmat_" << i << ", float4((tex" << i << ".xyz * 2) - 1, 0)).xyz);\n";
1385 } else {
1386 text << "\t float3 tsnormal = normalize((tex" << i << ".xyz * 2) - 1);\n";
1387 }
1388 is_first = false;
1389 continue;
1390 }
1391 text << "\t tsnormal.z += 1;\n";
1392 text << "\t float3 tmp" << i << " = tex" << i << ".xyz * float3(-2, -2, 2) + float3(1, 1, -1);\n";
1393 if (tex._flags & ShaderKey::TF_has_texscale) {
1394 text << "\t tmp" << i << " *= texscale_" << i << ";\n";
1395 } else if (tex._flags & ShaderKey::TF_has_texmat) {
1396 text << "\t tmp" << i << " = mul(texmat_" << i << ", float4(tmp" << i << ", 0)).xyz;\n";
1397 }
1398 text << "\t tsnormal = normalize(tsnormal * dot(tsnormal, tmp" << i << ") - tmp" << i << " * tsnormal.z);\n";
1399 }
1400 }
1401 text << "\t l_eye_normal *= tsnormal.z;\n";
1402 text << "\t l_eye_normal += normalize(l_tangent.xyz) * tsnormal.x;\n";
1403 text << "\t l_eye_normal += normalize(l_binormal.xyz) * tsnormal.y;\n";
1404 text << "\t l_eye_normal = normalize(l_eye_normal);\n";
1405 }
1406 if (key._outputs & AuxBitplaneAttrib::ABO_aux_normal) {
1407 text << "\t // Output the camera-space surface normal\n";
1408 text << "\t o_aux.rgb = (l_eye_normal*0.5) + float3(0.5,0.5,0.5);\n";
1409 }
1410 if (key._lighting) {
1411 text << "\t // Begin view-space light calculations\n";
1412 text << "\t float ldist,lattenv,langle,lshad;\n";
1413 text << "\t float4 lcolor,lspec,lpoint,latten,ldir,leye;\n";
1414 text << "\t float3 lvec,lhalf;\n";
1415 if (key._have_separate_ambient) {
1416 text << "\t float4 tot_ambient = float4(0,0,0,0);\n";
1417 }
1418 text << "\t float4 tot_diffuse = float4(0,0,0,0);\n";
1419 if (have_specular) {
1420 text << "\t float4 tot_specular = float4(0,0,0,0);\n";
1421 if (key._material_flags & Material::F_specular) {
1422 text << "\t float shininess = attr_material[3].w;\n";
1423 } else {
1424 text << "\t float shininess = 50; // no shininess specified, using default\n";
1425 }
1426 }
1427 if (key._have_separate_ambient) {
1428 text << "\t tot_ambient += attr_ambient;\n";
1429 } else {
1430 text << "\t tot_diffuse += attr_ambient;\n";
1431 }
1432 }
1433 for (size_t i = 0; i < key._lights.size(); ++i) {
1434 const ShaderKey::LightInfo &light = key._lights[i];
1435
1436 if (light._flags & ShaderKey::LF_has_shadows) {
1437 if (lightcoord_fregs[i] == nullptr) {
1438 // We have to do this one in the fragment shader if we ran out of
1439 // varyings.
1440 text << "\t float4 l_lightcoord" << i << " = mul(mat_shadow_" << i << ", float4(l_eye_position.xyz, 1.0f));\n";
1441 }
1442 }
1443
1444 if (light._type.is_derived_from(DirectionalLight::get_class_type())) {
1445 text << "\t // Directional Light " << i << "\n";
1446 text << "\t lcolor = attr_light" << i << "[0];\n";
1447 if (light._flags & ShaderKey::LF_has_specular_color) {
1448 text << "\t lspec = attr_lspec" << i << ";\n";
1449 } else {
1450 text << "\t lspec = lcolor;\n";
1451 }
1452 text << "\t lvec = attr_light" << i << "[3].xyz;\n";
1453 text << "\t lcolor *= saturate(dot(l_eye_normal, lvec.xyz));\n";
1454 if (light._flags & ShaderKey::LF_has_shadows) {
1455 if (_use_shadow_filter) {
1456 text << "\t lshad = shadow2DProj(shadow_" << i << ", l_lightcoord" << i << ").r;\n";
1457 } else {
1458 text << "\t lshad = tex2Dproj(shadow_" << i << ", l_lightcoord" << i << ").r > l_lightcoord" << i << ".z / l_lightcoord" << i << ".w;\n";
1459 }
1460 text << "\t lcolor *= lshad;\n";
1461 text << "\t lspec *= lshad;\n";
1462 }
1463 text << "\t tot_diffuse += lcolor;\n";
1464 if (have_specular) {
1465 if (key._material_flags & Material::F_local) {
1466 text << "\t lhalf = normalize(lvec - norm_eye_position);\n";
1467 } else {
1468 text << "\t lhalf = normalize(lvec - float3(0, 1, 0));\n";
1469 }
1470 text << "\t lspec *= pow(saturate(dot(l_eye_normal, lhalf)), shininess);\n";
1471 text << "\t tot_specular += lspec;\n";
1472 }
1473 } else if (light._type.is_derived_from(PointLight::get_class_type())) {
1474 text << "\t // Point Light " << i << "\n";
1475 text << "\t lcolor = attr_light" << i << "[0];\n";
1476 if (light._flags & ShaderKey::LF_has_specular_color) {
1477 text << "\t lspec = attr_lspec" << i << ";\n";
1478 } else {
1479 text << "\t lspec = lcolor;\n";
1480 }
1481 text << "\t latten = attr_light" << i << "[1];\n";
1482 text << "\t lpoint = attr_light" << i << "[3];\n";
1483 text << "\t lvec = lpoint.xyz - l_eye_position.xyz;\n";
1484 text << "\t ldist = length(lvec);\n";
1485 text << "\t lvec /= ldist;\n";
1486 if (light._type.is_derived_from(SphereLight::get_class_type())) {
1487 text << "\t ldist = max(ldist, attr_light" << i << "[2].w);\n";
1488 }
1489 text << "\t lattenv = 1/(latten.x + latten.y*ldist + latten.z*ldist*ldist);\n";
1490 text << "\t lcolor *= lattenv * saturate(dot(l_eye_normal, lvec));\n";
1491 if (light._flags & ShaderKey::LF_has_shadows) {
1492 text << "\t ldist = max(abs(l_lightcoord" << i << ".x), max(abs(l_lightcoord" << i << ".y), abs(l_lightcoord" << i << ".z)));\n";
1493 text << "\t ldist = ((latten.w+lpoint.w)/(latten.w-lpoint.w))+((-2*latten.w*lpoint.w)/(ldist * (latten.w-lpoint.w)));\n";
1494 text << "\t lshad = texCUBE(shadow_" << i << ", l_lightcoord" << i << ".xyz).r >= ldist * 0.5 + 0.5;\n";
1495 text << "\t lcolor *= lshad;\n";
1496 text << "\t lspec *= lshad;\n";
1497 }
1498 text << "\t tot_diffuse += lcolor;\n";
1499 if (have_specular) {
1500 if (key._material_flags & Material::F_local) {
1501 text << "\t lhalf = normalize(lvec - norm_eye_position);\n";
1502 } else {
1503 text << "\t lhalf = normalize(lvec - float3(0, 1, 0));\n";
1504 }
1505 text << "\t lspec *= lattenv;\n";
1506 text << "\t lspec *= pow(saturate(dot(l_eye_normal, lhalf)), shininess);\n";
1507 text << "\t tot_specular += lspec;\n";
1508 }
1509 } else if (light._type.is_derived_from(Spotlight::get_class_type())) {
1510 text << "\t // Spot Light " << i << "\n";
1511 text << "\t lcolor = attr_light" << i << "[0];\n";
1512 if (light._flags & ShaderKey::LF_has_specular_color) {
1513 text << "\t lspec = attr_lspec" << i << ";\n";
1514 } else {
1515 text << "\t lspec = lcolor;\n";
1516 }
1517 text << "\t latten = attr_light" << i << "[1];\n";
1518 text << "\t ldir = attr_light" << i << "[2];\n";
1519 text << "\t lpoint = attr_light" << i << "[3];\n";
1520 text << "\t lvec = lpoint.xyz - l_eye_position.xyz;\n";
1521 text << "\t ldist = length(lvec);\n";
1522 text << "\t lvec /= ldist;\n";
1523 text << "\t langle = saturate(dot(ldir.xyz, lvec));\n";
1524 text << "\t lattenv = 1/(latten.x + latten.y*ldist + latten.z*ldist*ldist);\n";
1525 text << "\t lattenv *= pow(langle, latten.w);\n";
1526 text << "\t if (langle < ldir.w) lattenv = 0;\n";
1527 text << "\t lcolor *= lattenv * saturate(dot(l_eye_normal, lvec));\n";
1528 if (light._flags & ShaderKey::LF_has_shadows) {
1529 if (_use_shadow_filter) {
1530 text << "\t lshad = shadow2DProj(shadow_" << i << ", l_lightcoord" << i << ").r;\n";
1531 } else {
1532 text << "\t lshad = tex2Dproj(shadow_" << i << ", l_lightcoord" << i << ").r > l_lightcoord" << i << ".z / l_lightcoord" << i << ".w;\n";
1533 }
1534 text << "\t lcolor *= lshad;\n";
1535 text << "\t lspec *= lshad;\n";
1536 }
1537
1538 text << "\t tot_diffuse += lcolor;\n";
1539 if (have_specular) {
1540 if (key._material_flags & Material::F_local) {
1541 text << "\t lhalf = normalize(lvec - norm_eye_position);\n";
1542 } else {
1543 text << "\t lhalf = normalize(lvec - float3(0,1,0));\n";
1544 }
1545 text << "\t lspec *= lattenv;\n";
1546 text << "\t lspec *= pow(saturate(dot(l_eye_normal, lhalf)), shininess);\n";
1547 text << "\t tot_specular += lspec;\n";
1548 }
1549 }
1550 }
1551 if (key._lighting) {
1552 if (key._light_ramp != nullptr) {
1553 switch (key._light_ramp->get_mode()) {
1554 case LightRampAttrib::LRT_single_threshold:
1555 {
1556 PN_stdfloat t = key._light_ramp->get_threshold(0);
1557 PN_stdfloat l0 = key._light_ramp->get_level(0);
1558 text << "\t // Single-threshold light ramp\n";
1559 text << "\t float lr_in = dot(tot_diffuse.rgb, float3(0.33,0.34,0.33));\n";
1560 text << "\t float lr_scale = (lr_in < " << t << ") ? 0.0 : (" << l0 << "/lr_in);\n";
1561 text << "\t tot_diffuse = tot_diffuse * lr_scale;\n";
1562 break;
1563 }
1564 case LightRampAttrib::LRT_double_threshold:
1565 {
1566 PN_stdfloat t0 = key._light_ramp->get_threshold(0);
1567 PN_stdfloat t1 = key._light_ramp->get_threshold(1);
1568 PN_stdfloat l0 = key._light_ramp->get_level(0);
1569 PN_stdfloat l1 = key._light_ramp->get_level(1);
1570 text << "\t // Double-threshold light ramp\n";
1571 text << "\t float lr_in = dot(tot_diffuse.rgb, float3(0.33,0.34,0.33));\n";
1572 text << "\t float lr_out = 0.0;\n";
1573 text << "\t if (lr_in > " << t0 << ") lr_out=" << l0 << ";\n";
1574 text << "\t if (lr_in > " << t1 << ") lr_out=" << l1 << ";\n";
1575 text << "\t tot_diffuse = tot_diffuse * (lr_out / lr_in);\n";
1576 break;
1577 }
1578 default:
1579 break;
1580 }
1581 }
1582 text << "\t // Begin view-space light summation\n";
1583 if (key._material_flags & Material::F_emission) {
1584 text << "\t result = attr_material[2];\n";
1585 } else if (key._texture_flags & (ShaderKey::TF_map_glow | ShaderKey::TF_map_emission)) {
1586 text << "\t result = float4(1,1,1,0);\n";
1587 } else {
1588 text << "\t result = float4(0,0,0,0);\n";
1589 }
1590 if (key._texture_flags & ShaderKey::TF_map_emission) {
1591 text << "\t result.rgb *= tex" << map_index_emission << ".rgb;\n";
1592 }
1593 if (key._texture_flags & ShaderKey::TF_map_glow) {
1594 text << "\t result *= saturate(2 * (tex" << map_index_glow << ".a - 0.5));\n";
1595 }
1596 if (key._have_separate_ambient) {
1597 if (key._material_flags & Material::F_ambient) {
1598 text << "\t result += tot_ambient * attr_material[0];\n";
1599 } else if (key._color_type == ColorAttrib::T_vertex) {
1600 text << "\t result += tot_ambient * l_color;\n";
1601 } else if (key._color_type == ColorAttrib::T_flat) {
1602 text << "\t result += tot_ambient * attr_color;\n";
1603 } else {
1604 text << "\t result += tot_ambient;\n";
1605 }
1606 }
1607 if (key._material_flags & Material::F_diffuse) {
1608 text << "\t result += tot_diffuse * attr_material[1];\n";
1609 } else if (key._color_type == ColorAttrib::T_vertex) {
1610 text << "\t result += tot_diffuse * l_color;\n";
1611 } else if (key._color_type == ColorAttrib::T_flat) {
1612 text << "\t result += tot_diffuse * attr_color;\n";
1613 } else {
1614 text << "\t result += tot_diffuse;\n";
1615 }
1616 if (key._light_ramp == nullptr ||
1617 key._light_ramp->get_mode() == LightRampAttrib::LRT_default) {
1618 text << "\t result = saturate(result);\n";
1619 }
1620 text << "\t // End view-space light calculations\n";
1621
1622 // Combine in alpha, which bypasses lighting calculations. Use of lerp
1623 // here is a workaround for a radeon driver bug.
1624 if (key._calc_primary_alpha) {
1625 if (key._material_flags & Material::F_diffuse) {
1626 text << "\t result.a = attr_material[1].w;\n";
1627 } else if (key._color_type == ColorAttrib::T_vertex) {
1628 text << "\t result.a = l_color.a;\n";
1629 } else if (key._color_type == ColorAttrib::T_flat) {
1630 text << "\t result.a = attr_color.a;\n";
1631 } else {
1632 text << "\t result.a = 1;\n";
1633 }
1634 }
1635 } else {
1636 if (key._color_type == ColorAttrib::T_vertex) {
1637 text << "\t result = l_color;\n";
1638 } else if (key._color_type == ColorAttrib::T_flat) {
1639 text << "\t result = attr_color;\n";
1640 } else {
1641 text << "\t result = float4(1, 1, 1, 1);\n";
1642 }
1643 }
1644
1645 // Apply the color scale.
1646 text << "\t result *= attr_colorscale;\n";
1647
1648 // Store these if any stages will use it.
1649 if (key._texture_flags & ShaderKey::TF_uses_primary_color) {
1650 text << "\t float4 primary_color = result;\n";
1651 }
1652 if (key._texture_flags & ShaderKey::TF_uses_last_saved_result) {
1653 text << "\t float4 last_saved_result = result;\n";
1654 }
1655
1656 // Now loop through the textures to compose our magic blending formulas.
1657 for (size_t i = 0; i < key._textures.size(); ++i) {
1658 const ShaderKey::TextureInfo &tex = key._textures[i];
1659 TextureStage::CombineMode combine_rgb, combine_alpha;
1660
1661 switch (tex._mode) {
1662 case TextureStage::M_modulate:
1663 if ((tex._flags & ShaderKey::TF_has_rgb) != 0 &&
1664 (tex._flags & ShaderKey::TF_has_alpha) != 0) {
1665 text << "\t result.rgba *= tex" << i << ".rgba;\n";
1666 } else if (tex._flags & ShaderKey::TF_has_alpha) {
1667 text << "\t result.a *= tex" << i << ".a;\n";
1668 } else if (tex._flags & ShaderKey::TF_has_rgb) {
1669 text << "\t result.rgb *= tex" << i << ".rgb;\n";
1670 }
1671 break;
1672 case TextureStage::M_modulate_glow:
1673 case TextureStage::M_modulate_gloss:
1674 // in the case of glow or spec we currently see the specularity evenly
1675 // across the surface even if transparency or masking is present not
1676 // sure if this is desired behavior or not. *MOST* would construct a
1677 // spec map based off of what isisn't seen based on the masktransparency
1678 // this may have to be left alone for now agartner
1679 text << "\t result.rgb *= tex" << i << ";\n";
1680 break;
1681 case TextureStage::M_decal:
1682 text << "\t result.rgb = lerp(result, tex" << i << ", tex" << i << ".a).rgb;\n";
1683 break;
1684 case TextureStage::M_blend:
1685 text << "\t result.rgb = lerp(result.rgb, texcolor_" << i << ".rgb, tex" << i << ".rgb);\n";
1686 if (key._calc_primary_alpha) {
1687 text << "\t result.a *= tex" << i << ".a;\n";
1688 }
1689 break;
1690 case TextureStage::M_replace:
1691 text << "\t result = tex" << i << ";\n";
1692 break;
1693 case TextureStage::M_add:
1694 text << "\t result.rgb += tex" << i << ".rgb;\n";
1695 if (key._calc_primary_alpha) {
1696 text << "\t result.a *= tex" << i << ".a;\n";
1697 }
1698 break;
1699 case TextureStage::M_combine:
1700 combine_rgb = (TextureStage::CombineMode)((tex._flags & ShaderKey::TF_COMBINE_RGB_MODE_MASK) >> ShaderKey::TF_COMBINE_RGB_MODE_SHIFT);
1701 combine_alpha = (TextureStage::CombineMode)((tex._flags & ShaderKey::TF_COMBINE_ALPHA_MODE_MASK) >> ShaderKey::TF_COMBINE_ALPHA_MODE_SHIFT);
1702 if (combine_rgb == TextureStage::CM_dot3_rgba) {
1703 text << "\t result = ";
1704 text << combine_mode_as_string(tex, combine_rgb, false, i);
1705 text << ";\n";
1706 } else {
1707 text << "\t result.rgb = ";
1708 text << combine_mode_as_string(tex, combine_rgb, false, i);
1709 text << ";\n\t result.a = ";
1710 text << combine_mode_as_string(tex, combine_alpha, true, i);
1711 text << ";\n";
1712 }
1713 if (tex._flags & ShaderKey::TF_rgb_scale_2) {
1714 text << "\t result.rgb *= 2;\n";
1715 }
1716 if (tex._flags & ShaderKey::TF_rgb_scale_4) {
1717 text << "\t result.rgb *= 4;\n";
1718 }
1719 if (tex._flags & ShaderKey::TF_alpha_scale_2) {
1720 text << "\t result.a *= 2;\n";
1721 }
1722 if (tex._flags & ShaderKey::TF_alpha_scale_4) {
1723 text << "\t result.a *= 4;\n";
1724 }
1725 break;
1726 case TextureStage::M_blend_color_scale:
1727 text << "\t result.rgb = lerp(result.rgb, texcolor_" << i << ".rgb * attr_colorscale.rgb, tex" << i << ".rgb);\n";
1728 if (key._calc_primary_alpha) {
1729 text << "\t result.a *= texcolor_" << i << ".a * attr_colorscale.a;\n";
1730 }
1731 break;
1732 default:
1733 break;
1734 }
1735 if (tex._flags & ShaderKey::TF_saved_result) {
1736 text << "\t last_saved_result = result;\n";
1737 }
1738 }
1739
1740 if (key._alpha_test_mode != RenderAttrib::M_none) {
1741 text << "\t // Shader includes alpha test:\n";
1742 double ref = key._alpha_test_ref;
1743 switch (key._alpha_test_mode) {
1744 case RenderAttrib::M_never:
1745 text << "\t discard;\n";
1746 break;
1747 case RenderAttrib::M_less:
1748 text << "\t if (result.a >= " << ref << ") discard;\n";
1749 break;
1750 case RenderAttrib::M_equal:
1751 text << "\t if (result.a != " << ref << ") discard;\n";
1752 break;
1753 case RenderAttrib::M_less_equal:
1754 text << "\t if (result.a > " << ref << ") discard;\n";
1755 break;
1756 case RenderAttrib::M_greater:
1757 text << "\t if (result.a <= " << ref << ") discard;\n";
1758 break;
1759 case RenderAttrib::M_not_equal:
1760 text << "\t if (result.a == " << ref << ") discard;\n";
1761 break;
1762 case RenderAttrib::M_greater_equal:
1763 text << "\t if (result.a < " << ref << ") discard;\n";
1764 break;
1765 case RenderAttrib::M_none:
1766 case RenderAttrib::M_always:
1767 break;
1768 }
1769 }
1770
1771 if (key._outputs & AuxBitplaneAttrib::ABO_glow) {
1772 if (key._texture_flags & ShaderKey::TF_map_glow) {
1773 text << "\t result.a = tex" << map_index_glow << ".a;\n";
1774 } else {
1775 text << "\t result.a = 0.5;\n";
1776 }
1777 }
1778 if (key._outputs & AuxBitplaneAttrib::ABO_aux_glow) {
1779 if (key._texture_flags & ShaderKey::TF_map_glow) {
1780 text << "\t o_aux.a = tex" << map_index_glow << ".a;\n";
1781 } else {
1782 text << "\t o_aux.a = 0.5;\n";
1783 }
1784 }
1785
1786 if (have_specular) {
1787 if (key._material_flags & Material::F_specular) {
1788 text << "\t tot_specular *= attr_material[3];\n";
1789 }
1790 if (key._texture_flags & ShaderKey::TF_map_gloss) {
1791 text << "\t tot_specular *= tex" << map_index_gloss << ".a;\n";
1792 }
1793 text << "\t result.rgb = result.rgb + tot_specular.rgb;\n";
1794 }
1795 if (key._light_ramp != nullptr) {
1796 switch (key._light_ramp->get_mode()) {
1797 case LightRampAttrib::LRT_hdr0:
1798 text << "\t result.rgb = (result*result*result + result*result + result) / (result*result*result + result*result + result + 1);\n";
1799 break;
1800 case LightRampAttrib::LRT_hdr1:
1801 text << "\t result.rgb = (result*result + result) / (result*result + result + 1);\n";
1802 break;
1803 case LightRampAttrib::LRT_hdr2:
1804 text << "\t result.rgb = result / (result + 1);\n";
1805 break;
1806 default: break;
1807 }
1808 }
1809
1810 // Apply fog.
1811 if ((key._fog_mode & 0xffff) != 0) {
1812 Fog::Mode fog_mode = (Fog::Mode)((key._fog_mode & 0xffff) - 1);
1813 switch (fog_mode) {
1814 case Fog::M_linear:
1815 text << "\t result.rgb = lerp(attr_fogcolor.rgb, result.rgb, saturate((attr_fog.z - l_hpos.z) * attr_fog.w));\n";
1816 break;
1817 case Fog::M_exponential: // 1.442695f = 1 / log(2)
1818 text << "\t result.rgb = lerp(attr_fogcolor.rgb, result.rgb, saturate(exp2(attr_fog.x * l_hpos.z * -1.442695f)));\n";
1819 break;
1820 case Fog::M_exponential_squared:
1821 text << "\t result.rgb = lerp(attr_fogcolor.rgb, result.rgb, saturate(exp2(attr_fog.x * attr_fog.x * l_hpos.z * l_hpos.z * -1.442695f)));\n";
1822 break;
1823 }
1824 }
1825
1826 // The multiply is a workaround for a radeon driver bug. It's annoying as
1827 // heck, since it produces an extra instruction.
1828 text << "\t o_color = result * 1.000001;\n";
1829 if (key._alpha_test_mode != RenderAttrib::M_none) {
1830 text << "\t // Shader subsumes normal alpha test.\n";
1831 }
1832 if (key._disable_alpha_write) {
1833 text << "\t // Shader disables alpha write.\n";
1834 }
1835 text << "}\n";
1836
1837 if (pgraphnodes_cat.is_spam()) {
1838 pgraphnodes_cat.spam() << "Generated shader:\n"
1839 << text.str() << "\n";
1840 }
1841
1842 // Insert the shader into the shader attrib.
1843 PT(Shader) shader = Shader::make(text.str(), Shader::SL_Cg);
1844 nassertr(shader != nullptr, nullptr);
1845
1846 CPT(RenderAttrib) shattr = ShaderAttrib::make(shader);
1847 if (key._alpha_test_mode != RenderAttrib::M_none) {
1848 shattr = DCAST(ShaderAttrib, shattr)->set_flag(ShaderAttrib::F_subsume_alpha_test, true);
1849 }
1850 if (key._disable_alpha_write) {
1851 shattr = DCAST(ShaderAttrib, shattr)->set_flag(ShaderAttrib::F_disable_alpha_write, true);
1852 }
1853 if (need_point_size) {
1854 shattr = DCAST(ShaderAttrib, shattr)->set_flag(ShaderAttrib::F_shader_point_size, true);
1855 }
1856
1857 reset_register_allocator();
1858
1859 CPT(ShaderAttrib) attr = DCAST(ShaderAttrib, shattr);
1860 _generated_shaders[key] = attr;
1861 return attr;
1862}
1863
1864/**
1865 * This 'synthesizes' a combine mode into a string.
1866 */
1867string ShaderGenerator::
1868combine_mode_as_string(const ShaderKey::TextureInfo &info, TextureStage::CombineMode c_mode, bool alpha, short texindex) {
1869 std::ostringstream text;
1870 switch (c_mode) {
1871 case TextureStage::CM_modulate:
1872 text << combine_source_as_string(info, 0, alpha, texindex);
1873 text << " * ";
1874 text << combine_source_as_string(info, 1, alpha, texindex);
1875 break;
1876 case TextureStage::CM_add:
1877 text << combine_source_as_string(info, 0, alpha, texindex);
1878 text << " + ";
1879 text << combine_source_as_string(info, 1, alpha, texindex);
1880 break;
1881 case TextureStage::CM_add_signed:
1882 text << combine_source_as_string(info, 0, alpha, texindex);
1883 text << " + ";
1884 text << combine_source_as_string(info, 1, alpha, texindex);
1885 if (alpha) {
1886 text << " - 0.5";
1887 } else {
1888 text << " - float3(0.5, 0.5, 0.5)";
1889 }
1890 break;
1891 case TextureStage::CM_interpolate:
1892 text << "lerp(";
1893 text << combine_source_as_string(info, 1, alpha, texindex);
1894 text << ", ";
1895 text << combine_source_as_string(info, 0, alpha, texindex);
1896 text << ", ";
1897 text << combine_source_as_string(info, 2, alpha, texindex);
1898 text << ")";
1899 break;
1900 case TextureStage::CM_subtract:
1901 text << combine_source_as_string(info, 0, alpha, texindex);
1902 text << " - ";
1903 text << combine_source_as_string(info, 1, alpha, texindex);
1904 break;
1905 case TextureStage::CM_dot3_rgb:
1906 case TextureStage::CM_dot3_rgba:
1907 text << "4 * dot(";
1908 text << combine_source_as_string(info, 0, alpha, texindex);
1909 text << " - float3(0.5), ";
1910 text << combine_source_as_string(info, 1, alpha, texindex);
1911 text << " - float3(0.5))";
1912 break;
1913 case TextureStage::CM_replace:
1914 default: // Not sure if this is correct as default value.
1915 text << combine_source_as_string(info, 0, alpha, texindex);
1916 break;
1917 }
1918 return text.str();
1919}
1920
1921/**
1922 * This 'synthesizes' a combine source into a string.
1923 */
1924string ShaderGenerator::
1925combine_source_as_string(const ShaderKey::TextureInfo &info, short num, bool alpha, short texindex) {
1926 TextureStage::CombineSource c_src;
1927 TextureStage::CombineOperand c_op;
1928 if (!alpha) {
1929 c_src = UNPACK_COMBINE_SRC(info._combine_rgb, num);
1930 c_op = UNPACK_COMBINE_OP(info._combine_rgb, num);
1931 } else {
1932 c_src = UNPACK_COMBINE_SRC(info._combine_alpha, num);
1933 c_op = UNPACK_COMBINE_OP(info._combine_alpha, num);
1934 }
1935 std::ostringstream csource;
1936 if (c_op == TextureStage::CO_one_minus_src_color ||
1937 c_op == TextureStage::CO_one_minus_src_alpha) {
1938 csource << "saturate(1.0f - ";
1939 }
1940 switch (c_src) {
1941 case TextureStage::CS_undefined:
1942 case TextureStage::CS_texture:
1943 csource << "tex" << texindex;
1944 break;
1945 case TextureStage::CS_constant:
1946 csource << "texcolor_" << texindex;
1947 break;
1948 case TextureStage::CS_primary_color:
1949 csource << "primary_color";
1950 break;
1951 case TextureStage::CS_previous:
1952 csource << "result";
1953 break;
1954 case TextureStage::CS_constant_color_scale:
1955 csource << "attr_colorscale";
1956 break;
1957 case TextureStage::CS_last_saved_result:
1958 csource << "last_saved_result";
1959 break;
1960 }
1961 if (c_op == TextureStage::CO_one_minus_src_color ||
1962 c_op == TextureStage::CO_one_minus_src_alpha) {
1963 csource << ")";
1964 }
1965 if (c_op == TextureStage::CO_src_color || c_op == TextureStage::CO_one_minus_src_color) {
1966 csource << ".rgb";
1967 } else {
1968 csource << ".a";
1969 if (!alpha) {
1970 // Dunno if it's legal in the FPP at all, but let's just allow it.
1971 return "float3(" + csource.str() + ")";
1972 }
1973 }
1974 return csource.str();
1975}
1976
1977/**
1978 * Returns 1D, 2D, 3D or CUBE, depending on the given texture type.
1979 */
1980const char *ShaderGenerator::
1981texture_type_as_string(Texture::TextureType ttype) {
1982 switch (ttype) {
1983 case Texture::TT_1d_texture:
1984 return "1D";
1985
1986 case Texture::TT_2d_texture:
1987 return "2D";
1988
1989 case Texture::TT_3d_texture:
1990 return "3D";
1991
1992 case Texture::TT_2d_texture_array:
1993 return "2DARRAY";
1994
1995 case Texture::TT_cube_map:
1996 return "CUBE";
1997
1998 case Texture::TT_buffer_texture:
1999 return "BUF";
2000
2001 case Texture::TT_cube_map_array:
2002 return "CUBEARRAY";
2003
2004 case Texture::TT_1d_texture_array:
2005 return "1DARRAY";
2006
2007 default:
2008 pgraphnodes_cat.error() << "Unsupported texture type!\n";
2009 return "2D";
2010 }
2011}
2012
2013/**
2014 * Initializes the ShaderKey to the empty state.
2015 */
2016ShaderGenerator::ShaderKey::
2017ShaderKey() :
2018 _color_type(ColorAttrib::T_vertex),
2019 _material_flags(0),
2020 _texture_flags(0),
2021 _lighting(false),
2022 _have_separate_ambient(false),
2023 _fog_mode(0),
2024 _outputs(0),
2025 _calc_primary_alpha(false),
2026 _disable_alpha_write(false),
2027 _alpha_test_mode(RenderAttrib::M_none),
2028 _alpha_test_ref(0.0),
2029 _num_clip_planes(0),
2030 _light_ramp(nullptr) {
2031}
2032
2033/**
2034 * Returns true if this ShaderKey sorts less than the other one. This is an
2035 * arbitrary, but consistent ordering.
2036 */
2037bool ShaderGenerator::ShaderKey::
2038operator < (const ShaderKey &other) const {
2039 if (_anim_spec != other._anim_spec) {
2040 return _anim_spec < other._anim_spec;
2041 }
2042 if (_color_type != other._color_type) {
2043 return _color_type < other._color_type;
2044 }
2045 if (_material_flags != other._material_flags) {
2046 return _material_flags < other._material_flags;
2047 }
2048 if (_texture_flags != other._texture_flags) {
2049 return _texture_flags < other._texture_flags;
2050 }
2051 if (_textures.size() != other._textures.size()) {
2052 return _textures.size() < other._textures.size();
2053 }
2054 for (size_t i = 0; i < _textures.size(); ++i) {
2055 const ShaderKey::TextureInfo &tex = _textures[i];
2056 const ShaderKey::TextureInfo &other_tex = other._textures[i];
2057 if (tex._texcoord_name != other_tex._texcoord_name) {
2058 return tex._texcoord_name < other_tex._texcoord_name;
2059 }
2060 if (tex._type != other_tex._type) {
2061 return tex._type < other_tex._type;
2062 }
2063 if (tex._mode != other_tex._mode) {
2064 return tex._mode < other_tex._mode;
2065 }
2066 if (tex._gen_mode != other_tex._gen_mode) {
2067 return tex._gen_mode < other_tex._gen_mode;
2068 }
2069 if (tex._flags != other_tex._flags) {
2070 return tex._flags < other_tex._flags;
2071 }
2072 if (tex._combine_rgb != other_tex._combine_rgb) {
2073 return tex._combine_rgb < other_tex._combine_rgb;
2074 }
2075 if (tex._combine_alpha != other_tex._combine_alpha) {
2076 return tex._combine_alpha < other_tex._combine_alpha;
2077 }
2078 }
2079 if (_lights.size() != other._lights.size()) {
2080 return _lights.size() < other._lights.size();
2081 }
2082 for (size_t i = 0; i < _lights.size(); ++i) {
2083 const ShaderKey::LightInfo &light = _lights[i];
2084 const ShaderKey::LightInfo &other_light = other._lights[i];
2085 if (light._type != other_light._type) {
2086 return light._type < other_light._type;
2087 }
2088 if (light._flags != other_light._flags) {
2089 return light._flags < other_light._flags;
2090 }
2091 }
2092 if (_lighting != other._lighting) {
2093 return _lighting < other._lighting;
2094 }
2095 if (_have_separate_ambient != other._have_separate_ambient) {
2096 return _have_separate_ambient < other._have_separate_ambient;
2097 }
2098 if (_fog_mode != other._fog_mode) {
2099 return _fog_mode < other._fog_mode;
2100 }
2101 if (_outputs != other._outputs) {
2102 return _outputs < other._outputs;
2103 }
2104 if (_calc_primary_alpha != other._calc_primary_alpha) {
2105 return _calc_primary_alpha < other._calc_primary_alpha;
2106 }
2107 if (_disable_alpha_write != other._disable_alpha_write) {
2108 return _disable_alpha_write < other._disable_alpha_write;
2109 }
2110 if (_alpha_test_mode != other._alpha_test_mode) {
2111 return _alpha_test_mode < other._alpha_test_mode;
2112 }
2113 if (_alpha_test_ref != other._alpha_test_ref) {
2114 return _alpha_test_ref < other._alpha_test_ref;
2115 }
2116 if (_num_clip_planes != other._num_clip_planes) {
2117 return _num_clip_planes < other._num_clip_planes;
2118 }
2119 return _light_ramp < other._light_ramp;
2120}
2121
2122/**
2123 * Returns true if this ShaderKey is equal to the other one.
2124 */
2125bool ShaderGenerator::ShaderKey::
2126operator == (const ShaderKey &other) const {
2127 if (_anim_spec != other._anim_spec) {
2128 return false;
2129 }
2130 if (_color_type != other._color_type) {
2131 return false;
2132 }
2133 if (_material_flags != other._material_flags) {
2134 return false;
2135 }
2136 if (_texture_flags != other._texture_flags) {
2137 return false;
2138 }
2139 if (_textures.size() != other._textures.size()) {
2140 return false;
2141 }
2142 for (size_t i = 0; i < _textures.size(); ++i) {
2143 const ShaderKey::TextureInfo &tex = _textures[i];
2144 const ShaderKey::TextureInfo &other_tex = other._textures[i];
2145 if (tex._texcoord_name != other_tex._texcoord_name ||
2146 tex._type != other_tex._type ||
2147 tex._mode != other_tex._mode ||
2148 tex._gen_mode != other_tex._gen_mode ||
2149 tex._flags != other_tex._flags ||
2150 tex._combine_rgb != other_tex._combine_rgb ||
2151 tex._combine_alpha != other_tex._combine_alpha) {
2152 return false;
2153 }
2154 }
2155 if (_lights.size() != other._lights.size()) {
2156 return false;
2157 }
2158 for (size_t i = 0; i < _lights.size(); ++i) {
2159 const ShaderKey::LightInfo &light = _lights[i];
2160 const ShaderKey::LightInfo &other_light = other._lights[i];
2161 if (light._type != other_light._type ||
2162 light._flags != other_light._flags) {
2163 return false;
2164 }
2165 }
2166 return _lighting == other._lighting
2167 && _have_separate_ambient == other._have_separate_ambient
2168 && _fog_mode == other._fog_mode
2169 && _outputs == other._outputs
2170 && _calc_primary_alpha == other._calc_primary_alpha
2171 && _disable_alpha_write == other._disable_alpha_write
2172 && _alpha_test_mode == other._alpha_test_mode
2173 && _alpha_test_ref == other._alpha_test_ref
2174 && _num_clip_planes == other._num_clip_planes
2175 && _light_ramp == other._light_ramp;
2176}
2177
2178#else
2179
2180// Stubs for when building without Cg support.
2181ShaderGenerator::
2182ShaderGenerator(const GraphicsStateGuardianBase *gsg) {
2183}
2184
2185ShaderGenerator::
2186~ShaderGenerator() {
2187}
2188
2189void ShaderGenerator::
2190rehash_generated_shaders() {
2191}
2192
2193void ShaderGenerator::
2194clear_generated_shaders() {
2195}
2196
2197CPT(ShaderAttrib) ShaderGenerator::
2198synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) {
2199 return nullptr;
2200}
2201
2202#endif // HAVE_CG
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Enables or disables writing of pixel to framebuffer based on its alpha value relative to a reference ...
get_reference_alpha
Returns the alpha reference value.
get_mode
Returns the alpha write mode.
Modern frame buffers can have 'aux' bitplanes, which are additional bitplanes above and beyond the st...
get_outputs
Returns the AuxBitplaneAttrib output bits.
This functions similarly to a LightAttrib.
get_num_on_planes
Returns the number of planes that are enabled by the attribute.
Indicates what color should be applied to renderable geometry.
Definition colorAttrib.h:27
This specifies how colors are blended into the frame buffer, for special effects.
get_mode
Returns the blending mode for the RGB channels.
Applies a Fog to the geometry at and below this node.
Definition fogAttrib.h:25
get_fog
If the FogAttrib is not an 'off' FogAttrib, returns the fog that is associated.
Definition fogAttrib.h:38
bool is_off() const
Returns true if the FogAttrib is an 'off' FogAttrib, indicating that it should disable fog.
Definition fogAttrib.I:26
This object describes how the vertex animation, if any, represented in a GeomVertexData is encoded.
This is a base class for the GraphicsStateGuardian class, which is itself a base class for the variou...
Encodes a string name in a hash table, mapping it to a pointer.
Indicates which set of lights should be considered "on" to illuminate geometry at this level and belo...
Definition lightAttrib.h:30
get_num_on_lights
Returns the number of lights that are turned on by the attribute.
Definition lightAttrib.h:74
get_on_light
Returns the nth light turned on by the attribute, sorted in render order.
Definition lightAttrib.h:74
A derivative of Light and of Camera.
is_shadow_caster
Returns whether this light is configured to cast shadows or not.
bool has_specular_color() const
Returns true if this light defines a specular color, false if the specular color is derived automatic...
void mark_used_by_auto_shader() const
Marks this light as having been used by the auto shader.
A Light Ramp is any unary operator that takes a rendered pixel as input, and adjusts the brightness o...
Similar to MutexHolder, but for a light reentrant mutex.
Indicates which, if any, material should be applied to geometry.
get_material
If the MaterialAttrib is not an 'off' MaterialAttrib, returns the material that is associated.
Defines the way an object appears in the presence of lighting.
Definition material.h:43
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition nodePath.h:159
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition nodePath.I:188
PandaNode * node() const
Returns the referenced node of the path.
Definition nodePath.I:227
A lightweight class that represents a single element that may be timed and/or counted via stats.
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition pStatTimer.h:30
A basic node of the scene graph or data graph.
Definition pandaNode.h:65
virtual bool is_ambient_light() const
Returns true if this is an AmbientLight, false if it is not a light, or it is some other kind of ligh...
void ref() const
Explicitly increments the reference count.
This is the base class for a number of render attributes (other than transform) that may be set on sc...
Specifies how polygons are to be drawn.
get_perspective
Returns the perspective flag.
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition renderState.h:47
bool auto_shader() const
If true, then this ShaderAttrib does not contain an explicit shader - instead, it requests the automa...
const Key & get_key(size_t n) const
Returns the key in the nth entry of the table.
void clear()
Completely empties the table.
size_t get_num_entries() const
Returns the number of active entries in the table.
Computes texture coordinates for geometry automatically based on vertex position and/or normal.
Applies a transform matrix to UV's before they are rendered.
Indicates the set of TextureStages and their associated Textures that should be applied to (or remove...
get_num_on_stages
Returns the number of stages that are turned on by the attribute.
get_on_texture
Returns the texture associated with the indicated stage, or NULL if no texture is associated.
get_on_stage
Returns the nth stage turned on by the attribute, sorted in render order.
Defines the properties of a named stage of the multitexture pipeline.
bool uses_primary_color() const
Returns true if the TextureStage makes use of the CS_primary_color combine source.
CombineMode get_combine_alpha_mode() const
Get combine_alpha_mode.
get_saved_result
Returns the current setting of the saved_result flag.
CombineOperand get_combine_rgb_operand2() const
Get operand2 of combine_rgb_mode.
CombineSource get_combine_alpha_source1() const
Get source1 of combine_alpha_mode.
CombineMode get_combine_rgb_mode() const
Get the combine_rgb_mode.
CombineSource get_combine_rgb_source1() const
Get source1 of combine_rgb_mode.
CombineOperand get_combine_alpha_operand0() const
Get operand0 of combine_alpha_mode.
CombineOperand get_combine_rgb_operand1() const
Get operand1 of combine_rgb_mode.
CombineSource get_combine_alpha_source0() const
Get source0 of combine_alpha_mode.
get_rgb_scale
See set_rgb_scale().
get_alpha_scale
See set_alpha_scale().
CombineOperand get_combine_alpha_operand1() const
Get operand1 of combine_alpha_mode.
CombineSource get_combine_rgb_source0() const
Get source0 of combine_rgb_mode.
CombineSource get_combine_rgb_source2() const
Get source2 of combine_rgb_mode.
bool uses_color() const
Returns true if the TextureStage makes use of whatever color is specified in set_color(),...
CombineOperand get_combine_rgb_operand0() const
Get operand0 of combine_rgb_mode.
CombineSource get_combine_alpha_source2() const
Get source2 of combine_alpha_mode.
get_texcoord_name
See set_texcoord_name.
bool uses_last_saved_result() const
Returns true if the TextureStage makes use of the CS_primary_color combine source.
CombineOperand get_combine_alpha_operand2() const
Get operand2 of combine_alpha_mode.
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition texture.h:72
get_format
Returns the format of the texture, which represents both the semantic meaning of the texels and,...
Definition texture.h:371
static bool has_alpha(Format format)
Returns true if the indicated format includes alpha, false otherwise.
Definition texture.cxx:2632
Indicates a coordinate-system transform on vertices.
This controls the enabling of transparency.
get_mode
Returns the transparency mode.
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition typedObject.I:28
This is our own Panda specialization on the default STL map.
Definition pmap.h:49
This is our own Panda specialization on the default STL vector.
Definition pvector.h:42
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.