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  * @since 1.10.0
578  */
579 void ShaderGenerator::
580 rehash_generated_shaders() {
581  LightReMutexHolder holder(*RenderState::_states_lock);
582 
583  // With uniquify-states turned on, we can actually go through all the states
584  // and check whether their generated shader is still OK.
585  size_t size = RenderState::_states->get_num_entries();
586  for (size_t si = 0; si < size; ++si) {
587  const RenderState *state = RenderState::_states->get_key(si);
588 
589  if (state->_generated_shader != nullptr) {
590  ShaderKey key;
591  analyze_renderstate(key, state);
592 
593  GeneratedShaders::const_iterator si;
594  si = _generated_shaders.find(key);
595  if (si != _generated_shaders.end()) {
596  if (si->second != state->_generated_shader) {
597  state->_generated_shader = si->second;
598  state->_munged_states.clear();
599  }
600  } else {
601  // We have not yet generated a shader for this modified state.
602  state->_generated_shader.clear();
603  state->_munged_states.clear();
604  }
605  }
606  }
607 
608  // If we don't have uniquify-states, however, the above list won't contain
609  // all the state. We can change a global seq value to require Panda to
610  // rehash the states the next time it tries to render an object with it.
611  if (!uniquify_states) {
612  GraphicsStateGuardianBase::mark_rehash_generated_shaders();
613  }
614 }
615 
616 /**
617  * Removes all previously generated shaders, requiring all shaders to be
618  * regenerated. Does not clear cache of compiled shaders.
619  *
620  * @since 1.10.0
621  */
622 void ShaderGenerator::
623 clear_generated_shaders() {
624  LightReMutexHolder holder(*RenderState::_states_lock);
625 
626  size_t size = RenderState::_states->get_num_entries();
627  for (size_t si = 0; si < size; ++si) {
628  const RenderState *state = RenderState::_states->get_key(si);
629  state->_generated_shader.clear();
630  }
631 
632  _generated_shaders.clear();
633 
634  // If we don't have uniquify-states, we can't clear all the ShaderAttribs
635  // that are cached on the states, but we can simulate the effect of that.
636  if (!uniquify_states) {
637  GraphicsStateGuardianBase::mark_rehash_generated_shaders();
638  }
639 }
640 
641 /**
642  * This is the routine that implements the next-gen fixed function pipeline by
643  * synthesizing a shader. It also takes care of setting up any buffers needed
644  * to produce the requested effects.
645  *
646  * Currently supports:
647  * - flat colors
648  * - vertex colors
649  * - lighting
650  * - normal maps, even multiple
651  * - gloss maps, but not multiple
652  * - glow maps, but not multiple
653  * - materials, but not updates to materials
654  * - 2D textures
655  * - all texture stage modes, including combine modes
656  * - color scale attrib
657  * - light ramps (for cartoon shading)
658  * - shadow mapping
659  * - most texgen modes
660  * - texmatrix
661  * - 1D/2D/3D textures, cube textures, 2D tex arrays
662  * - linear/exp/exp2 fog
663  * - animation
664  *
665  * Potential optimizations
666  * - omit attenuation calculations if attenuation off
667  *
668  */
669 CPT(ShaderAttrib) ShaderGenerator::
670 synthesize_shader(const RenderState *rs, const GeomVertexAnimationSpec &anim) {
671  ShaderKey key;
672 
673  // First look up the state key in the table of already generated shaders.
674  {
675  PStatTimer timer(lookup_collector);
676  key._anim_spec = anim;
677  analyze_renderstate(key, rs);
678 
679  GeneratedShaders::const_iterator si;
680  si = _generated_shaders.find(key);
681  if (si != _generated_shaders.end()) {
682  // We've already generated a shader for this state.
683  return si->second;
684  }
685  }
686 
687  PStatTimer timer(synthesize_collector);
688 
689  reset_register_allocator();
690 
691  if (pgraphnodes_cat.is_debug()) {
692  pgraphnodes_cat.debug()
693  << "Generating shader for render state " << rs << ":\n";
694  rs->write(pgraphnodes_cat.debug(false), 2);
695  }
696 
697  // These variables will hold the results of register allocation.
698 
699  const char *tangent_freg = nullptr;
700  const char *binormal_freg = nullptr;
701  string tangent_input;
702  string binormal_input;
704  pvector<const char *> lightcoord_fregs;
705  const char *world_position_freg = nullptr;
706  const char *world_normal_freg = nullptr;
707  const char *eye_position_freg = nullptr;
708  const char *eye_normal_freg = nullptr;
709  const char *hpos_freg = nullptr;
710 
711  const char *position_vreg;
712  const char *transform_weight_vreg = nullptr;
713  const char *normal_vreg;
714  const char *color_vreg = nullptr;
715  const char *transform_index_vreg = nullptr;
716 
717  if (_use_generic_attr) {
718  position_vreg = "ATTR0";
719  transform_weight_vreg = "ATTR1";
720  normal_vreg = "ATTR2";
721  transform_index_vreg = "ATTR7";
722  } else {
723  position_vreg = "POSITION";
724  normal_vreg = "NORMAL";
725  }
726 
727  if (key._color_type == ColorAttrib::T_vertex) {
728  // Reserve COLOR0
729  color_vreg = _use_generic_attr ? "ATTR3" : "COLOR0";
730  _vcregs_used = 1;
731  _fcregs_used = 1;
732  }
733 
734  // Generate the shader's text.
735 
736  std::ostringstream text;
737 
738  text << "//Cg\n";
739 
740  text << "/* Generated shader for render state:\n";
741  rs->write(text, 2);
742  text << "*/\n";
743 
744  int map_index_glow = -1;
745  int map_index_gloss = -1;
746 
747  // Figure out whether we need to calculate any of these variables.
748  bool need_world_position = (key._num_clip_planes > 0);
749  bool need_world_normal = false;
750  bool need_eye_position = key._lighting;
751  bool need_eye_normal = !key._lights.empty() || ((key._outputs & AuxBitplaneAttrib::ABO_aux_normal) != 0);
752 
753  bool have_specular = false;
754  if (key._lighting) {
755  if (key._material_flags & Material::F_specular) {
756  have_specular = true;
757  } else if ((key._texture_flags & ShaderKey::TF_map_gloss) != 0) {
758  have_specular = true;
759  }
760  }
761 
762  bool need_color = false;
763  if (key._color_type != ColorAttrib::T_off) {
764  if (key._lighting) {
765  if (((key._material_flags & Material::F_ambient) == 0 && key._have_separate_ambient) ||
766  (key._material_flags & Material::F_diffuse) == 0 ||
767  key._calc_primary_alpha) {
768  need_color = true;
769  }
770  } else {
771  need_color = true;
772  }
773  }
774 
775  text << "void vshader(\n";
776  for (size_t i = 0; i < key._textures.size(); ++i) {
777  const ShaderKey::TextureInfo &tex = key._textures[i];
778 
779  switch (tex._gen_mode) {
780  case TexGenAttrib::M_world_position:
781  need_world_position = true;
782  break;
783  case TexGenAttrib::M_world_normal:
784  need_world_normal = true;
785  break;
786  case TexGenAttrib::M_eye_position:
787  need_eye_position = true;
788  break;
789  case TexGenAttrib::M_eye_normal:
790  need_eye_normal = true;
791  break;
792  default:
793  break;
794  }
795 
796  if (tex._texcoord_name != nullptr) {
797  if (texcoord_fregs.count(tex._texcoord_name) == 0) {
798  const char *freg = alloc_freg();
799  texcoord_fregs[tex._texcoord_name] = freg;
800 
801  string tcname = tex._texcoord_name->join("_");
802  text << "\t in float4 vtx_" << tcname << " : " << alloc_vreg() << ",\n";
803  text << "\t out float4 l_" << tcname << " : " << freg << ",\n";
804  }
805  }
806 
807  if (tangent_input.empty() &&
808  (tex._flags & (ShaderKey::TF_map_normal | ShaderKey::TF_map_height)) != 0) {
809  PT(InternalName) tangent_name = InternalName::get_tangent();
810  PT(InternalName) binormal_name = InternalName::get_binormal();
811 
812  if (tex._texcoord_name != nullptr &&
813  tex._texcoord_name != InternalName::get_texcoord()) {
814  tangent_name = tangent_name->append(tex._texcoord_name->get_basename());
815  binormal_name = binormal_name->append(tex._texcoord_name->get_basename());
816  }
817 
818  tangent_input = tangent_name->join("_");
819  binormal_input = binormal_name->join("_");
820 
821  text << "\t in float4 vtx_" << tangent_input << " : " << alloc_vreg() << ",\n";
822  text << "\t in float4 vtx_" << binormal_input << " : " << alloc_vreg() << ",\n";
823  }
824 
825  if (tex._flags & ShaderKey::TF_map_glow) {
826  map_index_glow = i;
827  }
828  if (tex._flags & ShaderKey::TF_map_gloss) {
829  map_index_gloss = i;
830  }
831  }
832  if (key._texture_flags & ShaderKey::TF_map_normal) {
833  tangent_freg = alloc_freg();
834  binormal_freg = alloc_freg();
835  text << "\t out float4 l_tangent : " << tangent_freg << ",\n";
836  text << "\t out float4 l_binormal : " << binormal_freg << ",\n";
837  }
838  if (need_color && key._color_type == ColorAttrib::T_vertex) {
839  text << "\t in float4 vtx_color : " << color_vreg << ",\n";
840  text << "\t out float4 l_color : COLOR0,\n";
841  }
842  if (need_world_position || need_world_normal) {
843  text << "\t uniform float4x4 trans_model_to_world,\n";
844  }
845  if (need_world_position) {
846  world_position_freg = alloc_freg();
847  text << "\t out float4 l_world_position : " << world_position_freg << ",\n";
848  }
849  if (need_world_normal) {
850  world_normal_freg = alloc_freg();
851  text << "\t out float4 l_world_normal : " << world_normal_freg << ",\n";
852  }
853  if (need_eye_position) {
854  text << "\t uniform float4x4 trans_model_to_view,\n";
855  eye_position_freg = alloc_freg();
856  text << "\t out float4 l_eye_position : " << eye_position_freg << ",\n";
857  } else if (key._texture_flags & ShaderKey::TF_map_normal) {
858  text << "\t uniform float4x4 trans_model_to_view,\n";
859  }
860  if (need_eye_normal) {
861  eye_normal_freg = alloc_freg();
862  text << "\t uniform float4x4 tpose_view_to_model,\n";
863  text << "\t out float3 l_eye_normal : " << eye_normal_freg << ",\n";
864  }
865  if ((key._texture_flags & ShaderKey::TF_map_height) != 0 || need_world_normal || need_eye_normal) {
866  text << "\t in float3 vtx_normal : " << normal_vreg << ",\n";
867  }
868  if (key._texture_flags & ShaderKey::TF_map_height) {
869  text << "\t uniform float4 mspos_view,\n";
870  text << "\t out float3 l_eyevec,\n";
871  }
872  for (size_t i = 0; i < key._lights.size(); ++i) {
873  const ShaderKey::LightInfo &light = key._lights[i];
874  if (light._flags & ShaderKey::LF_has_shadows) {
875  lightcoord_fregs.push_back(alloc_freg());
876  text << "\t uniform float4x4 mat_shadow_" << i << ",\n";
877  text << "\t out float4 l_lightcoord" << i << " : " << lightcoord_fregs[i] << ",\n";
878  } else {
879  lightcoord_fregs.push_back(nullptr);
880  }
881  }
882  if (key._fog_mode != 0) {
883  hpos_freg = alloc_freg();
884  text << "\t out float4 l_hpos : " << hpos_freg << ",\n";
885  }
886  if (key._anim_spec.get_animation_type() == GeomEnums::AT_hardware &&
887  key._anim_spec.get_num_transforms() > 0) {
888  int num_transforms;
889  if (key._anim_spec.get_indexed_transforms()) {
890  num_transforms = 120;
891  } else {
892  num_transforms = key._anim_spec.get_num_transforms();
893  }
894  if (transform_weight_vreg == nullptr) {
895  transform_weight_vreg = alloc_vreg();
896  }
897  if (transform_index_vreg == nullptr) {
898  transform_index_vreg = alloc_vreg();
899  }
900  text << "\t uniform float4x4 tbl_transforms[" << num_transforms << "],\n";
901  text << "\t in float4 vtx_transform_weight : " << transform_weight_vreg << ",\n";
902  if (key._anim_spec.get_indexed_transforms()) {
903  text << "\t in uint4 vtx_transform_index : " << transform_index_vreg << ",\n";
904  }
905  }
906  text << "\t in float4 vtx_position : " << position_vreg << ",\n";
907  text << "\t out float4 l_position : POSITION,\n";
908  text << "\t uniform float4x4 mat_modelproj\n";
909  text << ") {\n";
910 
911  if (key._anim_spec.get_animation_type() == GeomEnums::AT_hardware &&
912  key._anim_spec.get_num_transforms() > 0) {
913 
914  if (!key._anim_spec.get_indexed_transforms()) {
915  text << "\t const uint4 vtx_transform_index = uint4(0, 1, 2, 3);\n";
916  }
917 
918  text << "\t float4x4 matrix = tbl_transforms[vtx_transform_index.x] * vtx_transform_weight.x";
919  if (key._anim_spec.get_num_transforms() > 1) {
920  text << "\n\t + tbl_transforms[vtx_transform_index.y] * vtx_transform_weight.y";
921  }
922  if (key._anim_spec.get_num_transforms() > 2) {
923  text << "\n\t + tbl_transforms[vtx_transform_index.z] * vtx_transform_weight.z";
924  }
925  if (key._anim_spec.get_num_transforms() > 3) {
926  text << "\n\t + tbl_transforms[vtx_transform_index.w] * vtx_transform_weight.w";
927  }
928  text << ";\n";
929 
930  text << "\t vtx_position = mul(matrix, vtx_position);\n";
931  if (need_world_normal || need_eye_normal) {
932  text << "\t vtx_normal = mul((float3x3)matrix, vtx_normal);\n";
933  }
934  }
935 
936  text << "\t l_position = mul(mat_modelproj, vtx_position);\n";
937  if (key._fog_mode != 0) {
938  text << "\t l_hpos = l_position;\n";
939  }
940  if (need_world_position) {
941  text << "\t l_world_position = mul(trans_model_to_world, vtx_position);\n";
942  }
943  if (need_world_normal) {
944  text << "\t l_world_normal = mul(trans_model_to_world, float4(vtx_normal, 0));\n";
945  }
946  if (need_eye_position) {
947  text << "\t l_eye_position = mul(trans_model_to_view, vtx_position);\n";
948  }
949  if (need_eye_normal) {
950  text << "\t l_eye_normal = normalize(mul((float3x3)tpose_view_to_model, vtx_normal));\n";
951  }
953  for (it = texcoord_fregs.begin(); it != texcoord_fregs.end(); ++it) {
954  // Pass through all texcoord inputs as-is.
955  string tcname = it->first->join("_");
956  text << "\t l_" << tcname << " = vtx_" << tcname << ";\n";
957  }
958  if (need_color && key._color_type == ColorAttrib::T_vertex) {
959  text << "\t l_color = vtx_color;\n";
960  }
961  if (key._texture_flags & ShaderKey::TF_map_normal) {
962  text << "\t l_tangent.xyz = normalize(mul((float3x3)trans_model_to_view, vtx_" << tangent_input << ".xyz));\n";
963  text << "\t l_tangent.w = 0;\n";
964  text << "\t l_binormal.xyz = normalize(mul((float3x3)trans_model_to_view, -vtx_" << binormal_input << ".xyz));\n";
965  text << "\t l_binormal.w = 0;\n";
966  }
967  for (size_t i = 0; i < key._lights.size(); ++i) {
968  if (key._lights[i]._flags & ShaderKey::LF_has_shadows) {
969  text << "\t l_lightcoord" << i << " = mul(mat_shadow_" << i << ", l_eye_position);\n";
970  }
971  }
972  if (key._texture_flags & ShaderKey::TF_map_height) {
973  text << "\t float3 eyedir = mspos_view.xyz - vtx_position.xyz;\n";
974  text << "\t l_eyevec.x = dot(vtx_" << tangent_input << ".xyz, eyedir);\n";
975  text << "\t l_eyevec.y = dot(vtx_" << binormal_input << ".xyz, eyedir);\n";
976  text << "\t l_eyevec.z = dot(vtx_normal, eyedir);\n";
977  text << "\t l_eyevec = normalize(l_eyevec);\n";
978  }
979  text << "}\n\n";
980 
981  // Fragment shader
982 
983  text << "void fshader(\n";
984  if (key._fog_mode != 0) {
985  text << "\t in float4 l_hpos : " << hpos_freg << ",\n";
986  text << "\t in uniform float4 attr_fog,\n";
987  text << "\t in uniform float4 attr_fogcolor,\n";
988  }
989  if (need_world_position) {
990  text << "\t in float4 l_world_position : " << world_position_freg << ",\n";
991  }
992  if (need_world_normal) {
993  text << "\t in float4 l_world_normal : " << world_normal_freg << ",\n";
994  }
995  if (need_eye_position) {
996  text << "\t in float4 l_eye_position : " << eye_position_freg << ",\n";
997  }
998  if (need_eye_normal) {
999  text << "\t in float3 l_eye_normal : " << eye_normal_freg << ",\n";
1000  }
1001  for (it = texcoord_fregs.begin(); it != texcoord_fregs.end(); ++it) {
1002  text << "\t in float4 l_" << it->first->join("_") << " : " << it->second << ",\n";
1003  }
1004  for (size_t i = 0; i < key._textures.size(); ++i) {
1005  const ShaderKey::TextureInfo &tex = key._textures[i];
1006  if (tex._mode == TextureStage::M_modulate && tex._flags == 0) {
1007  // Skip this stage.
1008  continue;
1009  }
1010 
1011  text << "\t uniform sampler" << texture_type_as_string(tex._type) << " tex_" << i << ",\n";
1012 
1013  if (tex._flags & ShaderKey::TF_has_texscale) {
1014  text << "\t uniform float3 texscale_" << i << ",\n";
1015  } else if (tex._flags & ShaderKey::TF_has_texmat) {
1016  text << "\t uniform float4x4 texmat_" << i << ",\n";
1017  }
1018 
1019  if (tex._flags & ShaderKey::TF_uses_color) {
1020  text << "\t uniform float4 texcolor_" << i << ",\n";
1021  }
1022  }
1023  if (key._texture_flags & ShaderKey::TF_map_normal) {
1024  text << "\t in float3 l_tangent : " << tangent_freg << ",\n";
1025  text << "\t in float3 l_binormal : " << binormal_freg << ",\n";
1026  }
1027  for (size_t i = 0; i < key._lights.size(); ++i) {
1028  text << "\t uniform float4x4 attr_light" << i << ",\n";
1029 
1030  const ShaderKey::LightInfo &light = key._lights[i];
1031  if (light._flags & ShaderKey::LF_has_shadows) {
1032  if (light._type.is_derived_from(PointLight::get_class_type())) {
1033  text << "\t uniform samplerCUBE shadow_" << i << ",\n";
1034  } else if (_use_shadow_filter) {
1035  text << "\t uniform sampler2DShadow shadow_" << i << ",\n";
1036  } else {
1037  text << "\t uniform sampler2D shadow_" << i << ",\n";
1038  }
1039  text << "\t in float4 l_lightcoord" << i << " : " << lightcoord_fregs[i] << ",\n";
1040  }
1041  if (light._flags & ShaderKey::LF_has_specular_color) {
1042  text << "\t uniform float4 attr_lspec" << i << ",\n";
1043  }
1044  }
1045 
1046  // Does the shader need material properties as input?
1047  if (key._material_flags & (Material::F_ambient | Material::F_diffuse | Material::F_emission | Material::F_specular)) {
1048  text << "\t uniform float4x4 attr_material,\n";
1049  }
1050  if (key._texture_flags & ShaderKey::TF_map_height) {
1051  text << "\t float3 l_eyevec,\n";
1052  }
1053  if (key._outputs & (AuxBitplaneAttrib::ABO_aux_normal | AuxBitplaneAttrib::ABO_aux_glow)) {
1054  text << "\t out float4 o_aux : COLOR1,\n";
1055  }
1056  text << "\t out float4 o_color : COLOR0,\n";
1057 
1058  if (need_color) {
1059  if (key._color_type == ColorAttrib::T_vertex) {
1060  text << "\t in float4 l_color : COLOR0,\n";
1061  } else if (key._color_type == ColorAttrib::T_flat) {
1062  text << "\t uniform float4 attr_color,\n";
1063  }
1064  }
1065 
1066  for (int i = 0; i < key._num_clip_planes; ++i) {
1067  text << "\t uniform float4 clipplane_" << i << ",\n";
1068  }
1069 
1070  text << "\t uniform float4 attr_ambient,\n";
1071  text << "\t uniform float4 attr_colorscale\n";
1072  text << ") {\n";
1073 
1074  // Clipping first!
1075  for (int i = 0; i < key._num_clip_planes; ++i) {
1076  text << "\t if (l_world_position.x * clipplane_" << i << ".x + l_world_position.y ";
1077  text << "* clipplane_" << i << ".y + l_world_position.z * clipplane_" << i << ".z + clipplane_" << i << ".w <= 0) {\n";
1078  text << "\t discard;\n";
1079  text << "\t }\n";
1080  }
1081  text << "\t float4 result;\n";
1082  if (key._outputs & (AuxBitplaneAttrib::ABO_aux_normal | AuxBitplaneAttrib::ABO_aux_glow)) {
1083  text << "\t o_aux = float4(0, 0, 0, 0);\n";
1084  }
1085  // Now generate any texture coordinates according to TexGenAttrib. If it
1086  // has a TexMatrixAttrib, also transform them.
1087  for (size_t i = 0; i < key._textures.size(); ++i) {
1088  const ShaderKey::TextureInfo &tex = key._textures[i];
1089  if (tex._mode == TextureStage::M_modulate && tex._flags == 0) {
1090  // Skip this stage.
1091  continue;
1092  }
1093  switch (tex._gen_mode) {
1094  case TexGenAttrib::M_off:
1095  // Cg seems to be able to optimize this temporary away when appropriate.
1096  text << "\t float4 texcoord" << i << " = l_" << tex._texcoord_name->join("_") << ";\n";
1097  break;
1098  case TexGenAttrib::M_world_position:
1099  text << "\t float4 texcoord" << i << " = l_world_position;\n";
1100  break;
1101  case TexGenAttrib::M_world_normal:
1102  text << "\t float4 texcoord" << i << " = l_world_normal;\n";
1103  break;
1104  case TexGenAttrib::M_eye_position:
1105  text << "\t float4 texcoord" << i << " = l_eye_position;\n";
1106  break;
1107  case TexGenAttrib::M_eye_normal:
1108  text << "\t float4 texcoord" << i << " = float4(l_eye_normal, 1.0f);\n";
1109  break;
1110  default:
1111  text << "\t float4 texcoord" << i << " = float4(0, 0, 0, 0);\n";
1112  pgraphnodes_cat.error()
1113  << "Unsupported TexGenAttrib mode: " << tex._gen_mode << "\n";
1114  }
1115  if (tex._flags & ShaderKey::TF_has_texscale) {
1116  text << "\t texcoord" << i << ".xyz *= texscale_" << i << ";\n";
1117  } else if (tex._flags & ShaderKey::TF_has_texmat) {
1118  text << "\t texcoord" << i << " = mul(texmat_" << i << ", texcoord" << i << ");\n";
1119  text << "\t texcoord" << i << ".xyz /= texcoord" << i << ".w;\n";
1120  }
1121  }
1122  text << "\t // Fetch all textures.\n";
1123  for (size_t i = 0; i < key._textures.size(); ++i) {
1124  const ShaderKey::TextureInfo &tex = key._textures[i];
1125  if ((tex._flags & ShaderKey::TF_map_height) == 0) {
1126  continue;
1127  }
1128 
1129  text << "\t float4 tex" << i << " = tex" << texture_type_as_string(tex._type);
1130  text << "(tex_" << i << ", texcoord" << i << ".";
1131  switch (tex._type) {
1132  case Texture::TT_cube_map:
1133  case Texture::TT_3d_texture:
1134  case Texture::TT_2d_texture_array:
1135  text << "xyz";
1136  break;
1137  case Texture::TT_2d_texture:
1138  text << "xy";
1139  break;
1140  case Texture::TT_1d_texture:
1141  text << "x";
1142  break;
1143  default:
1144  break;
1145  }
1146  text << ");\n\t float3 parallax_offset = l_eyevec.xyz * (tex" << i;
1147  if (tex._mode == TextureStage::M_normal_height ||
1148  (tex._flags & ShaderKey::TF_has_alpha) != 0) {
1149  text << ".aaa";
1150  } else {
1151  text << ".rgb";
1152  }
1153  text << " * 2.0 - 1.0) * " << parallax_mapping_scale << ";\n";
1154  // Additional samples
1155  for (int j = 0; j < parallax_mapping_samples - 1; ++j) {
1156  text << "\t parallax_offset = l_eyevec.xyz * (parallax_offset + (tex" << i;
1157  if (tex._mode == TextureStage::M_normal_height ||
1158  (tex._flags & ShaderKey::TF_has_alpha) != 0) {
1159  text << ".aaa";
1160  } else {
1161  text << ".rgb";
1162  }
1163  text << " * 2.0 - 1.0)) * " << 0.5 * parallax_mapping_scale << ";\n";
1164  }
1165  }
1166  for (size_t i = 0; i < key._textures.size(); ++i) {
1167  ShaderKey::TextureInfo &tex = key._textures[i];
1168  if (tex._mode == TextureStage::M_modulate && tex._flags == 0) {
1169  // Skip this stage.
1170  continue;
1171  }
1172  if ((tex._flags & ShaderKey::TF_map_height) == 0) {
1173  // Parallax mapping pushes the texture coordinates of the other textures
1174  // away from the camera.
1175  if (key._texture_flags & ShaderKey::TF_map_height) {
1176  text << "\t texcoord" << i << ".xyz -= parallax_offset;\n";
1177  }
1178  text << "\t float4 tex" << i << " = tex" << texture_type_as_string(tex._type);
1179  text << "(tex_" << i << ", texcoord" << i << ".";
1180  switch (tex._type) {
1181  case Texture::TT_cube_map:
1182  case Texture::TT_3d_texture:
1183  case Texture::TT_2d_texture_array:
1184  text << "xyz";
1185  break;
1186  case Texture::TT_2d_texture:
1187  text << "xy";
1188  break;
1189  case Texture::TT_1d_texture:
1190  text << "x";
1191  break;
1192  default:
1193  break;
1194  }
1195  text << ");\n";
1196  }
1197  }
1198  if (need_eye_normal) {
1199  text << "\t // Correct the surface normal for interpolation effects\n";
1200  text << "\t l_eye_normal = normalize(l_eye_normal);\n";
1201  }
1202  if (key._texture_flags & ShaderKey::TF_map_normal) {
1203  text << "\t // Translate tangent-space normal in map to view-space.\n";
1204 
1205  // Use Reoriented Normal Mapping to blend additional normal maps.
1206  bool is_first = true;
1207  for (size_t i = 0; i < key._textures.size(); ++i) {
1208  const ShaderKey::TextureInfo &tex = key._textures[i];
1209  if (tex._flags & ShaderKey::TF_map_normal) {
1210  if (is_first) {
1211  text << "\t float3 tsnormal = normalize((tex" << i << ".xyz * 2) - 1);\n";
1212  is_first = false;
1213  continue;
1214  }
1215  text << "\t tsnormal.z += 1;\n";
1216  text << "\t float3 tmp" << i << " = tex" << i << ".xyz * float3(-2, -2, 2) + float3(1, 1, -1);\n";
1217  text << "\t tsnormal = normalize(tsnormal * dot(tsnormal, tmp" << i << ") - tmp" << i << " * tsnormal.z);\n";
1218  }
1219  }
1220  text << "\t l_eye_normal *= tsnormal.z;\n";
1221  text << "\t l_eye_normal += normalize(l_tangent) * tsnormal.x;\n";
1222  text << "\t l_eye_normal += normalize(l_binormal) * tsnormal.y;\n";
1223  text << "\t l_eye_normal = normalize(l_eye_normal);\n";
1224  }
1225  if (key._outputs & AuxBitplaneAttrib::ABO_aux_normal) {
1226  text << "\t // Output the camera-space surface normal\n";
1227  text << "\t o_aux.rgb = (l_eye_normal*0.5) + float3(0.5,0.5,0.5);\n";
1228  }
1229  if (key._lighting) {
1230  text << "\t // Begin view-space light calculations\n";
1231  text << "\t float ldist,lattenv,langle,lshad;\n";
1232  text << "\t float4 lcolor,lspec,lpoint,latten,ldir,leye;\n";
1233  text << "\t float3 lvec,lhalf;\n";
1234  if (key._have_separate_ambient) {
1235  text << "\t float4 tot_ambient = float4(0,0,0,0);\n";
1236  }
1237  text << "\t float4 tot_diffuse = float4(0,0,0,0);\n";
1238  if (have_specular) {
1239  text << "\t float4 tot_specular = float4(0,0,0,0);\n";
1240  if (key._material_flags & Material::F_specular) {
1241  text << "\t float shininess = attr_material[3].w;\n";
1242  } else {
1243  text << "\t float shininess = 50; // no shininess specified, using default\n";
1244  }
1245  }
1246  if (key._have_separate_ambient) {
1247  text << "\t tot_ambient += attr_ambient;\n";
1248  } else {
1249  text << "\t tot_diffuse += attr_ambient;\n";
1250  }
1251  }
1252  for (size_t i = 0; i < key._lights.size(); ++i) {
1253  const ShaderKey::LightInfo &light = key._lights[i];
1254  if (light._type.is_derived_from(DirectionalLight::get_class_type())) {
1255  text << "\t // Directional Light " << i << "\n";
1256  text << "\t lcolor = attr_light" << i << "[0];\n";
1257  if (light._flags & ShaderKey::LF_has_specular_color) {
1258  text << "\t lspec = attr_lspec" << i << ";\n";
1259  } else {
1260  text << "\t lspec = lcolor;\n";
1261  }
1262  text << "\t lvec = attr_light" << i << "[3].xyz;\n";
1263  text << "\t lcolor *= saturate(dot(l_eye_normal, lvec.xyz));\n";
1264  if (light._flags & ShaderKey::LF_has_shadows) {
1265  if (_use_shadow_filter) {
1266  text << "\t lshad = shadow2DProj(shadow_" << i << ", l_lightcoord" << i << ").r;\n";
1267  } else {
1268  text << "\t lshad = tex2Dproj(shadow_" << i << ", l_lightcoord" << i << ").r > l_lightcoord" << i << ".z / l_lightcoord" << i << ".w;\n";
1269  }
1270  text << "\t lcolor *= lshad;\n";
1271  text << "\t lspec *= lshad;\n";
1272  }
1273  text << "\t tot_diffuse += lcolor;\n";
1274  if (have_specular) {
1275  if (key._material_flags & Material::F_local) {
1276  text << "\t lhalf = normalize(lvec - normalize(l_eye_position.xyz));\n";
1277  } else {
1278  text << "\t lhalf = normalize(lvec - float3(0, 1, 0));\n";
1279  }
1280  text << "\t lspec *= pow(saturate(dot(l_eye_normal, lhalf)), shininess);\n";
1281  text << "\t tot_specular += lspec;\n";
1282  }
1283  } else if (light._type.is_derived_from(PointLight::get_class_type())) {
1284  text << "\t // Point Light " << i << "\n";
1285  text << "\t lcolor = attr_light" << i << "[0];\n";
1286  if (light._flags & ShaderKey::LF_has_specular_color) {
1287  text << "\t lspec = attr_lspec" << i << ";\n";
1288  } else {
1289  text << "\t lspec = lcolor;\n";
1290  }
1291  text << "\t latten = attr_light" << i << "[1];\n";
1292  text << "\t lpoint = attr_light" << i << "[3];\n";
1293  text << "\t lvec = lpoint.xyz - l_eye_position.xyz;\n";
1294  text << "\t ldist = length(lvec);\n";
1295  text << "\t lvec /= ldist;\n";
1296  if (light._type.is_derived_from(SphereLight::get_class_type())) {
1297  text << "\t ldist = max(ldist, attr_light" << i << "[2].w);\n";
1298  }
1299  text << "\t lattenv = 1/(latten.x + latten.y*ldist + latten.z*ldist*ldist);\n";
1300  text << "\t lcolor *= lattenv * saturate(dot(l_eye_normal, lvec));\n";
1301  if (light._flags & ShaderKey::LF_has_shadows) {
1302  text << "\t ldist = max(abs(l_lightcoord" << i << ".x), max(abs(l_lightcoord" << i << ".y), abs(l_lightcoord" << i << ".z)));\n";
1303  text << "\t ldist = ((latten.w+lpoint.w)/(latten.w-lpoint.w))+((-2*latten.w*lpoint.w)/(ldist * (latten.w-lpoint.w)));\n";
1304  text << "\t lshad = texCUBE(shadow_" << i << ", l_lightcoord" << i << ".xyz).r >= ldist * 0.5 + 0.5;\n";
1305  text << "\t lcolor *= lshad;\n";
1306  text << "\t lspec *= lshad;\n";
1307  }
1308  text << "\t tot_diffuse += lcolor;\n";
1309  if (have_specular) {
1310  if (key._material_flags & Material::F_local) {
1311  text << "\t lhalf = normalize(lvec - normalize(l_eye_position.xyz));\n";
1312  } else {
1313  text << "\t lhalf = normalize(lvec - float3(0, 1, 0));\n";
1314  }
1315  text << "\t lspec *= lattenv;\n";
1316  text << "\t lspec *= pow(saturate(dot(l_eye_normal, lhalf)), shininess);\n";
1317  text << "\t tot_specular += lspec;\n";
1318  }
1319  } else if (light._type.is_derived_from(Spotlight::get_class_type())) {
1320  text << "\t // Spot Light " << i << "\n";
1321  text << "\t lcolor = attr_light" << i << "[0];\n";
1322  if (light._flags & ShaderKey::LF_has_specular_color) {
1323  text << "\t lspec = attr_lspec" << i << ";\n";
1324  } else {
1325  text << "\t lspec = lcolor;\n";
1326  }
1327  text << "\t latten = attr_light" << i << "[1];\n";
1328  text << "\t ldir = attr_light" << i << "[2];\n";
1329  text << "\t lpoint = attr_light" << i << "[3];\n";
1330  text << "\t lvec = lpoint.xyz - l_eye_position.xyz;\n";
1331  text << "\t ldist = length(lvec);\n";
1332  text << "\t lvec /= ldist;\n";
1333  text << "\t langle = saturate(dot(ldir.xyz, lvec));\n";
1334  text << "\t lattenv = 1/(latten.x + latten.y*ldist + latten.z*ldist*ldist);\n";
1335  text << "\t lattenv *= pow(langle, latten.w);\n";
1336  text << "\t if (langle < ldir.w) lattenv = 0;\n";
1337  text << "\t lcolor *= lattenv * saturate(dot(l_eye_normal, lvec));\n";
1338  if (light._flags & ShaderKey::LF_has_shadows) {
1339  if (_use_shadow_filter) {
1340  text << "\t lshad = shadow2DProj(shadow_" << i << ", l_lightcoord" << i << ").r;\n";
1341  } else {
1342  text << "\t lshad = tex2Dproj(shadow_" << i << ", l_lightcoord" << i << ").r > l_lightcoord" << i << ".z / l_lightcoord" << i << ".w;\n";
1343  }
1344  text << "\t lcolor *= lshad;\n";
1345  text << "\t lspec *= lshad;\n";
1346  }
1347 
1348  text << "\t tot_diffuse += lcolor;\n";
1349  if (have_specular) {
1350  if (key._material_flags & Material::F_local) {
1351  text << "\t lhalf = normalize(lvec - normalize(l_eye_position.xyz));\n";
1352  } else {
1353  text << "\t lhalf = normalize(lvec - float3(0,1,0));\n";
1354  }
1355  text << "\t lspec *= lattenv;\n";
1356  text << "\t lspec *= pow(saturate(dot(l_eye_normal, lhalf)), shininess);\n";
1357  text << "\t tot_specular += lspec;\n";
1358  }
1359  }
1360  }
1361  if (key._lighting) {
1362  if (key._light_ramp != nullptr) {
1363  switch (key._light_ramp->get_mode()) {
1364  case LightRampAttrib::LRT_single_threshold:
1365  {
1366  PN_stdfloat t = key._light_ramp->get_threshold(0);
1367  PN_stdfloat l0 = key._light_ramp->get_level(0);
1368  text << "\t // Single-threshold light ramp\n";
1369  text << "\t float lr_in = dot(tot_diffuse.rgb, float3(0.33,0.34,0.33));\n";
1370  text << "\t float lr_scale = (lr_in < " << t << ") ? 0.0 : (" << l0 << "/lr_in);\n";
1371  text << "\t tot_diffuse = tot_diffuse * lr_scale;\n";
1372  break;
1373  }
1374  case LightRampAttrib::LRT_double_threshold:
1375  {
1376  PN_stdfloat t0 = key._light_ramp->get_threshold(0);
1377  PN_stdfloat t1 = key._light_ramp->get_threshold(1);
1378  PN_stdfloat l0 = key._light_ramp->get_level(0);
1379  PN_stdfloat l1 = key._light_ramp->get_level(1);
1380  text << "\t // Double-threshold light ramp\n";
1381  text << "\t float lr_in = dot(tot_diffuse.rgb, float3(0.33,0.34,0.33));\n";
1382  text << "\t float lr_out = 0.0;\n";
1383  text << "\t if (lr_in > " << t0 << ") lr_out=" << l0 << ";\n";
1384  text << "\t if (lr_in > " << t1 << ") lr_out=" << l1 << ";\n";
1385  text << "\t tot_diffuse = tot_diffuse * (lr_out / lr_in);\n";
1386  break;
1387  }
1388  default:
1389  break;
1390  }
1391  }
1392  text << "\t // Begin view-space light summation\n";
1393  if (key._material_flags & Material::F_emission) {
1394  if (key._texture_flags & ShaderKey::TF_map_glow) {
1395  text << "\t result = attr_material[2] * saturate(2 * (tex" << map_index_glow << ".a - 0.5));\n";
1396  } else {
1397  text << "\t result = attr_material[2];\n";
1398  }
1399  } else {
1400  if (key._texture_flags & ShaderKey::TF_map_glow) {
1401  text << "\t result = saturate(2 * (tex" << map_index_glow << ".a - 0.5));\n";
1402  } else {
1403  text << "\t result = float4(0,0,0,0);\n";
1404  }
1405  }
1406  if (key._have_separate_ambient) {
1407  if (key._material_flags & Material::F_ambient) {
1408  text << "\t result += tot_ambient * attr_material[0];\n";
1409  } else if (key._color_type == ColorAttrib::T_vertex) {
1410  text << "\t result += tot_ambient * l_color;\n";
1411  } else if (key._color_type == ColorAttrib::T_flat) {
1412  text << "\t result += tot_ambient * attr_color;\n";
1413  } else {
1414  text << "\t result += tot_ambient;\n";
1415  }
1416  }
1417  if (key._material_flags & Material::F_diffuse) {
1418  text << "\t result += tot_diffuse * attr_material[1];\n";
1419  } else if (key._color_type == ColorAttrib::T_vertex) {
1420  text << "\t result += tot_diffuse * l_color;\n";
1421  } else if (key._color_type == ColorAttrib::T_flat) {
1422  text << "\t result += tot_diffuse * attr_color;\n";
1423  } else {
1424  text << "\t result += tot_diffuse;\n";
1425  }
1426  if (key._light_ramp == nullptr ||
1427  key._light_ramp->get_mode() == LightRampAttrib::LRT_default) {
1428  text << "\t result = saturate(result);\n";
1429  }
1430  text << "\t // End view-space light calculations\n";
1431 
1432  // Combine in alpha, which bypasses lighting calculations. Use of lerp
1433  // here is a workaround for a radeon driver bug.
1434  if (key._calc_primary_alpha) {
1435  if (key._color_type == ColorAttrib::T_vertex) {
1436  text << "\t result.a = l_color.a;\n";
1437  } else if (key._color_type == ColorAttrib::T_flat) {
1438  text << "\t result.a = attr_color.a;\n";
1439  } else {
1440  text << "\t result.a = 1;\n";
1441  }
1442  }
1443  } else {
1444  if (key._color_type == ColorAttrib::T_vertex) {
1445  text << "\t result = l_color;\n";
1446  } else if (key._color_type == ColorAttrib::T_flat) {
1447  text << "\t result = attr_color;\n";
1448  } else {
1449  text << "\t result = float4(1, 1, 1, 1);\n";
1450  }
1451  }
1452 
1453  // Apply the color scale.
1454  text << "\t result *= attr_colorscale;\n";
1455 
1456  // Store these if any stages will use it.
1457  if (key._texture_flags & ShaderKey::TF_uses_primary_color) {
1458  text << "\t float4 primary_color = result;\n";
1459  }
1460  if (key._texture_flags & ShaderKey::TF_uses_last_saved_result) {
1461  text << "\t float4 last_saved_result = result;\n";
1462  }
1463 
1464  // Now loop through the textures to compose our magic blending formulas.
1465  for (size_t i = 0; i < key._textures.size(); ++i) {
1466  const ShaderKey::TextureInfo &tex = key._textures[i];
1467  TextureStage::CombineMode combine_rgb, combine_alpha;
1468 
1469  switch (tex._mode) {
1470  case TextureStage::M_modulate:
1471  if ((tex._flags & ShaderKey::TF_has_rgb) != 0 &&
1472  (tex._flags & ShaderKey::TF_has_alpha) != 0) {
1473  text << "\t result.rgba *= tex" << i << ".rgba;\n";
1474  } else if (tex._flags & ShaderKey::TF_has_alpha) {
1475  text << "\t result.a *= tex" << i << ".a;\n";
1476  } else if (tex._flags & ShaderKey::TF_has_rgb) {
1477  text << "\t result.rgb *= tex" << i << ".rgb;\n";
1478  }
1479  break;
1480  case TextureStage::M_modulate_glow:
1481  case TextureStage::M_modulate_gloss:
1482  // in the case of glow or spec we currently see the specularity evenly
1483  // across the surface even if transparency or masking is present not
1484  // sure if this is desired behavior or not. *MOST* would construct a
1485  // spec map based off of what isisn't seen based on the masktransparency
1486  // this may have to be left alone for now agartner
1487  text << "\t result.rgb *= tex" << i << ";\n";
1488  break;
1489  case TextureStage::M_decal:
1490  text << "\t result.rgb = lerp(result, tex" << i << ", tex" << i << ".a).rgb;\n";
1491  break;
1492  case TextureStage::M_blend:
1493  text << "\t result.rgb = lerp(result.rgb, texcolor_" << i << ".rgb, tex" << i << ".rgb);\n";
1494  if (key._calc_primary_alpha) {
1495  text << "\t result.a *= tex" << i << ".a;\n";
1496  }
1497  break;
1498  case TextureStage::M_replace:
1499  text << "\t result = tex" << i << ";\n";
1500  break;
1501  case TextureStage::M_add:
1502  text << "\t result.rgb += tex" << i << ".rgb;\n";
1503  if (key._calc_primary_alpha) {
1504  text << "\t result.a *= tex" << i << ".a;\n";
1505  }
1506  break;
1507  case TextureStage::M_combine:
1508  combine_rgb = (TextureStage::CombineMode)((tex._flags & ShaderKey::TF_COMBINE_RGB_MODE_MASK) >> ShaderKey::TF_COMBINE_RGB_MODE_SHIFT);
1509  combine_alpha = (TextureStage::CombineMode)((tex._flags & ShaderKey::TF_COMBINE_ALPHA_MODE_MASK) >> ShaderKey::TF_COMBINE_ALPHA_MODE_SHIFT);
1510  if (combine_rgb == TextureStage::CM_dot3_rgba) {
1511  text << "\t result = ";
1512  text << combine_mode_as_string(tex, combine_rgb, false, i);
1513  text << ";\n";
1514  } else {
1515  text << "\t result.rgb = ";
1516  text << combine_mode_as_string(tex, combine_rgb, false, i);
1517  text << ";\n\t result.a = ";
1518  text << combine_mode_as_string(tex, combine_alpha, true, i);
1519  text << ";\n";
1520  }
1521  if (tex._flags & ShaderKey::TF_rgb_scale_2) {
1522  text << "\t result.rgb *= 2;\n";
1523  }
1524  if (tex._flags & ShaderKey::TF_rgb_scale_4) {
1525  text << "\t result.rgb *= 4;\n";
1526  }
1527  if (tex._flags & ShaderKey::TF_alpha_scale_2) {
1528  text << "\t result.a *= 2;\n";
1529  }
1530  if (tex._flags & ShaderKey::TF_alpha_scale_4) {
1531  text << "\t result.a *= 4;\n";
1532  }
1533  break;
1534  case TextureStage::M_blend_color_scale:
1535  text << "\t result.rgb = lerp(result.rgb, texcolor_" << i << ".rgb * attr_colorscale.rgb, tex" << i << ".rgb);\n";
1536  if (key._calc_primary_alpha) {
1537  text << "\t result.a *= texcolor_" << i << ".a * attr_colorscale.a;\n";
1538  }
1539  break;
1540  default:
1541  break;
1542  }
1543  if (tex._flags & ShaderKey::TF_saved_result) {
1544  text << "\t last_saved_result = result;\n";
1545  }
1546  }
1547 
1548  if (key._alpha_test_mode != RenderAttrib::M_none) {
1549  text << "\t // Shader includes alpha test:\n";
1550  double ref = key._alpha_test_ref;
1551  switch (key._alpha_test_mode) {
1552  case RenderAttrib::M_never:
1553  text << "\t discard;\n";
1554  break;
1555  case RenderAttrib::M_less:
1556  text << "\t if (result.a >= " << ref << ") discard;\n";
1557  break;
1558  case RenderAttrib::M_equal:
1559  text << "\t if (result.a != " << ref << ") discard;\n";
1560  break;
1561  case RenderAttrib::M_less_equal:
1562  text << "\t if (result.a > " << ref << ") discard;\n";
1563  break;
1564  case RenderAttrib::M_greater:
1565  text << "\t if (result.a <= " << ref << ") discard;\n";
1566  break;
1567  case RenderAttrib::M_not_equal:
1568  text << "\t if (result.a == " << ref << ") discard;\n";
1569  break;
1570  case RenderAttrib::M_greater_equal:
1571  text << "\t if (result.a < " << ref << ") discard;\n";
1572  break;
1573  case RenderAttrib::M_none:
1574  case RenderAttrib::M_always:
1575  break;
1576  }
1577  }
1578 
1579  if (key._outputs & AuxBitplaneAttrib::ABO_glow) {
1580  if (key._texture_flags & ShaderKey::TF_map_glow) {
1581  text << "\t result.a = tex" << map_index_glow << ".a;\n";
1582  } else {
1583  text << "\t result.a = 0.5;\n";
1584  }
1585  }
1586  if (key._outputs & AuxBitplaneAttrib::ABO_aux_glow) {
1587  if (key._texture_flags & ShaderKey::TF_map_glow) {
1588  text << "\t o_aux.a = tex" << map_index_glow << ".a;\n";
1589  } else {
1590  text << "\t o_aux.a = 0.5;\n";
1591  }
1592  }
1593 
1594  if (have_specular) {
1595  if (key._material_flags & Material::F_specular) {
1596  text << "\t tot_specular *= attr_material[3];\n";
1597  }
1598  if (key._texture_flags & ShaderKey::TF_map_gloss) {
1599  text << "\t tot_specular *= tex" << map_index_gloss << ".a;\n";
1600  }
1601  text << "\t result.rgb = result.rgb + tot_specular.rgb;\n";
1602  }
1603  if (key._light_ramp != nullptr) {
1604  switch (key._light_ramp->get_mode()) {
1605  case LightRampAttrib::LRT_hdr0:
1606  text << "\t result.rgb = (result*result*result + result*result + result) / (result*result*result + result*result + result + 1);\n";
1607  break;
1608  case LightRampAttrib::LRT_hdr1:
1609  text << "\t result.rgb = (result*result + result) / (result*result + result + 1);\n";
1610  break;
1611  case LightRampAttrib::LRT_hdr2:
1612  text << "\t result.rgb = result / (result + 1);\n";
1613  break;
1614  default: break;
1615  }
1616  }
1617 
1618  // Apply fog.
1619  if (key._fog_mode != 0) {
1620  Fog::Mode fog_mode = (Fog::Mode)(key._fog_mode - 1);
1621  switch (fog_mode) {
1622  case Fog::M_linear:
1623  text << "\t result.rgb = lerp(attr_fogcolor.rgb, result.rgb, saturate((attr_fog.z - l_hpos.z) * attr_fog.w));\n";
1624  break;
1625  case Fog::M_exponential: // 1.442695f = 1 / log(2)
1626  text << "\t result.rgb = lerp(attr_fogcolor.rgb, result.rgb, saturate(exp2(attr_fog.x * l_hpos.z * -1.442695f)));\n";
1627  break;
1628  case Fog::M_exponential_squared:
1629  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";
1630  break;
1631  }
1632  }
1633 
1634  // The multiply is a workaround for a radeon driver bug. It's annoying as
1635  // heck, since it produces an extra instruction.
1636  text << "\t o_color = result * 1.000001;\n";
1637  if (key._alpha_test_mode != RenderAttrib::M_none) {
1638  text << "\t // Shader subsumes normal alpha test.\n";
1639  }
1640  if (key._disable_alpha_write) {
1641  text << "\t // Shader disables alpha write.\n";
1642  }
1643  text << "}\n";
1644 
1645  // Insert the shader into the shader attrib.
1646  PT(Shader) shader = Shader::make(text.str(), Shader::SL_Cg);
1647  nassertr(shader != nullptr, nullptr);
1648 
1649  CPT(RenderAttrib) shattr = ShaderAttrib::make(shader);
1650  if (key._alpha_test_mode != RenderAttrib::M_none) {
1651  shattr = DCAST(ShaderAttrib, shattr)->set_flag(ShaderAttrib::F_subsume_alpha_test, true);
1652  }
1653  if (key._disable_alpha_write) {
1654  shattr = DCAST(ShaderAttrib, shattr)->set_flag(ShaderAttrib::F_disable_alpha_write, true);
1655  }
1656 
1657  reset_register_allocator();
1658 
1659  CPT(ShaderAttrib) attr = DCAST(ShaderAttrib, shattr);
1660  _generated_shaders[key] = attr;
1661  return attr;
1662 }
1663 
1664 /**
1665  * This 'synthesizes' a combine mode into a string.
1666  */
1667 string ShaderGenerator::
1668 combine_mode_as_string(const ShaderKey::TextureInfo &info, TextureStage::CombineMode c_mode, bool alpha, short texindex) {
1669  std::ostringstream text;
1670  switch (c_mode) {
1671  case TextureStage::CM_modulate:
1672  text << combine_source_as_string(info, 0, alpha, texindex);
1673  text << " * ";
1674  text << combine_source_as_string(info, 1, alpha, texindex);
1675  break;
1676  case TextureStage::CM_add:
1677  text << combine_source_as_string(info, 0, alpha, texindex);
1678  text << " + ";
1679  text << combine_source_as_string(info, 1, alpha, texindex);
1680  break;
1681  case TextureStage::CM_add_signed:
1682  text << combine_source_as_string(info, 0, alpha, texindex);
1683  text << " + ";
1684  text << combine_source_as_string(info, 1, alpha, texindex);
1685  if (alpha) {
1686  text << " - 0.5";
1687  } else {
1688  text << " - float3(0.5, 0.5, 0.5)";
1689  }
1690  break;
1691  case TextureStage::CM_interpolate:
1692  text << "lerp(";
1693  text << combine_source_as_string(info, 1, alpha, texindex);
1694  text << ", ";
1695  text << combine_source_as_string(info, 0, alpha, texindex);
1696  text << ", ";
1697  text << combine_source_as_string(info, 2, alpha, texindex);
1698  text << ")";
1699  break;
1700  case TextureStage::CM_subtract:
1701  text << combine_source_as_string(info, 0, alpha, texindex);
1702  text << " - ";
1703  text << combine_source_as_string(info, 1, alpha, texindex);
1704  break;
1705  case TextureStage::CM_dot3_rgb:
1706  case TextureStage::CM_dot3_rgba:
1707  text << "4 * dot(";
1708  text << combine_source_as_string(info, 0, alpha, texindex);
1709  text << " - float3(0.5), ";
1710  text << combine_source_as_string(info, 1, alpha, texindex);
1711  text << " - float3(0.5))";
1712  break;
1713  case TextureStage::CM_replace:
1714  default: // Not sure if this is correct as default value.
1715  text << combine_source_as_string(info, 0, alpha, texindex);
1716  break;
1717  }
1718  return text.str();
1719 }
1720 
1721 /**
1722  * This 'synthesizes' a combine source into a string.
1723  */
1724 string ShaderGenerator::
1725 combine_source_as_string(const ShaderKey::TextureInfo &info, short num, bool alpha, short texindex) {
1726  TextureStage::CombineSource c_src;
1727  TextureStage::CombineOperand c_op;
1728  if (!alpha) {
1729  c_src = UNPACK_COMBINE_SRC(info._combine_rgb, num);
1730  c_op = UNPACK_COMBINE_OP(info._combine_rgb, num);
1731  } else {
1732  c_src = UNPACK_COMBINE_SRC(info._combine_alpha, num);
1733  c_op = UNPACK_COMBINE_OP(info._combine_alpha, num);
1734  }
1735  std::ostringstream csource;
1736  if (c_op == TextureStage::CO_one_minus_src_color ||
1737  c_op == TextureStage::CO_one_minus_src_alpha) {
1738  csource << "saturate(1.0f - ";
1739  }
1740  switch (c_src) {
1741  case TextureStage::CS_undefined:
1742  case TextureStage::CS_texture:
1743  csource << "tex" << texindex;
1744  break;
1745  case TextureStage::CS_constant:
1746  csource << "texcolor_" << texindex;
1747  break;
1748  case TextureStage::CS_primary_color:
1749  csource << "primary_color";
1750  break;
1751  case TextureStage::CS_previous:
1752  csource << "result";
1753  break;
1754  case TextureStage::CS_constant_color_scale:
1755  csource << "attr_colorscale";
1756  break;
1757  case TextureStage::CS_last_saved_result:
1758  csource << "last_saved_result";
1759  break;
1760  }
1761  if (c_op == TextureStage::CO_one_minus_src_color ||
1762  c_op == TextureStage::CO_one_minus_src_alpha) {
1763  csource << ")";
1764  }
1765  if (c_op == TextureStage::CO_src_color || c_op == TextureStage::CO_one_minus_src_color) {
1766  csource << ".rgb";
1767  } else {
1768  csource << ".a";
1769  if (!alpha) {
1770  // Dunno if it's legal in the FPP at all, but let's just allow it.
1771  return "float3(" + csource.str() + ")";
1772  }
1773  }
1774  return csource.str();
1775 }
1776 
1777 /**
1778  * Returns 1D, 2D, 3D or CUBE, depending on the given texture type.
1779  */
1780 const char *ShaderGenerator::
1781 texture_type_as_string(Texture::TextureType ttype) {
1782  switch (ttype) {
1783  case Texture::TT_1d_texture:
1784  return "1D";
1785  break;
1786  case Texture::TT_2d_texture:
1787  return "2D";
1788  break;
1789  case Texture::TT_3d_texture:
1790  return "3D";
1791  break;
1792  case Texture::TT_cube_map:
1793  return "CUBE";
1794  break;
1795  case Texture::TT_2d_texture_array:
1796  return "2DARRAY";
1797  break;
1798  default:
1799  pgraphnodes_cat.error() << "Unsupported texture type!\n";
1800  return "2D";
1801  }
1802 }
1803 
1804 /**
1805  * Initializes the ShaderKey to the empty state.
1806  */
1807 ShaderGenerator::ShaderKey::
1808 ShaderKey() :
1809  _color_type(ColorAttrib::T_vertex),
1810  _material_flags(0),
1811  _texture_flags(0),
1812  _lighting(false),
1813  _have_separate_ambient(false),
1814  _fog_mode(0),
1815  _outputs(0),
1816  _calc_primary_alpha(false),
1817  _disable_alpha_write(false),
1818  _alpha_test_mode(RenderAttrib::M_none),
1819  _alpha_test_ref(0.0),
1820  _num_clip_planes(0),
1821  _light_ramp(nullptr) {
1822 }
1823 
1824 /**
1825  * Returns true if this ShaderKey sorts less than the other one. This is an
1826  * arbitrary, but consistent ordering.
1827  */
1828 bool ShaderGenerator::ShaderKey::
1829 operator < (const ShaderKey &other) const {
1830  if (_anim_spec != other._anim_spec) {
1831  return _anim_spec < other._anim_spec;
1832  }
1833  if (_color_type != other._color_type) {
1834  return _color_type < other._color_type;
1835  }
1836  if (_material_flags != other._material_flags) {
1837  return _material_flags < other._material_flags;
1838  }
1839  if (_texture_flags != other._texture_flags) {
1840  return _texture_flags < other._texture_flags;
1841  }
1842  if (_textures.size() != other._textures.size()) {
1843  return _textures.size() < other._textures.size();
1844  }
1845  for (size_t i = 0; i < _textures.size(); ++i) {
1846  const ShaderKey::TextureInfo &tex = _textures[i];
1847  const ShaderKey::TextureInfo &other_tex = other._textures[i];
1848  if (tex._texcoord_name != other_tex._texcoord_name) {
1849  return tex._texcoord_name < other_tex._texcoord_name;
1850  }
1851  if (tex._type != other_tex._type) {
1852  return tex._type < other_tex._type;
1853  }
1854  if (tex._mode != other_tex._mode) {
1855  return tex._mode < other_tex._mode;
1856  }
1857  if (tex._gen_mode != other_tex._gen_mode) {
1858  return tex._gen_mode < other_tex._gen_mode;
1859  }
1860  if (tex._flags != other_tex._flags) {
1861  return tex._flags < other_tex._flags;
1862  }
1863  if (tex._combine_rgb != other_tex._combine_rgb) {
1864  return tex._combine_rgb < other_tex._combine_rgb;
1865  }
1866  if (tex._combine_alpha != other_tex._combine_alpha) {
1867  return tex._combine_alpha < other_tex._combine_alpha;
1868  }
1869  }
1870  if (_lights.size() != other._lights.size()) {
1871  return _lights.size() < other._lights.size();
1872  }
1873  for (size_t i = 0; i < _lights.size(); ++i) {
1874  const ShaderKey::LightInfo &light = _lights[i];
1875  const ShaderKey::LightInfo &other_light = other._lights[i];
1876  if (light._type != other_light._type) {
1877  return light._type < other_light._type;
1878  }
1879  if (light._flags != other_light._flags) {
1880  return light._flags < other_light._flags;
1881  }
1882  }
1883  if (_lighting != other._lighting) {
1884  return _lighting < other._lighting;
1885  }
1886  if (_have_separate_ambient != other._have_separate_ambient) {
1887  return _have_separate_ambient < other._have_separate_ambient;
1888  }
1889  if (_fog_mode != other._fog_mode) {
1890  return _fog_mode < other._fog_mode;
1891  }
1892  if (_outputs != other._outputs) {
1893  return _outputs < other._outputs;
1894  }
1895  if (_calc_primary_alpha != other._calc_primary_alpha) {
1896  return _calc_primary_alpha < other._calc_primary_alpha;
1897  }
1898  if (_disable_alpha_write != other._disable_alpha_write) {
1899  return _disable_alpha_write < other._disable_alpha_write;
1900  }
1901  if (_alpha_test_mode != other._alpha_test_mode) {
1902  return _alpha_test_mode < other._alpha_test_mode;
1903  }
1904  if (_alpha_test_ref != other._alpha_test_ref) {
1905  return _alpha_test_ref < other._alpha_test_ref;
1906  }
1907  if (_num_clip_planes != other._num_clip_planes) {
1908  return _num_clip_planes < other._num_clip_planes;
1909  }
1910  return _light_ramp < other._light_ramp;
1911 }
1912 
1913 /**
1914  * Returns true if this ShaderKey is equal to the other one.
1915  */
1916 bool ShaderGenerator::ShaderKey::
1917 operator == (const ShaderKey &other) const {
1918  if (_anim_spec != other._anim_spec) {
1919  return false;
1920  }
1921  if (_color_type != other._color_type) {
1922  return false;
1923  }
1924  if (_material_flags != other._material_flags) {
1925  return false;
1926  }
1927  if (_texture_flags != other._texture_flags) {
1928  return false;
1929  }
1930  if (_textures.size() != other._textures.size()) {
1931  return false;
1932  }
1933  for (size_t i = 0; i < _textures.size(); ++i) {
1934  const ShaderKey::TextureInfo &tex = _textures[i];
1935  const ShaderKey::TextureInfo &other_tex = other._textures[i];
1936  if (tex._texcoord_name != other_tex._texcoord_name ||
1937  tex._type != other_tex._type ||
1938  tex._mode != other_tex._mode ||
1939  tex._gen_mode != other_tex._gen_mode ||
1940  tex._flags != other_tex._flags ||
1941  tex._combine_rgb != other_tex._combine_rgb ||
1942  tex._combine_alpha != other_tex._combine_alpha) {
1943  return false;
1944  }
1945  }
1946  if (_lights.size() != other._lights.size()) {
1947  return false;
1948  }
1949  for (size_t i = 0; i < _lights.size(); ++i) {
1950  const ShaderKey::LightInfo &light = _lights[i];
1951  const ShaderKey::LightInfo &other_light = other._lights[i];
1952  if (light._type != other_light._type ||
1953  light._flags != other_light._flags) {
1954  return false;
1955  }
1956  }
1957  return _lighting == other._lighting
1958  && _have_separate_ambient == other._have_separate_ambient
1959  && _fog_mode == other._fog_mode
1960  && _outputs == other._outputs
1961  && _calc_primary_alpha == other._calc_primary_alpha
1962  && _disable_alpha_write == other._disable_alpha_write
1963  && _alpha_test_mode == other._alpha_test_mode
1964  && _alpha_test_ref == other._alpha_test_ref
1965  && _num_clip_planes == other._num_clip_planes
1966  && _light_ramp == other._light_ramp;
1967 }
1968 
1969 #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 'off' 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 'aux' 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's before they are rendered.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_material
If the MaterialAttrib is not an 'off' 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(),...
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 'off' 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
get_color_type
Returns the type of color specified by this ColorAttrib.
Definition: colorAttrib.h:46
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