diff --git a/panda/src/pgraph/cullableObject.cxx b/panda/src/pgraph/cullableObject.cxx index ad508ddfb7..145fb4a22a 100644 --- a/panda/src/pgraph/cullableObject.cxx +++ b/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)); + } + } } } } diff --git a/panda/src/pgraph/lightAttrib.cxx b/panda/src/pgraph/lightAttrib.cxx index 080dd02219..8283f22231 100644 --- a/panda/src/pgraph/lightAttrib.cxx +++ b/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 diff --git a/panda/src/pgraph/lightAttrib.h b/panda/src/pgraph/lightAttrib.h index ceaf293644..c96a2badc0 100644 --- a/panda/src/pgraph/lightAttrib.h +++ b/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; diff --git a/panda/src/pgraph/nodePath.cxx b/panda/src/pgraph/nodePath.cxx index 75660a23bc..02cd7f383e 100644 --- a/panda/src/pgraph/nodePath.cxx +++ b/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 diff --git a/panda/src/pgraph/nodePath.h b/panda/src/pgraph/nodePath.h index ed17ac907c..3d1558c018 100644 --- a/panda/src/pgraph/nodePath.h +++ b/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; diff --git a/panda/src/pgraph/texGenAttrib.cxx b/panda/src/pgraph/texGenAttrib.cxx index a51f9bfba2..3bbbd104a6 100755 --- a/panda/src/pgraph/texGenAttrib.cxx +++ b/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;