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