Browse Source

shader: Support light struct parameters for shader input lights

rdb 5 years ago
parent
commit
ca6112bc19
2 changed files with 111 additions and 1 deletions
  1. 67 1
      panda/src/gobj/shader.cxx
  2. 44 0
      tests/display/test_glsl_shader.py

+ 67 - 1
panda/src/gobj/shader.cxx

@@ -2941,8 +2941,74 @@ bind_parameter(CPT_InternalName name, const ::ShaderType *type, int location) {
     for (size_t i = 0; i < struct_type->get_num_members(); ++i) {
       const ::ShaderType::Struct::Member &member = struct_type->get_member(i);
 
-      // Recurse.
       PT(InternalName) fqname = ((InternalName *)name.p())->append(member.name);
+
+      // Numeric struct members under GLSL may need a special treatment.
+      ScalarType scalar_type;
+      uint32_t dim[3];
+      if (_language == SL_GLSL &&
+          member.type->as_scalar_type(scalar_type, dim[0], dim[1], dim[2]) &&
+          scalar_type == ScalarType::ST_float &&
+          dim[0] == 1) {
+        // It might be something like an attribute of a shader input, like a
+        // light parameter.  It might also just be a custom struct parameter.
+        // We can't know yet, so we always have to handle it specially.
+        ShaderMatSpec bind;
+        bind._id = arg_id;
+        bind._id._seqno += i;
+        if (member.name == "shadowMatrix" && dim[1] == 4 && dim[2] == 4) {
+          // Special exception for shadowMatrix, which is deprecated because it
+          // includes the model transformation.  It is far more efficient to do
+          // that in the shader instead.
+          static bool warned = false;
+          if (!warned) {
+            warned = true;
+            shader_cat.warning()
+              << "light.shadowMatrix inputs are deprecated; use "
+                 "shadowViewMatrix instead, which transforms from view "
+                 "space instead of model space.\n";
+          }
+          bind._piece = SMP_whole;
+          bind._func = SMF_compose;
+          bind._part[0] = SMO_model_to_apiview;
+          bind._arg[0] = nullptr;
+          bind._part[1] = SMO_mat_constant_x_attrib;
+          bind._arg[1] = ((InternalName *)name.p())->append("shadowViewMatrix");
+        }
+        else {
+          bind._func = SMF_first;
+          if (dim[1] == 4) {
+            bind._piece = SMP_whole;
+            bind._part[0] = SMO_mat_constant_x_attrib;
+          }
+          else if (dim[1] == 3) {
+            bind._piece = SMP_upper3x3;
+            bind._part[0] = SMO_mat_constant_x_attrib;
+          }
+          else {
+            bind._part[0] = SMO_vec_constant_x_attrib;
+            if (dim[2] == 1) {
+              bind._piece = SMP_row3x1;
+            }
+            else if (dim[2] == 2) {
+              bind._piece = SMP_row3x2;
+            }
+            else if (dim[2] == 3) {
+              bind._piece = SMP_row3x3;
+            }
+            else {
+              bind._piece = SMP_row3;
+            }
+          }
+          bind._arg[0] = fqname;
+          bind._part[1] = SMO_identity;
+          bind._arg[1] = nullptr;
+        }
+        cp_add_mat_spec(bind);
+        continue;
+      }
+
+      // Otherwise, recurse.
       if (!bind_parameter(fqname, member.type, location + i)) {
         success = false;
       }

+ 44 - 0
tests/display/test_glsl_shader.py

@@ -461,6 +461,50 @@ def test_glsl_struct_array(gsg):
     })
 
 
+def test_glsl_light(gsg):
+    preamble = """
+    uniform struct p3d_LightSourceParameters {
+        vec4 color;
+        vec3 ambient;
+        vec4 diffuse;
+        vec4 specular;
+        vec3 position;
+        vec4 halfVector;
+        vec4 spotDirection;
+        float spotCutoff;
+        float spotCosCutoff;
+        float spotExponent;
+        vec3 attenuation;
+        float constantAttenuation;
+        float linearAttenuation;
+        float quadraticAttenuation;
+    } plight;
+    """
+    code = """
+    assert(plight.color == vec4(1, 2, 3, 4));
+    assert(plight.ambient == vec3(0, 0, 0));
+    assert(plight.diffuse == vec4(1, 2, 3, 4));
+    assert(plight.specular == vec4(5, 6, 7, 8));
+    assert(plight.position == vec3(9, 10, 11));
+    assert(plight.spotCutoff == 180);
+    assert(plight.spotCosCutoff == -1);
+    assert(plight.spotExponent == 0);
+    assert(plight.attenuation == vec3(12, 13, 14));
+    assert(plight.constantAttenuation == 12);
+    assert(plight.linearAttenuation == 13);
+    assert(plight.quadraticAttenuation == 14);
+    """
+    plight = core.PointLight("plight")
+    plight.color = (1, 2, 3, 4)
+    plight.specular_color = (5, 6, 7, 8)
+    plight.transform = core.TransformState.make_pos((9, 10, 11))
+    plight.attenuation = (12, 13, 14)
+
+    run_glsl_test(gsg, code, preamble, {
+        'plight': core.NodePath(plight),
+    })
+
+
 def test_glsl_write_extract_image_buffer(gsg):
     # Tests that we can write to a buffer texture on the GPU, and then extract
     # the data on the CPU.  We test two textures since there was in the past a