2
0
Эх сурвалжийг харах

NodePath::set_normal_map()

David Rose 20 жил өмнө
parent
commit
e5095d2c31

+ 59 - 42
panda/src/pgraph/cullableObject.cxx

@@ -501,50 +501,67 @@ munge_texcoord_light_vector(const CullTraverser *traverser) {
        ++lvi) {
     TextureStage *stage = (*lvi);
     NodePath light = tex_gen->get_light(stage);
-    nassertv(!light.is_empty());
-    string source_name = tex_gen->get_source_name(stage);
-    Light *light_obj = light.node()->as_light();
-    nassertv(light_obj != (Light *)NULL);
-
-    // Determine the names of the tangent and binormal columns
-    // associated with the stage's texcoord name.
-    CPT(InternalName) tangent_name = InternalName::get_tangent_name(source_name);
-    CPT(InternalName) binormal_name = InternalName::get_binormal_name(source_name);
-
-    CPT(InternalName) texcoord_name = stage->get_texcoord_name();
-
-    if (_munged_data->has_column(tangent_name) &&
-        _munged_data->has_column(binormal_name)) {
-      // Create a new column for the new texcoords.
-      PT(GeomVertexData) new_data = _munged_data->replace_column
-        (texcoord_name, 3, Geom::NT_float32, Geom::C_texcoord);
-      _munged_data = new_data;
-
-      // Remove this TexGen stage from the state, since we're handling
-      // it now.
-      _state = _state->add_attrib(tex_gen->remove_stage(stage));
+    if (light.is_empty()) {
+      // If a particular light isn't specified in the TexGenAttrib,
+      // use the most important light in the current state.
+      CPT(RenderAttrib) attrib = _state->get_attrib(LightAttrib::get_class_type());
+      if (attrib != (RenderAttrib *)NULL) {
+	CPT(LightAttrib) la = DCAST(LightAttrib, attrib);
+	light = la->get_most_important_light();
+	/*
+	if (!light.is_empty()) {
+	  // Remove that light, now that we're accounting for it in
+	  // the normal map.
+	  _state->set_attrib(la->remove_on_light(light));
+	}
+	*/
+      }
+    }
+    if (!light.is_empty()) {
+      string source_name = tex_gen->get_source_name(stage);
+      Light *light_obj = light.node()->as_light();
+      nassertv(light_obj != (Light *)NULL);
       
-      // Get the transform from the light to the object.
-      CPT(TransformState) light_transform =
-        net_transform->invert_compose(light.get_net_transform());
-      const LMatrix4f &light_mat = light_transform->get_mat();
-
-      GeomVertexWriter texcoord(new_data, texcoord_name);
-      GeomVertexReader vertex(new_data, InternalName::get_vertex());
-      GeomVertexReader tangent(new_data, tangent_name);
-      GeomVertexReader binormal(new_data, binormal_name);
-      GeomVertexReader normal(new_data, InternalName::get_normal());
+      // Determine the names of the tangent and binormal columns
+      // associated with the stage's texcoord name.
+      CPT(InternalName) tangent_name = InternalName::get_tangent_name(source_name);
+      CPT(InternalName) binormal_name = InternalName::get_binormal_name(source_name);
       
-      while (!vertex.is_at_end()) {
-        LPoint3f p = vertex.get_data3f();
-        LVector3f t = tangent.get_data3f();
-        LVector3f b = binormal.get_data3f();
-        LVector3f n = normal.get_data3f();
-
-        LVector3f lv;
-        if (light_obj->get_vector_to_light(lv, p, light_mat)) {
-          texcoord.add_data3f(lv.dot(t), lv.dot(b), lv.dot(n));
-        }
+      CPT(InternalName) texcoord_name = stage->get_texcoord_name();
+      
+      if (_munged_data->has_column(tangent_name) &&
+	  _munged_data->has_column(binormal_name)) {
+	// Create a new column for the new texcoords.
+	PT(GeomVertexData) new_data = _munged_data->replace_column
+	  (texcoord_name, 3, Geom::NT_float32, Geom::C_texcoord);
+	_munged_data = new_data;
+	
+	// Remove this TexGen stage from the state, since we're handling
+	// it now.
+	_state = _state->add_attrib(tex_gen->remove_stage(stage));
+	
+	// Get the transform from the light to the object.
+	CPT(TransformState) light_transform =
+	  net_transform->invert_compose(light.get_net_transform());
+	const LMatrix4f &light_mat = light_transform->get_mat();
+	
+	GeomVertexWriter texcoord(new_data, texcoord_name);
+	GeomVertexReader vertex(new_data, InternalName::get_vertex());
+	GeomVertexReader tangent(new_data, tangent_name);
+	GeomVertexReader binormal(new_data, binormal_name);
+	GeomVertexReader normal(new_data, InternalName::get_normal());
+	
+	while (!vertex.is_at_end()) {
+	  LPoint3f p = vertex.get_data3f();
+	  LVector3f t = tangent.get_data3f();
+	  LVector3f b = binormal.get_data3f();
+	  LVector3f n = normal.get_data3f();
+	  
+	  LVector3f lv;
+	  if (light_obj->get_vector_to_light(lv, p, light_mat)) {
+	    texcoord.add_data3f(lv.dot(t), lv.dot(b), lv.dot(n));
+	  }
+	}
       }
     }
   }

+ 28 - 0
panda/src/pgraph/lightAttrib.cxx

@@ -544,6 +544,34 @@ filter_to_max(int max_lights) const {
   return light_attrib;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: LightAttrib::get_most_important_light
+//       Access: Public
+//  Description: Returns the most important light (that is, the light
+//               with the highest priority) in the LightAttrib,
+//               excluding any ambient lights.  Returns an empty
+//               NodePath if no non-ambient lights are found.
+////////////////////////////////////////////////////////////////////
+NodePath LightAttrib::
+get_most_important_light() const {
+  NodePath best;
+
+  CompareLightPriorities compare;
+
+  Lights::const_iterator li;
+  for (li = _on_lights.begin(); li != _on_lights.end(); ++li) {
+    const NodePath &np = (*li);
+    nassertr(!np.is_empty() && np.node()->as_light() != (Light *)NULL, NodePath());
+    if (!np.node()->is_exact_type(AmbientLight::get_class_type())) {
+      if (best.is_empty() || compare(np, best)) {
+	best = np;
+      }
+    }
+  }
+
+  return best;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: LightAttrib::issue
 //       Access: Public, Virtual

+ 1 - 0
panda/src/pgraph/lightAttrib.h

@@ -92,6 +92,7 @@ PUBLISHED:
   CPT(RenderAttrib) remove_off_light(const NodePath &light) const;
 
   CPT(LightAttrib) filter_to_max(int max_lights) const;
+  NodePath get_most_important_light() const;
 
 public:
   virtual void issue(GraphicsStateGuardianBase *gsg) const;

+ 77 - 0
panda/src/pgraph/nodePath.cxx

@@ -43,6 +43,7 @@
 #include "transparencyAttrib.h"
 #include "antialiasAttrib.h"
 #include "texProjectorEffect.h"
+#include "texturePool.h"
 #include "planeNode.h"
 #include "lensNode.h"
 #include "materialPool.h"
@@ -3529,6 +3530,82 @@ project_texture(TextureStage *stage, Texture *tex, const NodePath &projector) {
   set_tex_projector(stage, NodePath(), projector);
 }
 
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::set_normal_map
+//       Access: Published
+//  Description: A convenience function to set up a normal map on this
+//               geometry.  This uses the single highest-priority
+//               light on the object only.  It also requires
+//               multitexture, and consumes at least two texture
+//               stages, in addition to what may already be in use.
+//
+//               The normal_map parameter is the texture that contains
+//               the normal map information (with a 3-d delta vector
+//               encoded into the r,g,b of each texel).  texcoord_name is
+//               the name of the texture coordinate set that contains
+//               the tangent and binormal we wish to use.
+//
+//               Only one normal map may be in effect through this
+//               interface at any given time.
+////////////////////////////////////////////////////////////////////
+void NodePath::
+set_normal_map(Texture *normal_map, const string &texcoord_name) {
+  clear_normal_map();
+
+  // First, we apply the normal map itself, to the bottom layer.
+  PT(TextureStage) normal_map_ts = new TextureStage("normal_map");
+  normal_map_ts->set_texcoord_name(texcoord_name);
+  normal_map_ts->set_sort(-20);
+  normal_map_ts->set_mode(TextureStage::M_replace);
+  set_texture(normal_map_ts, normal_map);
+
+  // Then, we apply a normalization map, to normalize, per-pixel, the
+  // vector to the light.
+  PT(Texture) normalization_map = TexturePool::get_normalization_cube_map(32);
+  PT(TextureStage) normalization_map_ts = new TextureStage("normalization_map");
+  normalization_map_ts->set_combine_rgb
+    (TextureStage::CM_dot3_rgb, 
+     TextureStage::CS_texture, TextureStage::CO_src_color,
+     TextureStage::CS_previous, TextureStage::CO_src_color);
+  normalization_map_ts->set_texcoord_name("light_vector");
+  normalization_map_ts->set_sort(-15);
+  set_texture(normalization_map_ts, normalization_map);
+
+  // Finally, we enable M_light_vector texture coordinate generation.
+  set_tex_gen(normalization_map_ts, TexGenAttrib::M_light_vector, 
+	      texcoord_name, NodePath());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NodePath::clear_normal_map
+//       Access: Published
+//  Description: Undoes the effect of a previous call to
+//               set_normal_map().
+////////////////////////////////////////////////////////////////////
+void NodePath::
+clear_normal_map() {
+  // Scan through the TextureStages, and if we find any whose name
+  // matches one of the stages that would have been left by
+  // set_normal_map(), remove it from the state.
+
+  CPT(RenderAttrib) attrib =
+    get_state()->get_attrib(TextureAttrib::get_class_type());
+  if (attrib != (const RenderAttrib *)NULL) {
+    const TextureAttrib *ta = DCAST(TextureAttrib, attrib);
+    for (int i = 0; i < ta->get_num_on_stages(); i++) {
+      TextureStage *stage = ta->get_on_stage(i);
+      if (stage->get_name() == "normal_map") {
+	clear_texture(stage);
+
+      } else if (stage->get_name() == "normalization_map") {
+	clear_texture(stage);
+	clear_tex_gen(stage);
+      }
+    }
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePath::find_texture
 //       Access: Published

+ 3 - 0
panda/src/pgraph/nodePath.h

@@ -600,6 +600,9 @@ PUBLISHED:
   void project_texture(TextureStage *stage, Texture *tex, const NodePath &projector);
   INLINE void clear_project_texture(TextureStage *stage);
 
+  void set_normal_map(Texture *normal_map, const string &texcoord_name = string());
+  void clear_normal_map();
+
   Texture *find_texture(const string &name) const;
   Texture *find_texture(TextureStage *stage) const;
   TextureCollection find_all_textures() const;

+ 11 - 12
panda/src/pgraph/texGenAttrib.cxx

@@ -93,20 +93,19 @@ add_stage(TextureStage *stage, TexGenAttrib::Mode mode,
 
   case M_light_vector:
     {
-      Light *light_obj = NULL;
       if (!light.is_empty()) {
-        light_obj = light.node()->as_light();
-      }
-      if (light_obj == (Light *)NULL) {
-        ostringstream strm;
-        strm << "Not a light: " << light;
-        nassert_raise(strm.str());
-
-      } else {
-        attrib->_light_vectors.insert(stage);
-        attrib->_geom_rendering |= Geom::GR_texcoord_light_vector;
-        attrib->_num_light_vectors++;
+	Light *light_obj = light.node()->as_light();
+	if (light_obj == (Light *)NULL) {
+	  ostringstream strm;
+	  strm << "Not a light: " << light;
+	  nassert_raise(strm.str());
+	  mode_def._light = NodePath();
+	}
       }
+	  
+      attrib->_light_vectors.insert(stage);
+      attrib->_geom_rendering |= Geom::GR_texcoord_light_vector;
+      attrib->_num_light_vectors++;
     }
     break;