diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index c7341329f6..215367870e 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -27,6 +27,7 @@ #include "colorScaleAttrib.h" #include "lightAttrib.h" #include "textureAttrib.h" +#include "texGenAttrib.h" #include "renderState.h" #include "depthWriteAttrib.h" #include "colorWriteAttrib.h" diff --git a/panda/src/framework/windowFramework.cxx b/panda/src/framework/windowFramework.cxx index 7ce2cfbea7..af43c91a8b 100644 --- a/panda/src/framework/windowFramework.cxx +++ b/panda/src/framework/windowFramework.cxx @@ -848,10 +848,10 @@ set_lighting(bool enable) { if (!_got_lights) { setup_lights(); } - render.node()->set_attrib(LightAttrib::make(LightAttrib::O_add, - _alight, _dlight)); + render.set_light(_alight); + render.set_light(_dlight); } else { - render.node()->clear_attrib(LightAttrib::get_class_type()); + render.clear_light(); } _lighting_enabled = enable; @@ -963,11 +963,12 @@ setup_lights() { NodePath camera_group = get_camera_group(); NodePath light_group = camera_group.attach_new_node("lights"); - _alight = new AmbientLight("ambient"); - _alight->set_color(Colorf(0.2f, 0.2f, 0.2f, 1.0f)); - _dlight = new DirectionalLight("directional"); - light_group.attach_new_node(_alight); - light_group.attach_new_node(_dlight); + AmbientLight *alight = new AmbientLight("ambient"); + alight->set_color(Colorf(0.2f, 0.2f, 0.2f, 1.0f)); + DirectionalLight *dlight = new DirectionalLight("directional"); + + _alight = light_group.attach_new_node(alight); + _dlight = light_group.attach_new_node(dlight); _got_lights = true; } diff --git a/panda/src/framework/windowFramework.h b/panda/src/framework/windowFramework.h index c8a795f6f8..f74f47f255 100644 --- a/panda/src/framework/windowFramework.h +++ b/panda/src/framework/windowFramework.h @@ -144,8 +144,8 @@ private: NodePath _mouse; PT(Trackball) _trackball; - AmbientLight *_alight; - DirectionalLight *_dlight; + NodePath _alight; + NodePath _dlight; bool _got_keyboard; bool _got_trackball; diff --git a/panda/src/gobj/geomEnums.h b/panda/src/gobj/geomEnums.h index 888bf225c3..acef90be0d 100644 --- a/panda/src/gobj/geomEnums.h +++ b/panda/src/gobj/geomEnums.h @@ -132,6 +132,11 @@ PUBLISHED: // The union of the above shade model types. GR_shade_model_bits = 0x6000, + + // If there is a TexGenAttrib in effect with M_light_vector + // enabled, meaning we need to generate the tangent space light + // vector as the texture coordinates. + GR_texcoord_light_vector = 0x8000, }; // The shade model specifies whether the per-vertex colors and diff --git a/panda/src/gobj/texture.cxx b/panda/src/gobj/texture.cxx index 2544ea1517..21613a012b 100644 --- a/panda/src/gobj/texture.cxx +++ b/panda/src/gobj/texture.cxx @@ -92,12 +92,17 @@ void Texture:: setup_texture(Texture::TextureType texture_type, int x_size, int y_size, int z_size, Texture::ComponentType component_type, Texture::Format format) { -#ifndef NDEBUG if (texture_type == TT_cube_map) { // Cube maps must always consist of six square images. nassertv(x_size == y_size && z_size == 6); + + // In principle the wrap mode shouldn't mean anything to a cube + // map, but some drivers seem to misbehave if it's other than + // WM_clamp. + _wrap_u = WM_clamp; + _wrap_v = WM_clamp; + _wrap_w = WM_clamp; } -#endif _texture_type = texture_type; _x_size = x_size; @@ -110,6 +115,109 @@ setup_texture(Texture::TextureType texture_type, int x_size, int y_size, _loaded_from_disk = false; } +//////////////////////////////////////////////////////////////////// +// Function: Texture::generate_normalization_cube_map +// Access: Published +// Description: Generates a special cube map image in the texture +// that can be used to apply bump mapping effects: for +// each texel in the cube map that is indexed by the 3-d +// texture coordinates (x, y, z), the resulting value is +// the normalized vector (x, y, z) (compressed from +// -1..1 into 0..1). +// +// This also implicitly sets keep_ram_image to true. +//////////////////////////////////////////////////////////////////// +void Texture:: +generate_normalization_cube_map(int size) { + setup_cube_map(size, T_unsigned_byte, F_rgb); + PTA_uchar image = modify_ram_image(); + + float half_size = (float)size * 0.5f; + float center = half_size - 0.5f; + + LMatrix4f scale + (127.5f, 0.0f, 0.0f, 0.0f, + 0.0f, 127.5f, 0.0f, 0.0f, + 0.0f, 0.0f, 127.5f, 0.0f, + 127.5f, 127.5f, 127.5f, 1.0f); + + unsigned char *p = image; + int xi, yi; + + // Page 0: positive X. + for (yi = 0; yi < size; ++yi) { + for (xi = 0; xi < size; ++xi) { + LVector3f vec(half_size, center - yi, center - xi); + vec.normalize(); + vec = scale.xform_point(vec); + + *p++ = (unsigned char)vec[0]; + *p++ = (unsigned char)vec[1]; + *p++ = (unsigned char)vec[2]; + } + } + + // Page 1: negative X. + for (yi = 0; yi < size; ++yi) { + for (xi = 0; xi < size; ++xi) { + LVector3f vec(-half_size, center - yi, xi - center); + vec.normalize(); + vec = scale.xform_point(vec); + *p++ = (unsigned char)vec[0]; + *p++ = (unsigned char)vec[1]; + *p++ = (unsigned char)vec[2]; + } + } + + // Page 2: positive Y. + for (yi = 0; yi < size; ++yi) { + for (xi = 0; xi < size; ++xi) { + LVector3f vec(xi - center, half_size, yi - center); + vec.normalize(); + vec = scale.xform_point(vec); + *p++ = (unsigned char)vec[0]; + *p++ = (unsigned char)vec[1]; + *p++ = (unsigned char)vec[2]; + } + } + + // Page 3: negative Y. + for (yi = 0; yi < size; ++yi) { + for (xi = 0; xi < size; ++xi) { + LVector3f vec(xi - center, -half_size, center - yi); + vec.normalize(); + vec = scale.xform_point(vec); + *p++ = (unsigned char)vec[0]; + *p++ = (unsigned char)vec[1]; + *p++ = (unsigned char)vec[2]; + } + } + + // Page 4: positive Z. + for (yi = 0; yi < size; ++yi) { + for (xi = 0; xi < size; ++xi) { + LVector3f vec(xi - center, center - yi, half_size); + vec.normalize(); + vec = scale.xform_point(vec); + *p++ = (unsigned char)vec[0]; + *p++ = (unsigned char)vec[1]; + *p++ = (unsigned char)vec[2]; + } + } + + // Page 5: negative Z. + for (yi = 0; yi < size; ++yi) { + for (xi = 0; xi < size; ++xi) { + LVector3f vec(center - xi, center - yi, -half_size); + vec.normalize(); + vec = scale.xform_point(vec); + *p++ = (unsigned char)vec[0]; + *p++ = (unsigned char)vec[1]; + *p++ = (unsigned char)vec[2]; + } + } +} + //////////////////////////////////////////////////////////////////// // Function: Texture::read // Access: Published diff --git a/panda/src/gobj/texture.h b/panda/src/gobj/texture.h index 6c7fc71d81..521e4642ae 100644 --- a/panda/src/gobj/texture.h +++ b/panda/src/gobj/texture.h @@ -150,6 +150,8 @@ PUBLISHED: INLINE void setup_cube_map(int size, ComponentType component_type, Format format); + void generate_normalization_cube_map(int size); + bool read(const Filename &fullpath, int z = 0, int primary_file_num_channels = 0); bool read(const Filename &fullpath, const Filename &alpha_fullpath, diff --git a/panda/src/pgraph/clipPlaneAttrib.cxx b/panda/src/pgraph/clipPlaneAttrib.cxx index 09b628d890..8d9916989a 100644 --- a/panda/src/pgraph/clipPlaneAttrib.cxx +++ b/panda/src/pgraph/clipPlaneAttrib.cxx @@ -23,6 +23,7 @@ #include "bamWriter.h" #include "datagram.h" #include "datagramIterator.h" +#include "config_pgraph.h" CPT(RenderAttrib) ClipPlaneAttrib::_empty_attrib; CPT(RenderAttrib) ClipPlaneAttrib::_all_off_attrib; @@ -39,6 +40,9 @@ TypeHandle ClipPlaneAttrib::_type_handle; //////////////////////////////////////////////////////////////////// CPT(RenderAttrib) ClipPlaneAttrib:: make(ClipPlaneAttrib::Operation op, PlaneNode *plane) { + pgraph_cat.warning() + << "Using deprecated ClipPlaneAttrib interface.\n"; + CPT(RenderAttrib) attrib; switch (op) { @@ -73,6 +77,9 @@ make(ClipPlaneAttrib::Operation op, PlaneNode *plane) { //////////////////////////////////////////////////////////////////// CPT(RenderAttrib) ClipPlaneAttrib:: make(ClipPlaneAttrib::Operation op, PlaneNode *plane1, PlaneNode *plane2) { + pgraph_cat.warning() + << "Using deprecated ClipPlaneAttrib interface.\n"; + CPT(RenderAttrib) attrib; switch (op) { @@ -111,6 +118,9 @@ make(ClipPlaneAttrib::Operation op, PlaneNode *plane1, PlaneNode *plane2) { CPT(RenderAttrib) ClipPlaneAttrib:: make(ClipPlaneAttrib::Operation op, PlaneNode *plane1, PlaneNode *plane2, PlaneNode *plane3) { + pgraph_cat.warning() + << "Using deprecated ClipPlaneAttrib interface.\n"; + CPT(RenderAttrib) attrib; switch (op) { @@ -152,6 +162,9 @@ make(ClipPlaneAttrib::Operation op, PlaneNode *plane1, PlaneNode *plane2, CPT(RenderAttrib) ClipPlaneAttrib:: make(ClipPlaneAttrib::Operation op, PlaneNode *plane1, PlaneNode *plane2, PlaneNode *plane3, PlaneNode *plane4) { + pgraph_cat.warning() + << "Using deprecated ClipPlaneAttrib interface.\n"; + CPT(RenderAttrib) attrib; switch (op) { @@ -202,6 +215,9 @@ make(ClipPlaneAttrib::Operation op, PlaneNode *plane1, PlaneNode *plane2, //////////////////////////////////////////////////////////////////// ClipPlaneAttrib::Operation ClipPlaneAttrib:: get_operation() const { + pgraph_cat.warning() + << "Using deprecated ClipPlaneAttrib interface.\n"; + if (has_all_off()) { return O_set; @@ -225,6 +241,9 @@ get_operation() const { //////////////////////////////////////////////////////////////////// int ClipPlaneAttrib:: get_num_planes() const { + pgraph_cat.warning() + << "Using deprecated ClipPlaneAttrib interface.\n"; + if (get_num_off_planes() == 0) { return get_num_on_planes(); } else { @@ -244,6 +263,9 @@ get_num_planes() const { //////////////////////////////////////////////////////////////////// PlaneNode *ClipPlaneAttrib:: get_plane(int n) const { + pgraph_cat.warning() + << "Using deprecated ClipPlaneAttrib interface.\n"; + if (get_num_off_planes() == 0) { return DCAST(PlaneNode, get_on_plane(n).node()); } else { @@ -264,6 +286,9 @@ get_plane(int n) const { //////////////////////////////////////////////////////////////////// bool ClipPlaneAttrib:: has_plane(PlaneNode *plane) const { + pgraph_cat.warning() + << "Using deprecated ClipPlaneAttrib interface.\n"; + if (get_num_off_planes() == 0) { return has_on_plane(NodePath(plane)); } else { @@ -282,6 +307,9 @@ has_plane(PlaneNode *plane) const { //////////////////////////////////////////////////////////////////// CPT(RenderAttrib) ClipPlaneAttrib:: add_plane(PlaneNode *plane) const { + pgraph_cat.warning() + << "Using deprecated ClipPlaneAttrib interface.\n"; + if (get_num_off_planes() == 0) { return add_on_plane(NodePath(plane)); } else { @@ -301,6 +329,9 @@ add_plane(PlaneNode *plane) const { //////////////////////////////////////////////////////////////////// CPT(RenderAttrib) ClipPlaneAttrib:: remove_plane(PlaneNode *plane) const { + pgraph_cat.warning() + << "Using deprecated ClipPlaneAttrib interface.\n"; + if (get_num_off_planes() == 0) { return remove_on_plane(NodePath(plane)); } else { diff --git a/panda/src/pgraph/cullableObject.cxx b/panda/src/pgraph/cullableObject.cxx index 6bd19dc575..5676d859d4 100644 --- a/panda/src/pgraph/cullableObject.cxx +++ b/panda/src/pgraph/cullableObject.cxx @@ -18,6 +18,7 @@ #include "cullableObject.h" #include "textureAttrib.h" +#include "texGenAttrib.h" #include "renderState.h" #include "clockObject.h" #include "cullTraverser.h" @@ -28,8 +29,10 @@ #include "geomVertexWriter.h" #include "geomVertexReader.h" #include "geomTriangles.h" +#include "light.h" PStatCollector CullableObject::_munge_points_pcollector("*:Munge:Points"); +PStatCollector CullableObject::_munge_light_vector_pcollector("*:Munge:Light Vector"); CullableObject *CullableObject::_deleted_chain = (CullableObject *)NULL; int CullableObject::_num_ever_allocated = 0; @@ -75,6 +78,9 @@ munge_geom(GraphicsStateGuardianBase *gsg, } munge_points_to_quads(traverser); } + if (unsupported_bits & Geom::GR_texcoord_light_vector) { + munge_texcoord_light_vector(traverser); + } // Now invoke the munger to ensure the resulting geometry is in // a GSG-friendly form. @@ -369,8 +375,8 @@ munge_points_to_quads(const CullTraverser *traverser) { LPoint2f c0(scale_x, scale_y); LPoint2f c1(-scale_x, scale_y); - if (has_rotate) { - // If we have a rotate factor, apply it to those two corners. + if (has_rotate) { + // If we have a rotate factor, apply it to those two corners. rotate.set_row(*vi); float r = rotate.get_data1f(); LMatrix3f mat = LMatrix3f::rotate_mat(r); @@ -439,3 +445,91 @@ munge_points_to_quads(const CullTraverser *traverser) { _geom = new_geom.p(); _munged_data = new_data; } + +//////////////////////////////////////////////////////////////////// +// Function: CullableObject::munge_texcoord_light_vector +// Access: Private +// Description: Generates the vector from each vertex to the +// indicated light as a 3-d texture coordinate. +// +// This may replace _geom, _munged_data, and _state. +//////////////////////////////////////////////////////////////////// +void CullableObject:: +munge_texcoord_light_vector(const CullTraverser *traverser) { + PStatTimer timer(_munge_light_vector_pcollector); + + if (_modelview_transform->is_singular()) { + // If we're under a singular transform, never mind. + return; + } + + CPT(TransformState) net_transform = + traverser->get_camera_transform()->compose(_modelview_transform); + + if (!_munged_data->has_column(InternalName::get_vertex()) || + !_munged_data->has_column(InternalName::get_normal())) { + // No vertex or normal; can't compute light vector. + return; + } + + CPT(TexGenAttrib) tex_gen = _state->get_tex_gen(); + nassertv(tex_gen != (TexGenAttrib *)NULL); + + const TexGenAttrib::LightVectors &light_vectors = tex_gen->get_light_vectors(); + TexGenAttrib::LightVectors::const_iterator lvi; + for (lvi = light_vectors.begin(); + lvi != light_vectors.end(); + ++lvi) { + TextureStage *stage = (*lvi).first; + const NodePath &light = (*lvi).second; + nassertv(!light.is_empty()); + 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) texcoord_name = stage->get_texcoord_name(); + string basename; + if (texcoord_name != InternalName::get_texcoord()) { + basename = texcoord_name->get_basename(); + } + + CPT(InternalName) tangent_name = InternalName::get_tangent_name(basename); + CPT(InternalName) binormal_name = InternalName::get_binormal_name(basename); + + 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/cullableObject.h b/panda/src/pgraph/cullableObject.h index 58ef25f72a..426dfa0b7d 100644 --- a/panda/src/pgraph/cullableObject.h +++ b/panda/src/pgraph/cullableObject.h @@ -85,6 +85,7 @@ public: private: void munge_points_to_quads(const CullTraverser *traverser); + void munge_texcoord_light_vector(const CullTraverser *traverser); private: // This class is used internally by munge_points_to_quads(). @@ -105,6 +106,7 @@ private: static int _num_ever_allocated; static PStatCollector _munge_points_pcollector; + static PStatCollector _munge_light_vector_pcollector; public: static TypeHandle get_class_type() { diff --git a/panda/src/pgraph/directionalLight.cxx b/panda/src/pgraph/directionalLight.cxx index 1fa2ace751..04a49cc778 100644 --- a/panda/src/pgraph/directionalLight.cxx +++ b/panda/src/pgraph/directionalLight.cxx @@ -133,6 +133,32 @@ write(ostream &out, int indent_level) const { << "direction " << get_direction() << "\n"; } +//////////////////////////////////////////////////////////////////// +// Function: DirectionalLight::get_vector_to_light +// Access: Public, Virtual +// Description: Computes the vector from a particular vertex to this +// light. The exact vector depends on the type of light +// (e.g. point lights return a different result than +// directional lights). +// +// The input parameters are the vertex position in +// question, expressed in object space, and the matrix +// which converts from light space to object space. The +// result is expressed in object space. +// +// The return value is true if the result is successful, +// or false if it cannot be computed (e.g. for an +// ambient light). +//////////////////////////////////////////////////////////////////// +bool DirectionalLight:: +get_vector_to_light(LVector3f &result, const LPoint3f &, + const LMatrix4f &to_object_space) { + CDReader cdata(_cycler); + result = cdata->_direction * to_object_space; + + return true; +} + //////////////////////////////////////////////////////////////////// // Function: DirectionalLight::bind // Access: Public, Virtual diff --git a/panda/src/pgraph/directionalLight.h b/panda/src/pgraph/directionalLight.h index 79a98b4d05..28615aebbd 100644 --- a/panda/src/pgraph/directionalLight.h +++ b/panda/src/pgraph/directionalLight.h @@ -40,6 +40,10 @@ public: virtual void xform(const LMatrix4f &mat); virtual void write(ostream &out, int indent_level) const; + virtual bool get_vector_to_light(LVector3f &result, + const LPoint3f &from_object_point, + const LMatrix4f &to_object_space); + PUBLISHED: INLINE const Colorf &get_specular_color() const; INLINE void set_specular_color(const Colorf &color); diff --git a/panda/src/pgraph/light.cxx b/panda/src/pgraph/light.cxx index 265a0374e8..b5697a9b90 100644 --- a/panda/src/pgraph/light.cxx +++ b/panda/src/pgraph/light.cxx @@ -67,6 +67,28 @@ Light:: ~Light() { } +//////////////////////////////////////////////////////////////////// +// Function: Light::get_vector_to_light +// Access: Public, Virtual +// Description: Computes the vector from a particular vertex to this +// light. The exact vector depends on the type of light +// (e.g. point lights return a different result than +// directional lights). +// +// The input parameters are the vertex position in +// question, expressed in object space, and the matrix +// which converts from light space to object space. The +// result is expressed in object space. +// +// The return value is true if the result is successful, +// or false if it cannot be computed (e.g. for an +// ambient light). +//////////////////////////////////////////////////////////////////// +bool Light:: +get_vector_to_light(LVector3f &, const LPoint3f &, const LMatrix4f &) { + return false; +} + //////////////////////////////////////////////////////////////////// // Function: Light::get_viz // Access: Public diff --git a/panda/src/pgraph/light.h b/panda/src/pgraph/light.h index 5842992b94..930bf72983 100644 --- a/panda/src/pgraph/light.h +++ b/panda/src/pgraph/light.h @@ -62,6 +62,10 @@ public: virtual void bind(GraphicsStateGuardianBase *gsg, const NodePath &light, int light_id)=0; + virtual bool get_vector_to_light(LVector3f &result, + const LPoint3f &from_object_point, + const LMatrix4f &to_object_space); + GeomNode *get_viz(); protected: diff --git a/panda/src/pgraph/lightAttrib.cxx b/panda/src/pgraph/lightAttrib.cxx index 88c420392b..98818c4797 100644 --- a/panda/src/pgraph/lightAttrib.cxx +++ b/panda/src/pgraph/lightAttrib.cxx @@ -24,6 +24,7 @@ #include "bamWriter.h" #include "datagram.h" #include "datagramIterator.h" +#include "config_pgraph.h" CPT(RenderAttrib) LightAttrib::_empty_attrib; CPT(RenderAttrib) LightAttrib::_all_off_attrib; @@ -40,6 +41,9 @@ TypeHandle LightAttrib::_type_handle; //////////////////////////////////////////////////////////////////// CPT(RenderAttrib) LightAttrib:: make(LightAttrib::Operation op, Light *light) { + pgraph_cat.warning() + << "Using deprecated LightAttrib interface.\n"; + CPT(RenderAttrib) attrib; switch (op) { @@ -74,6 +78,9 @@ make(LightAttrib::Operation op, Light *light) { //////////////////////////////////////////////////////////////////// CPT(RenderAttrib) LightAttrib:: make(LightAttrib::Operation op, Light *light1, Light *light2) { + pgraph_cat.warning() + << "Using deprecated LightAttrib interface.\n"; + CPT(RenderAttrib) attrib; switch (op) { @@ -111,7 +118,10 @@ make(LightAttrib::Operation op, Light *light1, Light *light2) { //////////////////////////////////////////////////////////////////// CPT(RenderAttrib) LightAttrib:: make(LightAttrib::Operation op, Light *light1, Light *light2, - Light *light3) { + Light *light3) { + pgraph_cat.warning() + << "Using deprecated LightAttrib interface.\n"; + CPT(RenderAttrib) attrib; switch (op) { @@ -153,6 +163,9 @@ make(LightAttrib::Operation op, Light *light1, Light *light2, CPT(RenderAttrib) LightAttrib:: make(LightAttrib::Operation op, Light *light1, Light *light2, Light *light3, Light *light4) { + pgraph_cat.warning() + << "Using deprecated LightAttrib interface.\n"; + CPT(RenderAttrib) attrib; switch (op) { @@ -203,6 +216,9 @@ make(LightAttrib::Operation op, Light *light1, Light *light2, //////////////////////////////////////////////////////////////////// LightAttrib::Operation LightAttrib:: get_operation() const { + pgraph_cat.warning() + << "Using deprecated LightAttrib interface.\n"; + if (has_all_off()) { return O_set; @@ -226,6 +242,9 @@ get_operation() const { //////////////////////////////////////////////////////////////////// int LightAttrib:: get_num_lights() const { + pgraph_cat.warning() + << "Using deprecated LightAttrib interface.\n"; + if (get_num_off_lights() == 0) { return get_num_on_lights(); } else { @@ -245,6 +264,9 @@ get_num_lights() const { //////////////////////////////////////////////////////////////////// Light *LightAttrib:: get_light(int n) const { + pgraph_cat.warning() + << "Using deprecated LightAttrib interface.\n"; + if (get_num_off_lights() == 0) { return get_on_light(n).node()->as_light(); } else { @@ -265,6 +287,9 @@ get_light(int n) const { //////////////////////////////////////////////////////////////////// bool LightAttrib:: has_light(Light *light) const { + pgraph_cat.warning() + << "Using deprecated LightAttrib interface.\n"; + if (get_num_off_lights() == 0) { return has_on_light(NodePath(light->as_node())); } else { @@ -283,6 +308,9 @@ has_light(Light *light) const { //////////////////////////////////////////////////////////////////// CPT(RenderAttrib) LightAttrib:: add_light(Light *light) const { + pgraph_cat.warning() + << "Using deprecated LightAttrib interface.\n"; + if (get_num_off_lights() == 0) { return add_on_light(NodePath(light->as_node())); } else { @@ -302,6 +330,9 @@ add_light(Light *light) const { //////////////////////////////////////////////////////////////////// CPT(RenderAttrib) LightAttrib:: remove_light(Light *light) const { + pgraph_cat.warning() + << "Using deprecated LightAttrib interface.\n"; + if (get_num_off_lights() == 0) { return remove_on_light(NodePath(light->as_node())); } else { diff --git a/panda/src/pgraph/nodePath.cxx b/panda/src/pgraph/nodePath.cxx index 941823b656..74918149aa 100644 --- a/panda/src/pgraph/nodePath.cxx +++ b/panda/src/pgraph/nodePath.cxx @@ -26,6 +26,7 @@ #include "cullBinAttrib.h" #include "textureAttrib.h" #include "texMatrixAttrib.h" +#include "texGenAttrib.h" #include "materialAttrib.h" #include "lightAttrib.h" #include "clipPlaneAttrib.h" @@ -3226,7 +3227,7 @@ get_tex_transform(const NodePath &other, TextureStage *stage) const { // the indicated texture stage. //////////////////////////////////////////////////////////////////// void NodePath:: -set_tex_gen(TextureStage *stage, TexGenAttrib::Mode mode, int priority) { +set_tex_gen(TextureStage *stage, RenderAttrib::TexGenMode mode, int priority) { nassertv_always(!is_empty()); const RenderAttrib *attrib = @@ -3246,6 +3247,36 @@ set_tex_gen(TextureStage *stage, TexGenAttrib::Mode mode, int priority) { node()->set_attrib(tga->add_stage(stage, mode), priority); } +//////////////////////////////////////////////////////////////////// +// Function: NodePath::set_tex_gen +// Access: Published +// Description: Enables automatic texture coordinate generation for +// the indicated texture stage. This version of this +// method is useful when setting M_light_vector, which +// requires a specific light. +//////////////////////////////////////////////////////////////////// +void NodePath:: +set_tex_gen(TextureStage *stage, RenderAttrib::TexGenMode mode, + const NodePath &light, int priority) { + nassertv_always(!is_empty()); + + const RenderAttrib *attrib = + node()->get_attrib(TexGenAttrib::get_class_type()); + + CPT(TexGenAttrib) tga; + + if (attrib != (const RenderAttrib *)NULL) { + priority = max(priority, + node()->get_state()->get_override(TextureAttrib::get_class_type())); + tga = DCAST(TexGenAttrib, attrib); + + } else { + tga = DCAST(TexGenAttrib, TexGenAttrib::make()); + } + + node()->set_attrib(tga->add_stage(stage, mode, light), priority); +} + //////////////////////////////////////////////////////////////////// // Function: NodePath::clear_tex_gen // Access: Published @@ -3311,7 +3342,7 @@ has_tex_gen(TextureStage *stage) const { // the given stage, or M_off if there is no explicit // mode set for the given stage. //////////////////////////////////////////////////////////////////// -TexGenAttrib::Mode NodePath:: +RenderAttrib::TexGenMode NodePath:: get_tex_gen(TextureStage *stage) const { nassertr_always(!is_empty(), TexGenAttrib::M_off); @@ -3325,6 +3356,28 @@ get_tex_gen(TextureStage *stage) const { return TexGenAttrib::M_off; } +//////////////////////////////////////////////////////////////////// +// Function: NodePath::get_tex_gen_light +// Access: Published +// Description: Returns the particular Light set for the indicated +// texgen mode's texture stage, or empty NodePath if no +// light is set. This is only meaningful if the texgen +// mode (returned by get_tex_gen()) is M_light_vector. +//////////////////////////////////////////////////////////////////// +NodePath NodePath:: +get_tex_gen_light(TextureStage *stage) const { + nassertr_always(!is_empty(), NodePath::fail()); + + const RenderAttrib *attrib = + node()->get_attrib(TexGenAttrib::get_class_type()); + if (attrib != (const RenderAttrib *)NULL) { + const TexGenAttrib *tga = DCAST(TexGenAttrib, attrib); + return tga->get_light(stage); + } + + return NodePath(); +} + //////////////////////////////////////////////////////////////////// // Function: NodePath::set_tex_projector // Access: Published diff --git a/panda/src/pgraph/nodePath.h b/panda/src/pgraph/nodePath.h index 701b4d3a86..3cadf0a033 100644 --- a/panda/src/pgraph/nodePath.h +++ b/panda/src/pgraph/nodePath.h @@ -24,7 +24,6 @@ #include "pandaNode.h" #include "renderState.h" #include "transformState.h" -#include "texGenAttrib.h" #include "renderModeAttrib.h" #include "transparencyAttrib.h" #include "nodePathComponent.h" @@ -581,11 +580,13 @@ PUBLISHED: INLINE float get_tex_rotate(const NodePath &other, TextureStage *stage) const; INLINE LVecBase2f get_tex_scale(const NodePath &other, TextureStage *stage) const; - void set_tex_gen(TextureStage *stage, TexGenAttrib::Mode mode, int priority = 0); + void set_tex_gen(TextureStage *stage, RenderAttrib::TexGenMode mode, int priority = 0); + void set_tex_gen(TextureStage *stage, RenderAttrib::TexGenMode mode, const NodePath &light, int priority = 0); void clear_tex_gen(); void clear_tex_gen(TextureStage *stage); bool has_tex_gen(TextureStage *stage) const; - TexGenAttrib::Mode get_tex_gen(TextureStage *stage) const; + RenderAttrib::TexGenMode get_tex_gen(TextureStage *stage) const; + NodePath get_tex_gen_light(TextureStage *stage) const; void set_tex_projector(TextureStage *stage, const NodePath &from, const NodePath &to); void clear_tex_projector(TextureStage *stage); diff --git a/panda/src/pgraph/pointLight.cxx b/panda/src/pgraph/pointLight.cxx index 21a8599f73..6e5027a1a4 100644 --- a/panda/src/pgraph/pointLight.cxx +++ b/panda/src/pgraph/pointLight.cxx @@ -132,6 +132,33 @@ write(ostream &out, int indent_level) const { << "attenuation " << get_attenuation() << "\n"; } +//////////////////////////////////////////////////////////////////// +// Function: PointLight::get_vector_to_light +// Access: Public, Virtual +// Description: Computes the vector from a particular vertex to this +// light. The exact vector depends on the type of light +// (e.g. point lights return a different result than +// directional lights). +// +// The input parameters are the vertex position in +// question, expressed in object space, and the matrix +// which converts from light space to object space. The +// result is expressed in object space. +// +// The return value is true if the result is successful, +// or false if it cannot be computed (e.g. for an +// ambient light). +//////////////////////////////////////////////////////////////////// +bool PointLight:: +get_vector_to_light(LVector3f &result, const LPoint3f &from_object_point, + const LMatrix4f &to_object_space) { + CDReader cdata(_cycler); + LPoint3f point = cdata->_point * to_object_space; + + result = point - from_object_point; + return true; +} + //////////////////////////////////////////////////////////////////// // Function: PointLight::bind // Access: Public, Virtual diff --git a/panda/src/pgraph/pointLight.h b/panda/src/pgraph/pointLight.h index 47ffe4c538..3b3599dc86 100644 --- a/panda/src/pgraph/pointLight.h +++ b/panda/src/pgraph/pointLight.h @@ -40,6 +40,10 @@ public: virtual void xform(const LMatrix4f &mat); virtual void write(ostream &out, int indent_level) const; + virtual bool get_vector_to_light(LVector3f &result, + const LPoint3f &from_object_point, + const LMatrix4f &to_object_space); + PUBLISHED: INLINE const Colorf &get_specular_color() const; INLINE void set_specular_color(const Colorf &color); diff --git a/panda/src/pgraph/renderAttrib.h b/panda/src/pgraph/renderAttrib.h index 7a65a99a99..0bec58454a 100644 --- a/panda/src/pgraph/renderAttrib.h +++ b/panda/src/pgraph/renderAttrib.h @@ -92,6 +92,74 @@ PUBLISHED: M_always // Always draw. }; + // This is the enumerated type for TexGenAttrib. It is inherited + // into TexGenAttrib. It is defined up at this level only to avoid + // circular dependencies in the header files. + enum TexGenMode { + M_off, + + // In the types below, "eye" means the coordinate space of the + // observing camera, "object" means the local coordinate space of + // the object, and "world" means world coordinates, e.g. the + // coordinate space of the root of the graph. + + // Sphere maps are classic static reflection maps. They are + // supported on just about any hardware, and require a precomputed + // 180-degree fisheye image. Sphere maps only make sense in eye + // coordinate space. + M_eye_sphere_map, + + // Cube maps are a modern improvement on the sphere map; they + // don't suffer from any polar singularities, but they require six + // texture images. They can also be generated dynamically for + // real-time reflections (see GraphicsOutput::make_cube_map()). + // Typically, a statically-generated cube map will be in eye + // space, while a dynamically-generated map will be in world space + // or object space (depending on where the camera rig that + // generates the map is parented). + + // Cube mapping is not supported on all hardware. + M_world_cube_map, + M_eye_cube_map, + + // Normal maps are most useful for applying diffuse lighting + // effects via a pregenerated cube map. + M_world_normal, + M_eye_normal, + + // Position maps convert XYZ coordinates directly to texture + // coordinates. This is particularly useful for implementing + // projective texturing (see NodePath::project_texture()). + M_world_position, + M_object_position, + M_eye_position, + + // With M_point_sprite, texture coordinates will be generated for + // large points in the range (0,0) - (1,1) from upper-left to + // lower-right across the point's face. Without this, each point + // will have just a single uniform texture coordinate value across + // its face. + + // Unfortunately, the generated texture coordinates are inverted + // (upside-down) from Panda's usual convention, but this is what + // the graphics card manufacturers decided to use. You could use + // a texture matrix to re-invert the texture, but that will + // probably force software rendering. You'll have to paint your + // textures upside-down if you want true hardware sprites. + M_point_sprite, + + // M_light_vector generates special 3-d texture coordinates that + // represent the vector to a particular Light in the scene graph, + // expressed in each vertex's tangent space. This is used to + // implement bumpmapping. + + // This requires a Light to be specified to the TexGenAttrib. It + // also requires each vertex to define a normal, as well as a + // tangent and binormal for the particular named texture + // coordinate set. + M_light_vector, + }; + protected: static CPT(RenderAttrib) return_new(RenderAttrib *attrib); virtual int compare_to_impl(const RenderAttrib *other) const; diff --git a/panda/src/pgraph/renderState.I b/panda/src/pgraph/renderState.I index ca86c0e796..ddea42e98f 100644 --- a/panda/src/pgraph/renderState.I +++ b/panda/src/pgraph/renderState.I @@ -336,29 +336,6 @@ get_render_mode() const { return _render_mode; } -//////////////////////////////////////////////////////////////////// -// Function: RenderState::get_geom_rendering -// Access: Published -// Description: Returns the union of the Geom::GeomRendering bits -// that will be required once this RenderState is -// applied to a geom which includes the indicated -// geom_rendering bits. -//////////////////////////////////////////////////////////////////// -INLINE int RenderState:: -get_geom_rendering(int geom_rendering) const { - if (get_render_mode() != (const RenderModeAttrib *)NULL) { - geom_rendering = _render_mode->get_geom_rendering(geom_rendering); - } - if (get_tex_gen() != (const TexGenAttrib *)NULL) { - geom_rendering = _tex_gen->get_geom_rendering(geom_rendering); - } - if (get_tex_matrix() != (const TexMatrixAttrib *)NULL) { - geom_rendering = _tex_matrix->get_geom_rendering(geom_rendering); - } - - return geom_rendering; -} - //////////////////////////////////////////////////////////////////// // Function: RenderState::set_destructing // Access: Private diff --git a/panda/src/pgraph/renderState.cxx b/panda/src/pgraph/renderState.cxx index 0530e65e02..92759e9583 100644 --- a/panda/src/pgraph/renderState.cxx +++ b/panda/src/pgraph/renderState.cxx @@ -930,6 +930,29 @@ validate_states() { return true; } +//////////////////////////////////////////////////////////////////// +// Function: RenderState::get_geom_rendering +// Access: Published +// Description: Returns the union of the Geom::GeomRendering bits +// that will be required once this RenderState is +// applied to a geom which includes the indicated +// geom_rendering bits. +//////////////////////////////////////////////////////////////////// +int RenderState:: +get_geom_rendering(int geom_rendering) const { + if (get_render_mode() != (const RenderModeAttrib *)NULL) { + geom_rendering = _render_mode->get_geom_rendering(geom_rendering); + } + if (get_tex_gen() != (const TexGenAttrib *)NULL) { + geom_rendering = _tex_gen->get_geom_rendering(geom_rendering); + } + if (get_tex_matrix() != (const TexMatrixAttrib *)NULL) { + geom_rendering = _tex_matrix->get_geom_rendering(geom_rendering); + } + + return geom_rendering; +} + //////////////////////////////////////////////////////////////////// // Function: RenderState::issue_delta_modify // Access: Public diff --git a/panda/src/pgraph/renderState.h b/panda/src/pgraph/renderState.h index fb27fbf59d..80ff96d312 100644 --- a/panda/src/pgraph/renderState.h +++ b/panda/src/pgraph/renderState.h @@ -28,7 +28,6 @@ #include "updateSeq.h" #include "pStatCollector.h" #include "renderModeAttrib.h" -#include "texGenAttrib.h" #include "texMatrixAttrib.h" #include "geomMunger.h" #include "weakPointerTo.h" @@ -40,6 +39,7 @@ class TransparencyAttrib; class ColorAttrib; class ColorScaleAttrib; class TextureAttrib; +class TexGenAttrib; class FactoryParams; //////////////////////////////////////////////////////////////////// @@ -134,7 +134,7 @@ PUBLISHED: INLINE const TexMatrixAttrib *get_tex_matrix() const; INLINE const RenderModeAttrib *get_render_mode() const; - INLINE int get_geom_rendering(int geom_rendering) const; + int get_geom_rendering(int geom_rendering) const; public: CPT(RenderState) issue_delta_modify(const RenderState *other, diff --git a/panda/src/pgraph/spotlight.cxx b/panda/src/pgraph/spotlight.cxx index 1fb892e376..7d6246c652 100644 --- a/panda/src/pgraph/spotlight.cxx +++ b/panda/src/pgraph/spotlight.cxx @@ -141,6 +141,29 @@ write(ostream &out, int indent_level) const { } } +//////////////////////////////////////////////////////////////////// +// Function: Spotlight::get_vector_to_light +// Access: Public, Virtual +// Description: Computes the vector from a particular vertex to this +// light. The exact vector depends on the type of light +// (e.g. point lights return a different result than +// directional lights). +// +// The input parameters are the vertex position in +// question, expressed in object space, and the matrix +// which converts from light space to object space. The +// result is expressed in object space. +// +// The return value is true if the result is successful, +// or false if it cannot be computed (e.g. for an +// ambient light). +//////////////////////////////////////////////////////////////////// +bool Spotlight:: +get_vector_to_light(LVector3f &result, const LPoint3f &from_object_point, + const LMatrix4f &to_object_space) { + return false; +} + //////////////////////////////////////////////////////////////////// // Function: Spotlight::make_spot // Access: Published, Static diff --git a/panda/src/pgraph/spotlight.h b/panda/src/pgraph/spotlight.h index 65695242b6..6a4fb6f5a8 100644 --- a/panda/src/pgraph/spotlight.h +++ b/panda/src/pgraph/spotlight.h @@ -51,6 +51,10 @@ public: virtual void xform(const LMatrix4f &mat); virtual void write(ostream &out, int indent_level) const; + virtual bool get_vector_to_light(LVector3f &result, + const LPoint3f &from_object_point, + const LMatrix4f &to_object_space); + PUBLISHED: INLINE float get_exponent() const; INLINE void set_exponent(float exponent); diff --git a/panda/src/pgraph/texGenAttrib.I b/panda/src/pgraph/texGenAttrib.I index fd8903f0be..9a04e5487b 100755 --- a/panda/src/pgraph/texGenAttrib.I +++ b/panda/src/pgraph/texGenAttrib.I @@ -25,7 +25,10 @@ //////////////////////////////////////////////////////////////////// INLINE TexGenAttrib:: TexGenAttrib() : - _num_point_sprites(0) + _num_point_sprites(0), + _num_light_vectors(0), + _point_geom_rendering(0), + _geom_rendering(0) { } @@ -39,7 +42,10 @@ INLINE TexGenAttrib:: TexGenAttrib(const TexGenAttrib ©) : _stages(copy._stages), _no_texcoords(copy._no_texcoords), - _num_point_sprites(copy._num_point_sprites) + _num_point_sprites(copy._num_point_sprites), + _num_light_vectors(copy._num_light_vectors), + _point_geom_rendering(copy._point_geom_rendering), + _geom_rendering(copy._geom_rendering) { } @@ -54,12 +60,10 @@ TexGenAttrib(const TexGenAttrib ©) : INLINE int TexGenAttrib:: get_geom_rendering(int geom_rendering) const { if ((geom_rendering & Geom::GR_point) != 0) { - if (_num_point_sprites > 0) { - geom_rendering |= Geom::GR_point_sprite; - } + geom_rendering |= _point_geom_rendering; } - return geom_rendering; + return geom_rendering | _geom_rendering; } //////////////////////////////////////////////////////////////////// @@ -75,3 +79,40 @@ INLINE const Geom::NoTexCoordStages &TexGenAttrib:: get_no_texcoords() const { return _no_texcoords; } + +//////////////////////////////////////////////////////////////////// +// Function: TexGenAttrib::get_light_vectors +// Access: Public +// Description: Returns the set of TextureStages that have +// M_light_vector in effect, as well as the associated +// Lights. +//////////////////////////////////////////////////////////////////// +INLINE const TexGenAttrib::LightVectors &TexGenAttrib:: +get_light_vectors() const { + return _light_vectors; +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenAttrib::ModeDef::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE TexGenAttrib::ModeDef:: +ModeDef() : + _mode(M_off) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: TexGenAttrib::ModeDef::compare_to +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int TexGenAttrib::ModeDef:: +compare_to(const TexGenAttrib::ModeDef &other) const { + if (_mode != other._mode) { + return (int)_mode < (int)other._mode ? -1 : 1; + } + return _light.compare_to(other._light); +} + diff --git a/panda/src/pgraph/texGenAttrib.cxx b/panda/src/pgraph/texGenAttrib.cxx index fe41746373..bcae9fe429 100755 --- a/panda/src/pgraph/texGenAttrib.cxx +++ b/panda/src/pgraph/texGenAttrib.cxx @@ -61,8 +61,8 @@ make() { // indicated stage. //////////////////////////////////////////////////////////////////// CPT(RenderAttrib) TexGenAttrib:: -make(TextureStage *stage, TexGenAttrib::Mode mode) { - return DCAST(TexGenAttrib, make())->add_stage(stage, mode); +make(TextureStage *stage, TexGenAttrib::Mode mode, const NodePath &light) { + return DCAST(TexGenAttrib, make())->add_stage(stage, mode, light); } //////////////////////////////////////////////////////////////////// @@ -74,14 +74,44 @@ make(TextureStage *stage, TexGenAttrib::Mode mode) { // replaced. //////////////////////////////////////////////////////////////////// CPT(RenderAttrib) TexGenAttrib:: -add_stage(TextureStage *stage, TexGenAttrib::Mode mode) const { - TexGenAttrib *attrib = new TexGenAttrib(*this); - attrib->_stages[stage] = mode; - if (mode != M_off) { +add_stage(TextureStage *stage, TexGenAttrib::Mode mode, const NodePath &light) const { + CPT(RenderAttrib) removed = remove_stage(stage); + TexGenAttrib *attrib = new TexGenAttrib(*DCAST(TexGenAttrib, removed)); + + ModeDef &mode_def = attrib->_stages[stage]; + mode_def._mode = mode; + mode_def._light = light; + switch (mode) { + case M_point_sprite: attrib->_no_texcoords.insert(stage); - if (mode == M_point_sprite) { - attrib->_num_point_sprites++; + attrib->_point_geom_rendering |= Geom::GR_point_sprite; + attrib->_num_point_sprites++; + break; + + 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[stage] = light; + attrib->_geom_rendering |= Geom::GR_texcoord_light_vector; + attrib->_num_light_vectors++; + } } + break; + + case M_off: + break; + + default: + attrib->_no_texcoords.insert(stage); } return return_new(attrib); } @@ -100,12 +130,21 @@ remove_stage(TextureStage *stage) const { return this; } - Mode mode = (*si).second; + Mode mode = (*si).second._mode; TexGenAttrib *attrib = new TexGenAttrib(*this); attrib->_stages.erase(stage); attrib->_no_texcoords.erase(stage); if (mode == M_point_sprite) { attrib->_num_point_sprites--; + if (attrib->_num_point_sprites == 0) { + attrib->_point_geom_rendering &= ~Geom::GR_point_sprite; + } + } else if (mode == M_light_vector) { + attrib->_light_vectors.erase(stage); + attrib->_num_light_vectors--; + if (attrib->_num_light_vectors == 0) { + attrib->_geom_rendering &= ~Geom::GR_texcoord_light_vector; + } } return return_new(attrib); } @@ -145,11 +184,28 @@ TexGenAttrib::Mode TexGenAttrib:: get_mode(TextureStage *stage) const { Stages::const_iterator mi = _stages.find(stage); if (mi != _stages.end()) { - return (*mi).second; + return (*mi).second._mode; } return M_off; } +//////////////////////////////////////////////////////////////////// +// Function: TexGenAttrib::get_light +// Access: Published +// Description: Returns the Light associated with the named texture +// stage, or the empty NodePath if no light is +// associated with the indicated stage. This is only +// meaningful if the mode is M_light_vector. +//////////////////////////////////////////////////////////////////// +NodePath TexGenAttrib:: +get_light(TextureStage *stage) const { + Stages::const_iterator mi = _stages.find(stage); + if (mi != _stages.end()) { + return (*mi).second._light; + } + return NodePath(); +} + //////////////////////////////////////////////////////////////////// // Function: TexGenAttrib::issue // Access: Public, Virtual @@ -176,9 +232,9 @@ output(ostream &out) const { Stages::const_iterator mi; for (mi = _stages.begin(); mi != _stages.end(); ++mi) { TextureStage *stage = (*mi).first; - Mode mode = (*mi).second; + const ModeDef &mode_def = (*mi).second; out << " " << stage->get_name() << "("; - switch (mode) { + switch (mode_def._mode) { case M_off: out << "off"; break; @@ -214,6 +270,10 @@ output(ostream &out) const { case M_point_sprite: out << "point_sprite"; break; + + case M_light_vector: + out << "light_vector: " << mode_def._light; + break; } out << ")"; } @@ -253,8 +313,9 @@ compare_to_impl(const RenderAttrib *other) const { } else { // This stage is in both; compare the stages. - if ((*ai).second != (*bi).second) { - return (int)(*ai).second < (int)(*bi).second ? -1 : 1; + int compare = (*ai).second.compare_to((*bi).second); + if (compare != 0) { + return compare; } ++ai; ++bi; @@ -338,8 +399,29 @@ compose_impl(const RenderAttrib *other) const { // Now copy from _stages to _no_texcoords. Stages::const_iterator ri; for (ri = attrib->_stages.begin(); ri != attrib->_stages.end(); ++ri) { - if ((*ri).second != M_off) { - attrib->_no_texcoords.insert((*ri).first); + TextureStage *stage = (*ri).first; + const ModeDef &mode_def = (*ri).second; + Mode mode = mode_def._mode; + const NodePath &light = mode_def._light; + + switch (mode) { + case M_point_sprite: + attrib->_no_texcoords.insert(stage); + attrib->_point_geom_rendering |= Geom::GR_point_sprite; + attrib->_num_point_sprites++; + break; + + case M_light_vector: + attrib->_light_vectors[stage] = light; + attrib->_geom_rendering |= Geom::GR_texcoord_light_vector; + attrib->_num_light_vectors++; + break; + + case M_off: + break; + + default: + attrib->_no_texcoords.insert(stage); } } @@ -371,7 +453,7 @@ invert_compose_impl(const RenderAttrib *other) const { while (ai != _stages.end() && bi != ta->_stages.end()) { if ((*ai).first < (*bi).first) { // This stage is in a but not in b. Turn a off. - attrib->_stages.insert(attrib->_stages.end(), Stages::value_type((*ai).first, M_off)); + attrib->_stages.insert(attrib->_stages.end(), Stages::value_type((*ai).first, ModeDef())); ++ai; } else if ((*bi).first < (*ai).first) { @@ -389,7 +471,7 @@ invert_compose_impl(const RenderAttrib *other) const { while (ai != _stages.end()) { // This stage is in a but not in b. - attrib->_stages.insert(attrib->_stages.end(), Stages::value_type((*ai).first, M_off)); + attrib->_stages.insert(attrib->_stages.end(), Stages::value_type((*ai).first, ModeDef())); ++ai; } @@ -402,8 +484,29 @@ invert_compose_impl(const RenderAttrib *other) const { // Now copy from _stages to _no_texcoords. Stages::const_iterator ri; for (ri = attrib->_stages.begin(); ri != attrib->_stages.end(); ++ri) { - if ((*ri).second != M_off) { - attrib->_no_texcoords.insert((*ri).first); + TextureStage *stage = (*ri).first; + const ModeDef &mode_def = (*ri).second; + Mode mode = mode_def._mode; + const NodePath &light = mode_def._light; + + switch (mode) { + case M_point_sprite: + attrib->_no_texcoords.insert(stage); + attrib->_point_geom_rendering |= Geom::GR_point_sprite; + attrib->_num_point_sprites++; + break; + + case M_light_vector: + attrib->_light_vectors[stage] = light; + attrib->_geom_rendering |= Geom::GR_texcoord_light_vector; + attrib->_num_light_vectors++; + break; + + case M_off: + break; + + default: + attrib->_no_texcoords.insert(stage); } } @@ -452,7 +555,7 @@ write_datagram(BamWriter *manager, Datagram &dg) { Stages::const_iterator si; for (si = _stages.begin(); si != _stages.end(); ++si) { TextureStage *stage = (*si).first; - Mode mode = (*si).second; + Mode mode = (*si).second._mode; manager->write_pointer(dg, stage); dg.add_uint8((unsigned int)mode); @@ -475,7 +578,7 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) { Mode mode = (*mi); TextureStage *stage = DCAST(TextureStage, p_list[pi++]); - _stages[stage] = mode; + _stages[stage]._mode = mode; if (mode != M_off) { _no_texcoords.insert(stage); diff --git a/panda/src/pgraph/texGenAttrib.h b/panda/src/pgraph/texGenAttrib.h index 7156300d76..61b52d3315 100755 --- a/panda/src/pgraph/texGenAttrib.h +++ b/panda/src/pgraph/texGenAttrib.h @@ -26,6 +26,7 @@ #include "textureStage.h" #include "texture.h" #include "pointerTo.h" +#include "nodePath.h" //////////////////////////////////////////////////////////////////// // Class : TexGenAttrib @@ -38,59 +39,11 @@ //////////////////////////////////////////////////////////////////// class EXPCL_PANDA TexGenAttrib : public RenderAttrib { PUBLISHED: - enum Mode { - M_off, - - // In the types below, "eye" means the coordinate space of the - // observing camera, "object" means the local coordinate space of - // the object, and "world" means world coordinates, e.g. the - // coordinate space of the root of the graph. - - // Sphere maps are classic static reflection maps. They are - // supported on just about any hardware, and require a precomputed - // 180-degree fisheye image. Sphere maps only make sense in eye - // coordinate space. - M_eye_sphere_map, - - // Cube maps are a modern improvement on the sphere map; they - // don't suffer from any polar singularities, but they require six - // texture images. They can also be generated dynamically for - // real-time reflections (see GraphicsOutput::make_cube_map()). - // Typically, a statically-generated cube map will be in eye - // space, while a dynamically-generated map will be in world space - // or object space (depending on where the camera rig that - // generates the map is parented). - - // Cube mapping is not supported on all hardware. - M_world_cube_map, - M_eye_cube_map, - - // Normal maps are most useful for applying diffuse lighting - // effects via a pregenerated cube map. - M_world_normal, - M_eye_normal, - - // Position maps convert XYZ coordinates directly to texture - // coordinates. This is particularly useful for implementing - // projective texturing (see NodePath::project_texture()). - M_world_position, - M_object_position, - M_eye_position, - - // With M_point_sprite, texture coordinates will be generated for - // large points in the range (0,0) - (1,1) from upper-left to - // lower-right across the point's face. Without this, each point - // will have just a single uniform texture coordinate value across - // its face. - - // Unfortunately, the generated texture coordinates are inverted - // (upside-down) from Panda's usual convention, but this is what - // the graphics card manufacturers decided to use. You could use - // a texture matrix to re-invert the texture, but that will - // probably force software rendering. You'll have to paint your - // textures upside-down if you want true hardware sprites. - M_point_sprite, - }; + // We inherit the definition of our Mode enumerated type from + // RenderAttrib. Normally, Mode would be defined here, but we + // define it in the base class instead as a hack to avoid a problem + // with circular includes. + typedef RenderAttrib::TexGenMode Mode; protected: INLINE TexGenAttrib(); @@ -101,20 +54,24 @@ public: PUBLISHED: static CPT(RenderAttrib) make(); - static CPT(RenderAttrib) make(TextureStage *stage, Mode mode); + static CPT(RenderAttrib) make(TextureStage *stage, Mode mode, const NodePath &light = NodePath()); - CPT(RenderAttrib) add_stage(TextureStage *stage, Mode mode) const; + CPT(RenderAttrib) add_stage(TextureStage *stage, Mode mode, const NodePath &light = NodePath()) const; CPT(RenderAttrib) remove_stage(TextureStage *stage) const; bool is_empty() const; bool has_stage(TextureStage *stage) const; Mode get_mode(TextureStage *stage) const; + NodePath get_light(TextureStage *stage) const; INLINE int get_geom_rendering(int geom_rendering) const; public: INLINE const Geom::NoTexCoordStages &get_no_texcoords() const; + typedef pmap LightVectors; + INLINE const LightVectors &get_light_vectors() const; + virtual void issue(GraphicsStateGuardianBase *gsg) const; virtual void output(ostream &out) const; @@ -125,21 +82,40 @@ protected: virtual RenderAttrib *make_default_impl() const; private: - typedef pmap Stages; + class ModeDef { + public: + INLINE ModeDef(); + INLINE int compare_to(const ModeDef &other) const; + Mode _mode; + NodePath _light; + }; + typedef pmap Stages; Stages _stages; // This is a set of TextureStage pointers for which texture // coordinates will not be needed from the Geom. It's redundant; // it's almost the same set that is listed in _stages, above. It's - // just here as an optimization to pass to - // Geom::setup_multitexcoord_iterator() during rendering. + // just here as an optimization during rendering. Geom::NoTexCoordStages _no_texcoords; + // This is another optimization during rendering; it lists the + // texture stages (if any) that use M_light_vector, and their + // associated lights. + LightVectors _light_vectors; + // This element is only used during reading from a bam file. It has // no meaningful value any other time. pvector _read_modes; int _num_point_sprites; + int _num_light_vectors; + + // _point_geom_rendering is the GeomRendering bits that are added by + // the TexGenAttrib if there are any points in the Geom. + // _geom_rendering is the GeomRendering bits that are added + // regardless of the kind of Geom it is. + int _point_geom_rendering; + int _geom_rendering; static CPT(RenderAttrib) _empty_attrib;