diff --git a/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx b/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx index 5a9c494b60..215c5a1be4 100644 --- a/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx +++ b/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx @@ -2593,7 +2593,10 @@ bool DXGraphicsStateGuardian8:: begin_draw_primitives(const qpGeomVertexData *vertex_data) { DO_PSTATS_STUFF(_draw_primitive_pcollector.start()); - GraphicsStateGuardian::begin_draw_primitives(vertex_data); + if (!GraphicsStateGuardian::begin_draw_primitives(vertex_data)) { + return false; + } + nassertr(_vertex_data != (qpGeomVertexData *)NULL, false); const qpGeomVertexFormat *format = _vertex_data->get_format(); diff --git a/panda/src/egg/eggBinMaker.cxx b/panda/src/egg/eggBinMaker.cxx index 6eed7f73af..0be90afa5f 100644 --- a/panda/src/egg/eggBinMaker.cxx +++ b/panda/src/egg/eggBinMaker.cxx @@ -92,6 +92,17 @@ make_bins(EggGroupNode *root_group) { return num_bins; } +//////////////////////////////////////////////////////////////////// +// Function: EggBinMaker::prepare_node +// Access: Public, Virtual +// Description: May be overridden in derived classes to perform some +// setup work as each node is encountered. This will be +// called once for each node in the egg hierarchy. +//////////////////////////////////////////////////////////////////// +void EggBinMaker:: +prepare_node(EggNode *) { +} + //////////////////////////////////////////////////////////////////// // Function: EggBinMaker::sorts_less // Access: Public, Virtual @@ -153,6 +164,8 @@ collect_nodes(EggGroupNode *group) { EggNode *node = (*i); ++next; + prepare_node(node); + if (get_bin_number(node) != 0) { // Ok, here's a node to be binned. Add it to the appropriate // bin. diff --git a/panda/src/egg/eggBinMaker.h b/panda/src/egg/eggBinMaker.h index 4e0f9093a3..3e81b47f88 100644 --- a/panda/src/egg/eggBinMaker.h +++ b/panda/src/egg/eggBinMaker.h @@ -96,6 +96,14 @@ // // You may also redefine any or all of the following functions: // +// virtual void prepare_node(EggNode *node); +// +// This method is called, once, on each node in the egg hierarchy +// as it is visited the first time. It allows the subclass a +// chance to analyze the node or do any other initial processing. +// This is a fine opportunity to tag an EggUserData onto the node, +// for instance. +// // virtual bool sorts_less(int bin_number, const EggNode *a, const EggNode *b); // // Sometimes a simple bin number alone is not enough. For @@ -254,6 +262,9 @@ PUBLISHED: int make_bins(EggGroupNode *root_group); + virtual void + prepare_node(EggNode *node); + virtual int get_bin_number(const EggNode *node)=0; diff --git a/panda/src/egg/eggGroup.cxx b/panda/src/egg/eggGroup.cxx index 746cd5f514..c27e901246 100644 --- a/panda/src/egg/eggGroup.cxx +++ b/panda/src/egg/eggGroup.cxx @@ -61,9 +61,14 @@ operator = (const EggGroup ©) { EggTransform3d::operator = (copy); _flags = copy._flags; _flags2 = copy._flags2; + _collide_mask = copy._collide_mask; + _from_collide_mask = copy._from_collide_mask; + _into_collide_mask = copy._into_collide_mask; + _billboard_center = copy._billboard_center; _object_types = copy._object_types; _collision_name = copy._collision_name; _fps = copy._fps; + _lod = copy._lod; _tag_data = copy._tag_data; diff --git a/panda/src/egg/eggObject.I b/panda/src/egg/eggObject.I index 30f7f7f75d..c8fc72e728 100644 --- a/panda/src/egg/eggObject.I +++ b/panda/src/egg/eggObject.I @@ -35,7 +35,8 @@ EggObject() { INLINE EggObject:: EggObject(const EggObject ©) : TypedReferenceCount(copy), - _user_data(copy._user_data) + _user_data(copy._user_data), + _default_user_data(copy._default_user_data) { } @@ -49,6 +50,7 @@ INLINE EggObject &EggObject:: operator = (const EggObject ©) { TypedReferenceCount::operator = (copy); _user_data = copy._user_data; + _default_user_data = copy._default_user_data; return *this; } @@ -61,54 +63,102 @@ operator = (const EggObject ©) { // hold its reference count and return the pointer on // request. // +// The EggObject maintains multiple different +// EggUserData pointers, one for each unique type (as +// reported by get_type()). If you know that only one +// type of EggUserData object will be added in your +// application, you may use the query functions that +// accept no parameters, but it is recommended that in +// general you pass in the type of your particular user +// data, to allow multiple applications to coexist in +// the same egg data. +// // This pointer is also copied by the copy assignment // operator and copy constructor. //////////////////////////////////////////////////////////////////// INLINE void EggObject:: set_user_data(EggUserData *user_data) { - _user_data = user_data; + _user_data[user_data->get_type()] = user_data; + _default_user_data = user_data; } //////////////////////////////////////////////////////////////////// // Function: EggObject::get_user_data // Access: Public -// Description: Returns the user data pointer previously stored on +// Description: Returns the user data pointer most recently stored on // this object, or NULL if nothing was previously // stored. //////////////////////////////////////////////////////////////////// INLINE EggUserData *EggObject:: get_user_data() const { - return _user_data; + return _default_user_data; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggObject::get_user_data +// Access: Public +// Description: Returns the user data pointer of the indicated type, +// if it exists, or NULL if it does not. +//////////////////////////////////////////////////////////////////// +INLINE EggUserData *EggObject:: +get_user_data(TypeHandle type) const { + UserData::const_iterator ui; + ui = _user_data.find(type); + if (ui != _user_data.end()) { + return (*ui).second; + } + return NULL; } //////////////////////////////////////////////////////////////////// // Function: EggObject::has_user_data // Access: Public -// Description: Returns true if the user data pointer has been set, -// false otherwise. +// Description: Returns true if a generic user data pointer has +// recently been set and not yet cleared, false +// otherwise. //////////////////////////////////////////////////////////////////// INLINE bool EggObject:: has_user_data() const { - return !_user_data.is_null(); + return !_default_user_data.is_null(); } //////////////////////////////////////////////////////////////////// // Function: EggObject::has_user_data // Access: Public -// Description: Returns true if the user data pointer has been set -// and is of the indicated type, false otherwise. +// Description: Returns true if the user data pointer of the +// indicated type has been set, false otherwise. //////////////////////////////////////////////////////////////////// INLINE bool EggObject:: has_user_data(TypeHandle type) const { - return !_user_data.is_null() && _user_data->is_of_type(type); + UserData::const_iterator ui; + ui = _user_data.find(type); + return (ui != _user_data.end()); } //////////////////////////////////////////////////////////////////// // Function: EggObject::clear_user_data // Access: Public -// Description: Resets the user data pointer to NULL. +// Description: Removes *all* user data pointers from the node. //////////////////////////////////////////////////////////////////// INLINE void EggObject:: clear_user_data() { _user_data.clear(); + _default_user_data.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggObject::clear_user_data +// Access: Public +// Description: Removes the user data pointer of the indicated type. +//////////////////////////////////////////////////////////////////// +INLINE void EggObject:: +clear_user_data(TypeHandle type) { + UserData::iterator ui; + ui = _user_data.find(type); + if (ui != _user_data.end()) { + if ((*ui).second == _default_user_data) { + _default_user_data.clear(); + } + _user_data.erase(ui); + } } diff --git a/panda/src/egg/eggObject.h b/panda/src/egg/eggObject.h index d482bed227..9dc7a16262 100644 --- a/panda/src/egg/eggObject.h +++ b/panda/src/egg/eggObject.h @@ -23,6 +23,7 @@ #include "eggUserData.h" #include "typedReferenceCount.h" #include "pointerTo.h" +#include "pmap.h" //////////////////////////////////////////////////////////////////// // Class : EggObject @@ -39,12 +40,16 @@ PUBLISHED: INLINE void set_user_data(EggUserData *user_data); INLINE EggUserData *get_user_data() const; + INLINE EggUserData *get_user_data(TypeHandle type) const; INLINE bool has_user_data() const; INLINE bool has_user_data(TypeHandle type) const; INLINE void clear_user_data(); + INLINE void clear_user_data(TypeHandle type); private: - PT(EggUserData) _user_data; + typedef pmap UserData; + UserData _user_data; + PT(EggUserData) _default_user_data; public: static TypeHandle get_class_type() { diff --git a/panda/src/egg/eggVertexPool.cxx b/panda/src/egg/eggVertexPool.cxx index dc20f418d7..994c435a9a 100644 --- a/panda/src/egg/eggVertexPool.cxx +++ b/panda/src/egg/eggVertexPool.cxx @@ -178,6 +178,108 @@ get_highest_index() const { return _highest_index; } +//////////////////////////////////////////////////////////////////// +// Function: EggVertexPool::get_num_dimensions +// Access: Public +// Description: Returns the maximum number of dimensions used by any +// vertex in the pool. +//////////////////////////////////////////////////////////////////// +int EggVertexPool:: +get_num_dimensions() const { + int num_dimensions = 0; + + IndexVertices::const_iterator ivi; + for (ivi = _index_vertices.begin(); ivi != _index_vertices.end(); ++ivi) { + EggVertex *vertex = (*ivi).second; + num_dimensions = max(num_dimensions, vertex->get_num_dimensions()); + } + + return num_dimensions; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggVertexPool::has_normals +// Access: Public +// Description: Returns true if any vertex in the pool has a normal +// defined, false if none of them do. +//////////////////////////////////////////////////////////////////// +bool EggVertexPool:: +has_normals() const { + IndexVertices::const_iterator ivi; + for (ivi = _index_vertices.begin(); ivi != _index_vertices.end(); ++ivi) { + EggVertex *vertex = (*ivi).second; + if (vertex->has_normal()) { + return true; + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggVertexPool::has_colors +// Access: Public +// Description: Returns true if any vertex in the pool has a color +// defined, false if none of them do. +//////////////////////////////////////////////////////////////////// +bool EggVertexPool:: +has_colors() const { + IndexVertices::const_iterator ivi; + for (ivi = _index_vertices.begin(); ivi != _index_vertices.end(); ++ivi) { + EggVertex *vertex = (*ivi).second; + if (vertex->has_color()) { + return true; + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggVertexPool::has_uvs +// Access: Public +// Description: Returns true if any vertex in the pool has a uv +// defined, false if none of them do. +//////////////////////////////////////////////////////////////////// +bool EggVertexPool:: +has_uvs() const { + IndexVertices::const_iterator ivi; + for (ivi = _index_vertices.begin(); ivi != _index_vertices.end(); ++ivi) { + EggVertex *vertex = (*ivi).second; + if (vertex->has_uv()) { + return true; + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggVertexPool::get_uv_names +// Access: Public +// Description: Returns the list of UV names that are defined by any +// vertices in the pool. It is the user's +// responsibility to clear the vector before calling +// this method. +//////////////////////////////////////////////////////////////////// +void EggVertexPool:: +get_uv_names(vector_string &uv_names) const { + pset names; + IndexVertices::const_iterator ivi; + for (ivi = _index_vertices.begin(); ivi != _index_vertices.end(); ++ivi) { + EggVertex *vertex = (*ivi).second; + EggVertex::const_uv_iterator uvi; + for (uvi = vertex->uv_begin(); uvi != vertex->uv_end(); ++uvi) { + names.insert((*uvi)->get_name()); + } + } + + pset::const_iterator si; + for (si = names.begin(); si != names.end(); ++si) { + uv_names.push_back(*si); + } +} + //////////////////////////////////////////////////////////////////// // Function: EggVertexPool::begin() // Access: Public diff --git a/panda/src/egg/eggVertexPool.h b/panda/src/egg/eggVertexPool.h index 02168b5092..4b9ba04cdc 100644 --- a/panda/src/egg/eggVertexPool.h +++ b/panda/src/egg/eggVertexPool.h @@ -92,6 +92,12 @@ PUBLISHED: // Returns 0 if the pool is empty. int get_highest_index() const; + int get_num_dimensions() const; + bool has_normals() const; + bool has_colors() const; + bool has_uvs() const; + void get_uv_names(vector_string &uv_names) const; + public: // Can be used to traverse all the vertices in index number order. iterator begin() const; diff --git a/panda/src/egg2pg/Sources.pp b/panda/src/egg2pg/Sources.pp index a1a7071184..ea773b8928 100644 --- a/panda/src/egg2pg/Sources.pp +++ b/panda/src/egg2pg/Sources.pp @@ -18,7 +18,8 @@ config_egg2pg.h \ deferredNodeProperty.h \ eggBinner.h \ - eggLoader.h \ + eggLoader.h eggLoader.I \ + eggRenderState.h eggRenderState.I \ egg_parametrics.h \ load_egg_file.h \ loaderFileTypeEgg.h @@ -31,6 +32,7 @@ deferredNodeProperty.cxx \ eggBinner.cxx \ eggLoader.cxx \ + eggRenderState.cxx \ egg_parametrics.cxx \ load_egg_file.cxx \ loaderFileTypeEgg.cxx diff --git a/panda/src/egg2pg/config_egg2pg.cxx b/panda/src/egg2pg/config_egg2pg.cxx index f257c406aa..674bfe9a67 100644 --- a/panda/src/egg2pg/config_egg2pg.cxx +++ b/panda/src/egg2pg/config_egg2pg.cxx @@ -23,6 +23,7 @@ #include "loaderFileTypeRegistry.h" #include "configVariableManager.h" #include "configVariableCore.h" +#include "eggRenderState.h" ConfigureDef(config_egg2pg); NotifyCategoryDef(egg2pg, ""); @@ -153,6 +154,7 @@ init_libegg2pg() { "Defines egg syntax for the named object type.", ConfigVariableCore::F_dynamic); + EggRenderState::init_type(); LoaderFileTypeEgg::init_type(); LoaderFileTypeRegistry *reg = LoaderFileTypeRegistry::get_global_ptr(); diff --git a/panda/src/egg2pg/egg2pg_composite1.cxx b/panda/src/egg2pg/egg2pg_composite1.cxx index 52a036a68b..467d760219 100644 --- a/panda/src/egg2pg/egg2pg_composite1.cxx +++ b/panda/src/egg2pg/egg2pg_composite1.cxx @@ -3,3 +3,4 @@ #include "computedVerticesMaker.cxx" #include "config_egg2pg.cxx" #include "egg_parametrics.cxx" +#include "eggRenderState.cxx" diff --git a/panda/src/egg2pg/eggBinner.cxx b/panda/src/egg2pg/eggBinner.cxx index c521534ada..c3b86b2458 100644 --- a/panda/src/egg2pg/eggBinner.cxx +++ b/panda/src/egg2pg/eggBinner.cxx @@ -17,11 +17,40 @@ //////////////////////////////////////////////////////////////////// #include "eggBinner.h" - +#include "eggRenderState.h" +#include "eggPrimitive.h" #include "eggSwitchCondition.h" #include "eggGroup.h" #include "dcast.h" +//////////////////////////////////////////////////////////////////// +// Function: EggBinner::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggBinner:: +EggBinner(EggLoader &loader) : + _loader(loader) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: EggBinner::prepare_node +// Access: Public, Virtual +// Description: May be overridden in derived classes to perform some +// setup work as each node is encountered. This will be +// called once for each node in the egg hierarchy. +//////////////////////////////////////////////////////////////////// +void EggBinner:: +prepare_node(EggNode *node) { + if (node->is_of_type(EggPrimitive::get_class_type())) { + EggPrimitive *egg_prim = DCAST(EggPrimitive, node); + PT(EggRenderState) render_state = new EggRenderState(_loader); + render_state->fill_state(egg_prim); + egg_prim->set_user_data(render_state); + } +} + //////////////////////////////////////////////////////////////////// // Function: EggBinner::get_bin_number // Access: Public, Virtual @@ -29,7 +58,10 @@ //////////////////////////////////////////////////////////////////// int EggBinner:: get_bin_number(const EggNode *node) { - if (node->is_of_type(EggGroup::get_class_type())) { + if (node->is_of_type(EggPrimitive::get_class_type())) { + return (int)BN_polyset; + + } else if (node->is_of_type(EggGroup::get_class_type())) { const EggGroup *group = DCAST(EggGroup, node); if (group->has_lod()) { return (int)BN_lod; @@ -47,36 +79,47 @@ get_bin_number(const EggNode *node) { //////////////////////////////////////////////////////////////////// bool EggBinner:: sorts_less(int bin_number, const EggNode *a, const EggNode *b) { - assert((BinNumber)bin_number == BN_lod); + switch (bin_number) { + case BN_polyset: + { + const EggPrimitive *pa, *pb; + DCAST_INTO_R(pa, a, false); + DCAST_INTO_R(pb, b, false); - const EggGroup *ga = DCAST(EggGroup, a); - const EggGroup *gb = DCAST(EggGroup, b); + // Different vertex pools have to be binned separately. + if (pa->get_pool() != pb->get_pool()) { + return pa->get_pool() < pb->get_pool(); + } + + // Otherwise, different render states are binned separately. + const EggRenderState *rsa, *rsb; + DCAST_INTO_R(rsa, a->get_user_data(EggRenderState::get_class_type()), false); + DCAST_INTO_R(rsb, b->get_user_data(EggRenderState::get_class_type()), false); + return (*rsa) < (*rsb); + } - const EggSwitchCondition &swa = ga->get_lod(); - const EggSwitchCondition &swb = gb->get_lod(); - - // For now, this is the only kind of switch condition there is. - const EggSwitchConditionDistance &swda = - *DCAST(EggSwitchConditionDistance, &swa); - const EggSwitchConditionDistance &swdb = - *DCAST(EggSwitchConditionDistance, &swb); - - // Group LOD nodes in order by switching center. - return (swda._center.compare_to(swdb._center) < 0); -} - -//////////////////////////////////////////////////////////////////// -// Function: EggBinner::collapse_group -// Access: Public, Virtual -// Description: -//////////////////////////////////////////////////////////////////// -bool EggBinner:: -collapse_group(const EggGroup *group, int) { - if (group->get_dart_type() != EggGroup::DT_none) { - // A group with the flag set means to create a character. - // We can't turn the top character node into an LOD. - return false; + case BN_lod: + { + const EggGroup *ga = DCAST(EggGroup, a); + const EggGroup *gb = DCAST(EggGroup, b); + + const EggSwitchCondition &swa = ga->get_lod(); + const EggSwitchCondition &swb = gb->get_lod(); + + // For now, this is the only kind of switch condition there is. + const EggSwitchConditionDistance &swda = + *DCAST(EggSwitchConditionDistance, &swa); + const EggSwitchConditionDistance &swdb = + *DCAST(EggSwitchConditionDistance, &swb); + + // Group LOD nodes in order by switching center. + return (swda._center.compare_to(swdb._center) < 0); + } + + case BN_none: + break; } - return true; + // Shouldn't get here. + return false; } diff --git a/panda/src/egg2pg/eggBinner.h b/panda/src/egg2pg/eggBinner.h index db5040c9cc..6192661ac9 100644 --- a/panda/src/egg2pg/eggBinner.h +++ b/panda/src/egg2pg/eggBinner.h @@ -23,33 +23,40 @@ #include "eggBinMaker.h" +class EggLoader; + /////////////////////////////////////////////////////////////////// // Class : EggBinner // Description : A special binner used only within this package to // pre-process the egg tree for the loader and group // things together as appropriate. // -// Presently, it only groups related LOD children +// It is used to collect similar polygons together for a +// Geom, as well as to group related LOD children // together under a single LOD node. //////////////////////////////////////////////////////////////////// class EggBinner : public EggBinMaker { public: - // The BinNumber serves to identify why a particular EggBin was // created. enum BinNumber { BN_none = 0, + BN_polyset, BN_lod, }; + EggBinner(EggLoader &loader); + + virtual void + prepare_node(EggNode *node); + virtual int get_bin_number(const EggNode *node); virtual bool sorts_less(int bin_number, const EggNode *a, const EggNode *b); - virtual bool - collapse_group(const EggGroup *group, int bin_number); + EggLoader &_loader; }; diff --git a/panda/src/egg2pg/eggLoader.I b/panda/src/egg2pg/eggLoader.I new file mode 100644 index 0000000000..5e5284cab4 --- /dev/null +++ b/panda/src/egg2pg/eggLoader.I @@ -0,0 +1,31 @@ +// Filename: eggLoader.I +// Created by: drose (13Mar05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::VertexPoolTransform::operator < +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool EggLoader::VertexPoolTransform:: +operator < (const EggLoader::VertexPoolTransform &other) const { + if (_vertex_pool != other._vertex_pool) { + return _vertex_pool < other._vertex_pool; + } + return _transform.compare_to(other._transform, 0.001) < 0; +} diff --git a/panda/src/egg2pg/eggLoader.cxx b/panda/src/egg2pg/eggLoader.cxx index 0420e9383f..daf223f06d 100644 --- a/panda/src/egg2pg/eggLoader.cxx +++ b/panda/src/egg2pg/eggLoader.cxx @@ -1,4 +1,4 @@ -// Filename: EggLoader.cxx +// Filename: eggLoader.cxx // Created by: drose (26Feb02) // //////////////////////////////////////////////////////////////////// @@ -19,26 +19,25 @@ #include "pandabase.h" #include "eggLoader.h" +#include "eggRenderState.h" #include "egg_parametrics.h" #include "config_egg2pg.h" #include "nodePath.h" #include "renderState.h" #include "transformState.h" -#include "textureAttrib.h" -#include "textureApplyAttrib.h" #include "texturePool.h" #include "billboardEffect.h" -#include "cullFaceAttrib.h" -#include "cullBinAttrib.h" -#include "transparencyAttrib.h" #include "decalEffect.h" -#include "depthTestAttrib.h" -#include "depthWriteAttrib.h" -#include "materialAttrib.h" -#include "texMatrixAttrib.h" #include "colorAttrib.h" +#include "textureAttrib.h" #include "materialPool.h" #include "geomNode.h" +#include "qpgeomVertexFormat.h" +#include "qpgeomVertexArrayFormat.h" +#include "qpgeomVertexData.h" +#include "qpgeomVertexIterator.h" +#include "qpgeom.h" +#include "qpgeomTriangles.h" #include "sequenceNode.h" #include "switchNode.h" #include "portalNode.h" @@ -150,13 +149,13 @@ void EggLoader:: build_graph() { _deferred_nodes.clear(); - // First, bin up the LOD nodes. - EggBinner binner; - binner.make_bins(&_data); - - // Then load up all of the textures. + // First, load up all of the textures. load_textures(); + // Then bin up the polysets and LOD nodes. + EggBinner binner(*this); + binner.make_bins(&_data); + // Now build up the scene graph. _root = new ModelRoot(_data.get_egg_filename().get_basename()); make_node(&_data, _root); @@ -1276,61 +1275,6 @@ make_texture_stage(const EggTexture *egg_tex) { return stage; } -//////////////////////////////////////////////////////////////////// -// Function: EggLoader::get_material_attrib -// Access: Private -// Description: Returns a RenderAttrib suitable for enabling the -// material indicated by the given EggMaterial, and with -// the indicated backface flag. -//////////////////////////////////////////////////////////////////// -CPT(RenderAttrib) EggLoader:: -get_material_attrib(const EggMaterial *egg_mat, bool bface) { - Materials &materials = bface ? _materials_bface : _materials; - - // First, check whether we've seen this material before. - Materials::const_iterator mi; - mi = materials.find(egg_mat); - if (mi != materials.end()) { - return (*mi).second; - } - - // Ok, this is the first time we've seen this particular - // EggMaterial. Create a new Material that matches it. - PT(Material) mat = new Material; - if (egg_mat->has_diff()) { - mat->set_diffuse(egg_mat->get_diff()); - // By default, ambient is the same as diffuse, if diffuse is - // specified but ambient is not. - mat->set_ambient(egg_mat->get_diff()); - } - if (egg_mat->has_amb()) { - mat->set_ambient(egg_mat->get_amb()); - } - if (egg_mat->has_emit()) { - mat->set_emission(egg_mat->get_emit()); - } - if (egg_mat->has_spec()) { - mat->set_specular(egg_mat->get_spec()); - } - if (egg_mat->has_shininess()) { - mat->set_shininess(egg_mat->get_shininess()); - } - if (egg_mat->has_local()) { - mat->set_local(egg_mat->get_local()); - } - - mat->set_twoside(bface); - - // Now get a global Material pointer, shared with other models. - const Material *shared_mat = MaterialPool::get_material(mat); - - // And create a MaterialAttrib for this Material. - CPT(RenderAttrib) mt = MaterialAttrib::make(shared_mat); - materials.insert(Materials::value_type(egg_mat, mt)); - - return mt; -} - //////////////////////////////////////////////////////////////////// // Function: EggLoader::setup_bucket @@ -1363,315 +1307,12 @@ setup_bucket(BuilderBucket &bucket, EggLoader::BakeInUVs &bake_in_uvs, bucket.set_name(egg_prim->get_name()); } - // Assign the appropriate properties to the bucket. + EggRenderState render_state(*this); + render_state.fill_state(egg_prim); - // The various EggRenderMode properties can be defined directly at - // the primitive, at a group above the primitive, or an a texture - // applied to the primitive. The EggNode::determine_*() functions - // can find the right pointer to the level at which this is actually - // defined for a given primitive. - EggRenderMode::AlphaMode am = EggRenderMode::AM_unspecified; - EggRenderMode::DepthWriteMode dwm = EggRenderMode::DWM_unspecified; - EggRenderMode::DepthTestMode dtm = EggRenderMode::DTM_unspecified; - EggRenderMode::VisibilityMode vm = EggRenderMode::VM_unspecified; - bool implicit_alpha = false; - bool has_draw_order = false; - int draw_order = 0; - bool has_bin = false; - string bin; - - EggRenderMode *render_mode; - render_mode = egg_prim->determine_alpha_mode(); - if (render_mode != (EggRenderMode *)NULL) { - am = render_mode->get_alpha_mode(); - } - render_mode = egg_prim->determine_depth_write_mode(); - if (render_mode != (EggRenderMode *)NULL) { - dwm = render_mode->get_depth_write_mode(); - } - render_mode = egg_prim->determine_depth_test_mode(); - if (render_mode != (EggRenderMode *)NULL) { - dtm = render_mode->get_depth_test_mode(); - } - render_mode = egg_prim->determine_visibility_mode(); - if (render_mode != (EggRenderMode *)NULL) { - vm = render_mode->get_visibility_mode(); - } - render_mode = egg_prim->determine_draw_order(); - if (render_mode != (EggRenderMode *)NULL) { - has_draw_order = true; - draw_order = render_mode->get_draw_order(); - } - render_mode = egg_prim->determine_bin(); - if (render_mode != (EggRenderMode *)NULL) { - has_bin = true; - bin = render_mode->get_bin(); - } - - bucket.add_attrib(TextureAttrib::make_off()); - int num_textures = egg_prim->get_num_textures(); - CPT(RenderAttrib) texture_attrib = NULL; - CPT(RenderAttrib) tex_gen_attrib = NULL; - CPT(RenderAttrib) tex_mat_attrib = NULL; - TexMats tex_mats; - - for (int i = 0; i < num_textures; i++) { - PT_EggTexture egg_tex = egg_prim->get_texture(i); - - const TextureDef &def = _textures[egg_tex]; - if (def._texture != (const RenderAttrib *)NULL) { - if (texture_attrib == (RenderAttrib *)NULL) { - texture_attrib = def._texture; - } else { - texture_attrib = texture_attrib->compose(def._texture); - } - - // If neither the primitive nor the texture specified an alpha - // mode, assume it should be alpha'ed if the texture has an - // alpha channel (unless the texture environment type is one - // that doesn't apply its alpha to the result). - if (am == EggRenderMode::AM_unspecified) { - const TextureAttrib *tex_attrib = DCAST(TextureAttrib, def._texture); - Texture *tex = tex_attrib->get_texture(); - nassertv(tex != (Texture *)NULL); - int num_components = tex->get_num_components(); - if (egg_tex->has_alpha_channel(num_components)) { - switch (egg_tex->get_env_type()) { - case EggTexture::ET_decal: - case EggTexture::ET_add: - break; - - default: - implicit_alpha = true; - } - } - } - - // Check for a texgen attrib. - bool has_tex_gen = false; - if (egg_tex->get_tex_gen() != EggTexture::TG_unspecified) { - has_tex_gen = true; - if (tex_gen_attrib == (const RenderAttrib *)NULL) { - tex_gen_attrib = TexGenAttrib::make(); - } - tex_gen_attrib = DCAST(TexGenAttrib, tex_gen_attrib)-> - add_stage(def._stage, get_tex_gen(egg_tex)); - } - - // Record the texture's associated texture matrix, so we can see - // if we can safely bake it into the UV's. (We need to get the - // complete list of textures that share this same set of UV's - // per each unique texture matrix. Whew!) - CPT(InternalName) uv_name; - if (egg_tex->has_uv_name() && egg_tex->get_uv_name() != string("default")) { - uv_name = InternalName::get_texcoord_name(egg_tex->get_uv_name()); - } else { - uv_name = InternalName::get_texcoord(); - } - - if (has_tex_gen) { - // If the texture has a texgen mode, we will always apply its - // texture transform, never bake it in. In fact, we don't - // even care about its UV's in this case, since we won't be - // using them. - tex_mat_attrib = apply_tex_mat(tex_mat_attrib, def._stage, egg_tex); - - } else { - // Otherwise, we need to record that there is at least one - // texture on this particular UV name and with this particular - // texture matrix. If there are no other textures, or if all - // of the other textures use the same texture matrix, then - // tex_mats[uv_name].size() will remain 1 (which tells us we - // can bake in the texture matrix to the UV's). On the other - // hand, if there is another texture on the same uv name but - // with a different transform, it will increase - // tex_mats[uv_name].size() to at least 2, indicating we can't - // bake in the texture matrix. - tex_mats[uv_name][egg_tex->get_transform()].push_back(&def); - } - } - } - - // These parametric primitive types can't have their UV's baked in, - // so if we have one of these we always need to apply the texture - // matrix as a separate attribute, regardless of how many textures - // share the particular UV set. - bool needs_tex_mat = (egg_prim->is_of_type(EggCurve::get_class_type()) || - egg_prim->is_of_type(EggSurface::get_class_type())); - - // Now that we've visited all of the textures in the above loop, we - // can go back and see how many of them share the same UV name and - // texture matrix. - TexMats::const_iterator tmi; - for (tmi = tex_mats.begin(); tmi != tex_mats.end(); ++tmi) { - const InternalName *uv_name = (*tmi).first; - const TexMatTransforms &tmt = (*tmi).second; - - if (tmt.size() == 1 && !needs_tex_mat) { - // Only one unique transform sharing this set of UV's. We can - // bake in the transform! - const TexMatTextures &tmtex = (*tmt.begin()).second; - - // The first EggTexture on the list is sufficient, since we know - // they all have the same transform. - nassertv(!tmtex.empty()); - TexMatTextures::const_iterator tmtexi = tmtex.begin(); - const EggTexture *egg_tex = (*tmtexi)->_egg_tex; - if (egg_tex->has_transform()) { - // If there's no transform, it's an identity matrix; don't - // bother recording it. Of course, it would do no harm to - // record it if we felt like it. - bake_in_uvs[uv_name] = egg_tex; - } - - } else { - // Multiple transforms on this UV set, or a geometry type that - // doesn't support baking in UV's. We have to apply the - // texture matrix to each stage. - TexMatTransforms::const_iterator tmti; - for (tmti = tmt.begin(); tmti != tmt.end(); ++tmti) { - const TexMatTextures &tmtex = (*tmti).second; - TexMatTextures::const_iterator tmtexi; - for (tmtexi = tmtex.begin(); tmtexi != tmtex.end(); ++tmtexi) { - const EggTexture *egg_tex = (*tmtexi)->_egg_tex; - TextureStage *stage = (*tmtexi)->_stage; - - tex_mat_attrib = apply_tex_mat(tex_mat_attrib, stage, egg_tex); - } - } - } - } - - if (texture_attrib != (RenderAttrib *)NULL) { - bucket.add_attrib(texture_attrib); - } - - if (tex_gen_attrib != (RenderAttrib *)NULL) { - bucket.add_attrib(tex_gen_attrib); - } - - if (tex_mat_attrib != (RenderAttrib *)NULL) { - bucket.add_attrib(tex_mat_attrib); - } - - if (egg_prim->has_material()) { - CPT(RenderAttrib) mt = - get_material_attrib(egg_prim->get_material(), - egg_prim->get_bface_flag()); - bucket.add_attrib(mt); - } - - - // Also check the color of the primitive to see if we should assume - // alpha based on the alpha values specified in the egg file. - if (am == EggRenderMode::AM_unspecified) { - if (egg_prim->has_color()) { - if (egg_prim->get_color()[3] != 1.0) { - implicit_alpha = true; - } - } - EggPrimitive::const_iterator vi; - for (vi = egg_prim->begin(); - !implicit_alpha && vi != egg_prim->end(); - ++vi) { - if ((*vi)->has_color()) { - if ((*vi)->get_color()[3] != 1.0) { - implicit_alpha = true; - } - } - } - - if (implicit_alpha) { - am = EggRenderMode::AM_on; - } - } - - if (am == EggRenderMode::AM_on && - egg_alpha_mode != EggRenderMode::AM_unspecified) { - // Alpha type "on" means to get the default transparency type. - am = egg_alpha_mode; - } - - switch (am) { - case EggRenderMode::AM_on: - case EggRenderMode::AM_blend: - bucket.add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha)); - break; - - case EggRenderMode::AM_blend_no_occlude: - bucket.add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha)); - bucket.add_attrib(DepthWriteAttrib::make(DepthWriteAttrib::M_off)); - break; - - case EggRenderMode::AM_ms: - bucket.add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_multisample)); - break; - - case EggRenderMode::AM_ms_mask: - bucket.add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_multisample_mask)); - break; - - case EggRenderMode::AM_binary: - bucket.add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_binary)); - break; - - case EggRenderMode::AM_dual: - bucket.add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_dual)); - break; - - default: - break; - } - - switch (dwm) { - case EggRenderMode::DWM_on: - bucket.add_attrib(DepthWriteAttrib::make(DepthWriteAttrib::M_on)); - break; - - case EggRenderMode::DWM_off: - bucket.add_attrib(DepthWriteAttrib::make(DepthWriteAttrib::M_off)); - break; - - default: - break; - } - - switch (dtm) { - case EggRenderMode::DTM_on: - bucket.add_attrib(DepthTestAttrib::make(DepthTestAttrib::M_less)); - break; - - case EggRenderMode::DTM_off: - bucket.add_attrib(DepthTestAttrib::make(DepthTestAttrib::M_none)); - break; - - default: - break; - } - - switch (vm) { - case EggRenderMode::VM_hidden: - bucket._hidden = true; - break; - - case EggRenderMode::VM_normal: - default: - break; - } - - if (has_bin) { - bucket.add_attrib(CullBinAttrib::make(bin, draw_order)); - - } else if (has_draw_order) { - bucket.add_attrib(CullBinAttrib::make("fixed", draw_order)); - } - - - if (egg_prim->get_bface_flag()) { - // The primitive is marked with backface culling disabled--we want - // to see both sides. - bucket.add_attrib(CullFaceAttrib::make(CullFaceAttrib::M_cull_none)); - } + bucket._state = render_state._state; + bucket._hidden = render_state._hidden; + bake_in_uvs = render_state._bake_in_uvs; } @@ -1683,9 +1324,7 @@ setup_bucket(BuilderBucket &bucket, EggLoader::BakeInUVs &bake_in_uvs, //////////////////////////////////////////////////////////////////// PandaNode *EggLoader:: make_node(EggNode *egg_node, PandaNode *parent) { - if (egg_node->is_of_type(EggPrimitive::get_class_type())) { - return make_node(DCAST(EggPrimitive, egg_node), parent); - } else if (egg_node->is_of_type(EggBin::get_class_type())) { + if (egg_node->is_of_type(EggBin::get_class_type())) { return make_node(DCAST(EggBin, egg_node), parent); } else if (egg_node->is_of_type(EggGroup::get_class_type())) { return make_node(DCAST(EggGroup, egg_node), parent); @@ -1698,6 +1337,7 @@ make_node(EggNode *egg_node, PandaNode *parent) { return (PandaNode *)NULL; } +/* //////////////////////////////////////////////////////////////////// // Function: EggLoader::make_node (EggPrimitive) // Access: Private @@ -1727,6 +1367,7 @@ make_node(EggPrimitive *egg_prim, PandaNode *parent) { } return (PandaNode *)NULL; } +*/ //////////////////////////////////////////////////////////////////// // Function: EggLoader::make_node (EggBin) @@ -1735,41 +1376,149 @@ make_node(EggPrimitive *egg_prim, PandaNode *parent) { //////////////////////////////////////////////////////////////////// PandaNode *EggLoader:: make_node(EggBin *egg_bin, PandaNode *parent) { - // Presently, an EggBin can only mean an LOD node (i.e. a parent of - // one or more EggGroups with LOD specifications). Later it might - // mean other things as well. + // An EggBin might mean an LOD node (i.e. a parent of one or more + // EggGroups with LOD specifications), or it might mean a polyset + // node (a parent of one or more similar EggPrimitives). + switch (egg_bin->get_bin_number()) { + case EggBinner::BN_polyset: + return make_polyset(egg_bin, parent); - nassertr((EggBinner::BinNumber)egg_bin->get_bin_number() == EggBinner::BN_lod, NULL); + case EggBinner::BN_lod: + return make_lod(egg_bin, parent); + + case EggBinner::BN_none: + break; + } + + // Shouldn't get here. + return (PandaNode *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::make_polyset +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +PandaNode *EggLoader:: +make_polyset(EggBin *egg_bin, PandaNode *parent) { + if (egg_bin->empty()) { + // If there are no children--no primitives--never mind. + return NULL; + } + + // We know that all of the primitives in the bin have the same + // vertex pool and same render state, so we can get that information + // from the first primitive. + EggGroup::const_iterator ci = egg_bin->begin(); + nassertr(ci != egg_bin->end(), NULL); + CPT(EggPrimitive) first_prim = DCAST(EggPrimitive, (*ci)); + nassertr(first_prim != (EggPrimitive *)NULL, NULL); + const EggRenderState *render_state; + DCAST_INTO_R(render_state, first_prim->get_user_data(EggRenderState::get_class_type()), NULL); + + if (render_state->_hidden && egg_suppress_hidden) { + // Eat this polyset. + return NULL; + } + + if (!use_qpgeom) { + // In the old Geom system, just send each primitive to the + // Builder. + for (ci = egg_bin->begin(); ci != egg_bin->end(); ++ci) { + EggPrimitive *egg_prim; + DCAST_INTO_R(egg_prim, (*ci), NULL); + make_nonindexed_primitive(egg_prim, parent, NULL, _comp_verts_maker); + } + + return NULL; + } + + // Convert the primitives' vertex pool to a GeomVertexData. + PT(qpGeomVertexData) vertex_data = + make_vertex_data(first_prim->get_pool(), first_prim->get_vertex_to_node()); + nassertr(vertex_data != (qpGeomVertexData *)NULL, NULL); + + // And now create a Geom to hold the primitives. + PT(qpGeom) geom = new qpGeom; + geom->set_vertex_data(vertex_data); + + // Automatically triangulate any higher-order polygons we might have. + egg_bin->triangulate_polygons(true); + + // Now create a handful of GeomPrimitives corresponding to the + // various types of primitives we have. + Primitives primitives; + for (ci = egg_bin->begin(); ci != egg_bin->end(); ++ci) { + EggPrimitive *egg_prim; + DCAST_INTO_R(egg_prim, (*ci), NULL); + make_primitive(egg_prim, primitives); + } + + if (!primitives.empty()) { + // Add each new primitive to the Geom. + Primitives::const_iterator pi; + for (pi = primitives.begin(); pi != primitives.end(); ++pi) { + qpGeomPrimitive *primitive = (*pi).second; + geom->add_primitive(primitive); + } + + // Now, is our parent node a GeomNode, or just an ordinary + // PandaNode? If it's a GeomNode, we can add the new Geom directly + // to our parent; otherwise, we need to create a new node. + if (parent->is_geom_node() && !render_state->_hidden) { + DCAST(GeomNode, parent)->add_geom(geom, render_state->_state); + + } else { + PT(GeomNode) geom_node = new GeomNode(egg_bin->get_name()); + if (render_state->_hidden) { + parent->add_stashed(geom_node); + } else { + parent->add_child(geom_node); + } + geom_node->add_geom(geom, render_state->_state); + } + } + + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::make_lod +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +PandaNode *EggLoader:: +make_lod(EggBin *egg_bin, PandaNode *parent) { LODNode *lod_node = new LODNode(egg_bin->get_name()); pvector instances; - + EggGroup::const_iterator ci; for (ci = egg_bin->begin(); ci != egg_bin->end(); ++ci) { LODInstance instance(*ci); instances.push_back(instance); } - + // Now that we've created all of our children, put them in the // proper order and tell the LOD node about them. sort(instances.begin(), instances.end()); - + if (!instances.empty()) { // Set up the LOD node's center. All of the children should have // the same center, because that's how we binned them. lod_node->set_center(LCAST(float, instances[0]._d->_center)); } - + for (size_t i = 0; i < instances.size(); i++) { // Create the children in the proper order within the scene graph. const LODInstance &instance = instances[i]; make_node(instance._egg_node, lod_node); - + // All of the children should have the same center, because that's // how we binned them. nassertr(lod_node->get_center().almost_equal (LCAST(float, instance._d->_center), 0.01), NULL); - + // Tell the LOD node about this child's switching distances. lod_node->add_switch(instance._d->_switch_in, instance._d->_switch_out); } @@ -1891,8 +1640,21 @@ make_node(EggGroup *egg_group, PandaNode *parent) { } } else { - // A normal group; just create a normal node, and traverse. - node = new PandaNode(egg_group->get_name()); + // A normal group; just create a normal node, and traverse. But + // if all of the children of this group are polysets, anticipate + // this for the benefit of smaller grouping, and create a single + // GeomNode for all of the children. + bool all_polysets = false; + bool any_hidden = false; + if (use_qpgeom) { + check_for_polysets(egg_group, all_polysets, any_hidden); + } + + if (all_polysets && !any_hidden) { + node = new GeomNode(egg_group->get_name()); + } else { + node = new PandaNode(egg_group->get_name()); + } EggGroup::const_iterator ci; for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { @@ -2030,6 +1792,175 @@ make_node(EggGroupNode *egg_group, PandaNode *parent) { return node; } +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::check_for_polysets +// Access: Private +// Description: Sets all_polysets true if all of the children of this +// node represent a polyset. Sets any_hidden true if +// any of those polysets are flagged hidden. +//////////////////////////////////////////////////////////////////// +void EggLoader:: +check_for_polysets(EggGroup *egg_group, bool &all_polysets, bool &any_hidden) { + all_polysets = (!egg_group->empty()); + any_hidden = false; + + EggGroup::const_iterator ci; + for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { + if ((*ci)->is_of_type(EggBin::get_class_type())) { + EggBin *egg_bin = DCAST(EggBin, (*ci)); + if (egg_bin->get_bin_number() == EggBinner::BN_polyset) { + // We know that all of the primitives in the bin have the same + // render state, so we can get that information from the first + // primitive. + EggGroup::const_iterator bci = egg_bin->begin(); + nassertv(bci != egg_bin->end()); + const EggPrimitive *first_prim; + DCAST_INTO_V(first_prim, (*bci)); + const EggRenderState *render_state; + DCAST_INTO_V(render_state, first_prim->get_user_data(EggRenderState::get_class_type())); + + if (render_state->_hidden) { + any_hidden = true; + } + } else { + all_polysets = false; + return; + } + } else { + all_polysets = false; + return; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::make_vertex_data +// Access: Private +// Description: Creates a GeomVertexData structure from the vertex +// pool, for the indicated transform space. If a +// GeomVertexData has already been created for this +// transform, just returns it. +//////////////////////////////////////////////////////////////////// +PT(qpGeomVertexData) EggLoader:: +make_vertex_data(EggVertexPool *vertex_pool, const LMatrix4d &transform) { + VertexPoolTransform vpt; + vpt._vertex_pool = vertex_pool; + vpt._transform = transform; + + VertexPoolData::iterator di; + di = _vertex_pool_data.find(vpt); + if (di != _vertex_pool_data.end()) { + return (*di).second; + } + + // Decide on the format for the vertices. + PT(qpGeomVertexArrayFormat) array_format = new qpGeomVertexArrayFormat; + array_format->add_data_type + (InternalName::get_vertex(), vertex_pool->get_num_dimensions(), + qpGeomVertexDataType::NT_float); + + if (vertex_pool->has_normals()) { + array_format->add_data_type + (InternalName::get_normal(), 3, qpGeomVertexDataType::NT_float); + } + + if (vertex_pool->has_colors()) { + array_format->add_data_type + (InternalName::get_color(), 1, qpGeomVertexDataType::NT_packed_argb); + } + + vector_string uv_names; + vertex_pool->get_uv_names(uv_names); + vector_string::const_iterator ni; + for (ni = uv_names.begin(); ni != uv_names.end(); ++ni) { + string name = (*ni); + if (name == "default") { + name = string(); + } + array_format->add_data_type + (InternalName::get_texcoord_name(name), 2, + qpGeomVertexDataType::NT_float); + } + + CPT(qpGeomVertexFormat) format = + qpGeomVertexFormat::register_format(new qpGeomVertexFormat(array_format)); + + // Now create a new GeomVertexData using the indicated format. + PT(qpGeomVertexData) vertex_data = new qpGeomVertexData(format); + + // And fill the data from the vertex pool. + EggVertexPool::const_iterator vi; + for (vi = vertex_pool->begin(); vi != vertex_pool->end(); ++vi) { + qpGeomVertexIterator gvi(vertex_data); + EggVertex *vertex = (*vi); + gvi.set_vertex(vertex->get_index()); + + gvi.set_data_type(InternalName::get_vertex()); + gvi.set_data4(LCAST(float, vertex->get_pos4())); + + if (vertex->has_normal()) { + gvi.set_data_type(InternalName::get_normal()); + gvi.set_data3(LCAST(float, vertex->get_normal())); + } + + if (vertex->has_color()) { + gvi.set_data_type(InternalName::get_color()); + gvi.set_data4(vertex->get_color()); + } + + EggVertex::const_uv_iterator uvi; + for (uvi = vertex->uv_begin(); uvi != vertex->uv_end(); ++uvi) { + EggVertexUV *uv = (*uvi); + string name = uv->get_name(); + if (name == "default") { + name = string(); + } + gvi.set_data_type(InternalName::get_texcoord_name(name)); + gvi.set_data2(LCAST(float, uv->get_uv())); + } + } + + bool inserted = _vertex_pool_data.insert + (VertexPoolData::value_type(vpt, vertex_data)).second; + nassertr(inserted, vertex_data); + + return vertex_data; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::make_primitive +// Access: Private +// Description: Creates a GeomPrimitive corresponding to the +// indicated EggPrimitive, and adds it to the set. +//////////////////////////////////////////////////////////////////// +void EggLoader:: +make_primitive(EggPrimitive *egg_prim, EggLoader::Primitives &primitives) { + PT(qpGeomPrimitive) primitive; + if (egg_prim->is_of_type(EggPolygon::get_class_type())) { + if (egg_prim->size() == 3) { + primitive = new qpGeomTriangles; + } + } + + if (primitive == NULL) { + // Don't know how to make this kind of primitive. + return; + } + + // Insert the primitive into the set, but if we already have a + // primitive of that type, reset the pointer to that one instead. + pair result = + primitives.insert(Primitives::value_type(primitive->get_type(), primitive)); + primitive = (*result.first).second; + + // Now add the vertices. + EggPrimitive::const_iterator vi; + for (vi = egg_prim->begin(); vi != egg_prim->end(); ++vi) { + primitive->add_vertex((*vi)->get_index()); + } + primitive->close_primitive(); +} + //////////////////////////////////////////////////////////////////// // Function: EggLoader::set_portal_polygon // Access: Private @@ -3086,84 +3017,5 @@ get_combine_operand(const EggTexture *egg_tex, return TextureStage::CO_undefined; } -//////////////////////////////////////////////////////////////////// -// Function: EggLoader::get_tex_gen -// Access: Private, Static -// Description: Extracts the tex_gen from the given egg texture, -// and returns its corresponding TexGenAttrib mode. -//////////////////////////////////////////////////////////////////// -TexGenAttrib::Mode EggLoader:: -get_tex_gen(const EggTexture *egg_tex) { - switch (egg_tex->get_tex_gen()) { - case EggTexture::TG_unspecified: - return TexGenAttrib::M_off; - - case EggTexture::TG_eye_sphere_map: - return TexGenAttrib::M_eye_sphere_map; - - case EggTexture::TG_world_cube_map: - return TexGenAttrib::M_world_cube_map; - - case EggTexture::TG_eye_cube_map: - return TexGenAttrib::M_eye_cube_map; - - case EggTexture::TG_world_normal: - return TexGenAttrib::M_world_normal; - - case EggTexture::TG_eye_normal: - return TexGenAttrib::M_eye_normal; - - case EggTexture::TG_world_position: - return TexGenAttrib::M_world_position; - - case EggTexture::TG_object_position: - return TexGenAttrib::M_object_position; - - case EggTexture::TG_eye_position: - return TexGenAttrib::M_eye_position; - }; - - return TexGenAttrib::M_off; -} - -//////////////////////////////////////////////////////////////////// -// Function: EggLoader::apply_tex_mat -// Access: Private, Static -// Description: Applies the texture matrix from the indicated egg -// texture to the given TexMatrixAttrib, and returns the -// new attrib. -//////////////////////////////////////////////////////////////////// -CPT(RenderAttrib) EggLoader:: -apply_tex_mat(CPT(RenderAttrib) tex_mat_attrib, - TextureStage *stage, const EggTexture *egg_tex) { - if (egg_tex->has_transform()) { - const LMatrix3d &tex_mat = egg_tex->get_transform(); - LMatrix4f mat4(tex_mat(0, 0), tex_mat(0, 1), 0.0f, tex_mat(0, 2), - tex_mat(1, 0), tex_mat(1, 1), 0.0f, tex_mat(1, 2), - 0.0f, 0.0f, 1.0f, 0.0f, - tex_mat(2, 0), tex_mat(2, 1), 0.0f, tex_mat(2, 2)); - CPT(TransformState) transform; - - LVecBase3f scale, shear, hpr, translate; - if (decompose_matrix(mat4, scale, shear, hpr, translate)) { - // If the texture matrix can be represented componentwise, do - // so. - transform = TransformState::make_pos_hpr_scale_shear - (translate, hpr, scale, shear); - - } else { - // Otherwise, make a matrix transform. - transform = TransformState::make_mat(mat4); - } - - if (tex_mat_attrib == (const RenderAttrib *)NULL) { - tex_mat_attrib = TexMatrixAttrib::make(); - } - tex_mat_attrib = DCAST(TexMatrixAttrib, tex_mat_attrib)-> - add_stage(stage, transform); - } - - return tex_mat_attrib; -} diff --git a/panda/src/egg2pg/eggLoader.h b/panda/src/egg2pg/eggLoader.h index cf5b4490c6..0ee365dbe3 100644 --- a/panda/src/egg2pg/eggLoader.h +++ b/panda/src/egg2pg/eggLoader.h @@ -39,6 +39,8 @@ #include "texGenAttrib.h" #include "eggTransform3d.h" #include "computedVerticesMaker.h" +#include "qpgeomVertexData.h" +#include "qpgeomPrimitive.h" class EggNode; class EggBin; @@ -95,6 +97,9 @@ private: // This structure is returned by setup_bucket(). typedef pmap BakeInUVs; + + // This is used by make_primitive(). + typedef pmap Primitives; void make_nurbs_curve(EggNurbsCurve *egg_curve, PandaNode *parent, const LMatrix4d &mat); @@ -108,21 +113,25 @@ private: void apply_texture_attributes(Texture *tex, const EggTexture *egg_tex); PT(TextureStage) make_texture_stage(const EggTexture *egg_tex); - CPT(RenderAttrib) get_material_attrib(const EggMaterial *egg_mat, - bool bface); - void setup_bucket(BuilderBucket &bucket, BakeInUVs &bake_in_uvs, PandaNode *parent, EggPrimitive *egg_prim); PandaNode *make_node(EggNode *egg_node, PandaNode *parent); - PandaNode *make_node(EggPrimitive *egg_prim, PandaNode *parent); PandaNode *make_node(EggBin *egg_bin, PandaNode *parent); + PandaNode *make_polyset(EggBin *egg_bin, PandaNode *parent); + PandaNode *make_lod(EggBin *egg_bin, PandaNode *parent); PandaNode *make_node(EggGroup *egg_group, PandaNode *parent); PandaNode *create_group_arc(EggGroup *egg_group, PandaNode *parent, PandaNode *node); PandaNode *make_node(EggTable *egg_table, PandaNode *parent); PandaNode *make_node(EggGroupNode *egg_group, PandaNode *parent); + void check_for_polysets(EggGroup *egg_group, bool &all_polysets, + bool &any_hidden); + PT(qpGeomVertexData) make_vertex_data(EggVertexPool *vertex_pool, + const LMatrix4d &transform); + void make_primitive(EggPrimitive *egg_prim, Primitives &primitives); + void set_portal_polygon(EggGroup *egg_group, PortalNode *pnode); EggPolygon *find_first_polygon(EggGroup *egg_group); @@ -173,11 +182,6 @@ private: static TextureStage::CombineOperand get_combine_operand(const EggTexture *egg_tex, EggTexture::CombineChannel channel, int n); - static TexGenAttrib::Mode get_tex_gen(const EggTexture *egg_tex); - - static CPT(RenderAttrib) - apply_tex_mat(CPT(RenderAttrib) tex_mat_attrib, - TextureStage *stage, const EggTexture *egg_tex); Builder _builder; @@ -191,6 +195,15 @@ private: typedef pset Decals; Decals _decals; + class VertexPoolTransform { + public: + INLINE bool operator < (const VertexPoolTransform &other) const; + EggVertexPool *_vertex_pool; + LMatrix4d _transform; + }; + typedef pmap VertexPoolData; + VertexPoolData _vertex_pool_data; + DeferredNodes _deferred_nodes; ComputedVerticesMaker _comp_verts_maker; @@ -199,7 +212,10 @@ public: PT(PandaNode) _root; EggData _data; bool _error; + + friend class EggRenderState; }; +#include "eggLoader.I" #endif diff --git a/panda/src/egg2pg/eggRenderState.I b/panda/src/egg2pg/eggRenderState.I new file mode 100644 index 0000000000..2c65c632f2 --- /dev/null +++ b/panda/src/egg2pg/eggRenderState.I @@ -0,0 +1,58 @@ +// Filename: eggRenderState.I +// Created by: drose (12Mar05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: EggRenderState::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +EggRenderState:: +EggRenderState(EggLoader &loader) : + _loader(loader), + _state(RenderState::make_empty()), + _hidden(false) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: EggRenderState::add_attrib +// Access: Public +// Description: A convenience function to add the indicated render +// attribute to the aggregate state. +//////////////////////////////////////////////////////////////////// +INLINE void EggRenderState:: +add_attrib(const RenderAttrib *attrib) { + _state = _state->add_attrib(attrib); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggRenderState::operator < +// Access: Public +// Description: Provides a unique ordering for different +// EggRenderState objects, so that primitives of similar +// state can be grouped together by the EggBinner. +//////////////////////////////////////////////////////////////////// +INLINE bool EggRenderState:: +operator < (const EggRenderState &other) const { + if (_state != other._state) { + return _state < other._state; + } + + return (int)_hidden < (int)other._hidden; +} diff --git a/panda/src/egg2pg/eggRenderState.cxx b/panda/src/egg2pg/eggRenderState.cxx new file mode 100644 index 0000000000..3a834caa22 --- /dev/null +++ b/panda/src/egg2pg/eggRenderState.cxx @@ -0,0 +1,491 @@ +// Filename: eggRenderState.cxx +// Created by: drose (12Mar05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "eggRenderState.h" +#include "eggRenderMode.h" +#include "textureAttrib.h" +#include "renderAttrib.h" +#include "eggTexture.h" +#include "texGenAttrib.h" +#include "internalName.h" +#include "eggCurve.h" +#include "eggSurface.h" +#include "cullBinAttrib.h" +#include "cullFaceAttrib.h" +#include "transparencyAttrib.h" +#include "depthWriteAttrib.h" +#include "depthTestAttrib.h" +#include "texMatrixAttrib.h" +#include "material.h" +#include "materialAttrib.h" +#include "materialPool.h" +#include "config_gobj.h" + + +//////////////////////////////////////////////////////////////////// +// Function: EggRenderState::fill_state +// Access: Public +// Description: Sets up the state as appropriate for the indicated +// primitive. +//////////////////////////////////////////////////////////////////// +void EggRenderState:: +fill_state(EggPrimitive *egg_prim) { + // The various EggRenderMode properties can be defined directly at + // the primitive, at a group above the primitive, or an a texture + // applied to the primitive. The EggNode::determine_*() functions + // can find the right pointer to the level at which this is actually + // defined for a given primitive. + EggRenderMode::AlphaMode am = EggRenderMode::AM_unspecified; + EggRenderMode::DepthWriteMode dwm = EggRenderMode::DWM_unspecified; + EggRenderMode::DepthTestMode dtm = EggRenderMode::DTM_unspecified; + EggRenderMode::VisibilityMode vm = EggRenderMode::VM_unspecified; + bool implicit_alpha = false; + bool has_draw_order = false; + int draw_order = 0; + bool has_bin = false; + string bin; + + EggRenderMode *render_mode; + render_mode = egg_prim->determine_alpha_mode(); + if (render_mode != (EggRenderMode *)NULL) { + am = render_mode->get_alpha_mode(); + } + render_mode = egg_prim->determine_depth_write_mode(); + if (render_mode != (EggRenderMode *)NULL) { + dwm = render_mode->get_depth_write_mode(); + } + render_mode = egg_prim->determine_depth_test_mode(); + if (render_mode != (EggRenderMode *)NULL) { + dtm = render_mode->get_depth_test_mode(); + } + render_mode = egg_prim->determine_visibility_mode(); + if (render_mode != (EggRenderMode *)NULL) { + vm = render_mode->get_visibility_mode(); + } + render_mode = egg_prim->determine_draw_order(); + if (render_mode != (EggRenderMode *)NULL) { + has_draw_order = true; + draw_order = render_mode->get_draw_order(); + } + render_mode = egg_prim->determine_bin(); + if (render_mode != (EggRenderMode *)NULL) { + has_bin = true; + bin = render_mode->get_bin(); + } + + add_attrib(TextureAttrib::make_off()); + int num_textures = egg_prim->get_num_textures(); + CPT(RenderAttrib) texture_attrib = NULL; + CPT(RenderAttrib) tex_gen_attrib = NULL; + CPT(RenderAttrib) tex_mat_attrib = NULL; + TexMats tex_mats; + + for (int i = 0; i < num_textures; i++) { + PT_EggTexture egg_tex = egg_prim->get_texture(i); + + const TextureDef &def = _loader._textures[egg_tex]; + if (def._texture != (const RenderAttrib *)NULL) { + if (texture_attrib == (RenderAttrib *)NULL) { + texture_attrib = def._texture; + } else { + texture_attrib = texture_attrib->compose(def._texture); + } + + // If neither the primitive nor the texture specified an alpha + // mode, assume it should be alpha'ed if the texture has an + // alpha channel (unless the texture environment type is one + // that doesn't apply its alpha to the result). + if (am == EggRenderMode::AM_unspecified) { + const TextureAttrib *tex_attrib = DCAST(TextureAttrib, def._texture); + Texture *tex = tex_attrib->get_texture(); + nassertv(tex != (Texture *)NULL); + int num_components = tex->get_num_components(); + if (egg_tex->has_alpha_channel(num_components)) { + switch (egg_tex->get_env_type()) { + case EggTexture::ET_decal: + case EggTexture::ET_add: + break; + + default: + implicit_alpha = true; + } + } + } + + // Check for a texgen attrib. + bool has_tex_gen = false; + if (egg_tex->get_tex_gen() != EggTexture::TG_unspecified) { + has_tex_gen = true; + if (tex_gen_attrib == (const RenderAttrib *)NULL) { + tex_gen_attrib = TexGenAttrib::make(); + } + tex_gen_attrib = DCAST(TexGenAttrib, tex_gen_attrib)-> + add_stage(def._stage, get_tex_gen(egg_tex)); + } + + // Record the texture's associated texture matrix, so we can see + // if we can safely bake it into the UV's. (We need to get the + // complete list of textures that share this same set of UV's + // per each unique texture matrix. Whew!) + CPT(InternalName) uv_name; + if (egg_tex->has_uv_name() && egg_tex->get_uv_name() != string("default")) { + uv_name = InternalName::get_texcoord_name(egg_tex->get_uv_name()); + } else { + uv_name = InternalName::get_texcoord(); + } + + if (has_tex_gen) { + // If the texture has a texgen mode, we will always apply its + // texture transform, never bake it in. In fact, we don't + // even care about its UV's in this case, since we won't be + // using them. + tex_mat_attrib = apply_tex_mat(tex_mat_attrib, def._stage, egg_tex); + + } else { + // Otherwise, we need to record that there is at least one + // texture on this particular UV name and with this particular + // texture matrix. If there are no other textures, or if all + // of the other textures use the same texture matrix, then + // tex_mats[uv_name].size() will remain 1 (which tells us we + // can bake in the texture matrix to the UV's). On the other + // hand, if there is another texture on the same uv name but + // with a different transform, it will increase + // tex_mats[uv_name].size() to at least 2, indicating we can't + // bake in the texture matrix. + tex_mats[uv_name][egg_tex->get_transform()].push_back(&def); + } + } + } + + // These parametric primitive types can't have their UV's baked in, + // so if we have one of these we always need to apply the texture + // matrix as a separate attribute, regardless of how many textures + // share the particular UV set. + bool needs_tex_mat = (egg_prim->is_of_type(EggCurve::get_class_type()) || + egg_prim->is_of_type(EggSurface::get_class_type())); + + // Now that we've visited all of the textures in the above loop, we + // can go back and see how many of them share the same UV name and + // texture matrix. + TexMats::const_iterator tmi; + for (tmi = tex_mats.begin(); tmi != tex_mats.end(); ++tmi) { + const InternalName *uv_name = (*tmi).first; + const TexMatTransforms &tmt = (*tmi).second; + + if (tmt.size() == 1 && !needs_tex_mat && !use_qpgeom) { + // Only one unique transform sharing this set of UV's. We can + // bake in the transform! + const TexMatTextures &tmtex = (*tmt.begin()).second; + + // The first EggTexture on the list is sufficient, since we know + // they all have the same transform. + nassertv(!tmtex.empty()); + TexMatTextures::const_iterator tmtexi = tmtex.begin(); + const EggTexture *egg_tex = (*tmtexi)->_egg_tex; + if (egg_tex->has_transform()) { + // If there's no transform, it's an identity matrix; don't + // bother recording it. Of course, it would do no harm to + // record it if we felt like it. + _bake_in_uvs[uv_name] = egg_tex; + } + + } else { + // Multiple transforms on this UV set, or a geometry type that + // doesn't support baking in UV's. We have to apply the + // texture matrix to each stage. + TexMatTransforms::const_iterator tmti; + for (tmti = tmt.begin(); tmti != tmt.end(); ++tmti) { + const TexMatTextures &tmtex = (*tmti).second; + TexMatTextures::const_iterator tmtexi; + for (tmtexi = tmtex.begin(); tmtexi != tmtex.end(); ++tmtexi) { + const EggTexture *egg_tex = (*tmtexi)->_egg_tex; + TextureStage *stage = (*tmtexi)->_stage; + + tex_mat_attrib = apply_tex_mat(tex_mat_attrib, stage, egg_tex); + } + } + } + } + + if (texture_attrib != (RenderAttrib *)NULL) { + add_attrib(texture_attrib); + } + + if (tex_gen_attrib != (RenderAttrib *)NULL) { + add_attrib(tex_gen_attrib); + } + + if (tex_mat_attrib != (RenderAttrib *)NULL) { + add_attrib(tex_mat_attrib); + } + + if (egg_prim->has_material()) { + CPT(RenderAttrib) mt = + get_material_attrib(egg_prim->get_material(), + egg_prim->get_bface_flag()); + add_attrib(mt); + } + + + // Also check the color of the primitive to see if we should assume + // alpha based on the alpha values specified in the egg file. + if (am == EggRenderMode::AM_unspecified) { + if (egg_prim->has_color()) { + if (egg_prim->get_color()[3] != 1.0) { + implicit_alpha = true; + } + } + EggPrimitive::const_iterator vi; + for (vi = egg_prim->begin(); + !implicit_alpha && vi != egg_prim->end(); + ++vi) { + if ((*vi)->has_color()) { + if ((*vi)->get_color()[3] != 1.0) { + implicit_alpha = true; + } + } + } + + if (implicit_alpha) { + am = EggRenderMode::AM_on; + } + } + + if (am == EggRenderMode::AM_on && + egg_alpha_mode != EggRenderMode::AM_unspecified) { + // Alpha type "on" means to get the default transparency type. + am = egg_alpha_mode; + } + + switch (am) { + case EggRenderMode::AM_on: + case EggRenderMode::AM_blend: + add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha)); + break; + + case EggRenderMode::AM_blend_no_occlude: + add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha)); + add_attrib(DepthWriteAttrib::make(DepthWriteAttrib::M_off)); + break; + + case EggRenderMode::AM_ms: + add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_multisample)); + break; + + case EggRenderMode::AM_ms_mask: + add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_multisample_mask)); + break; + + case EggRenderMode::AM_binary: + add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_binary)); + break; + + case EggRenderMode::AM_dual: + add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_dual)); + break; + + default: + break; + } + + switch (dwm) { + case EggRenderMode::DWM_on: + add_attrib(DepthWriteAttrib::make(DepthWriteAttrib::M_on)); + break; + + case EggRenderMode::DWM_off: + add_attrib(DepthWriteAttrib::make(DepthWriteAttrib::M_off)); + break; + + default: + break; + } + + switch (dtm) { + case EggRenderMode::DTM_on: + add_attrib(DepthTestAttrib::make(DepthTestAttrib::M_less)); + break; + + case EggRenderMode::DTM_off: + add_attrib(DepthTestAttrib::make(DepthTestAttrib::M_none)); + break; + + default: + break; + } + + switch (vm) { + case EggRenderMode::VM_hidden: + _hidden = true; + break; + + case EggRenderMode::VM_normal: + default: + break; + } + + if (has_bin) { + add_attrib(CullBinAttrib::make(bin, draw_order)); + + } else if (has_draw_order) { + add_attrib(CullBinAttrib::make("fixed", draw_order)); + } + + + if (egg_prim->get_bface_flag()) { + // The primitive is marked with backface culling disabled--we want + // to see both sides. + add_attrib(CullFaceAttrib::make(CullFaceAttrib::M_cull_none)); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggRenderState::get_material_attrib +// Access: Private +// Description: Returns a RenderAttrib suitable for enabling the +// material indicated by the given EggMaterial, and with +// the indicated backface flag. +//////////////////////////////////////////////////////////////////// +CPT(RenderAttrib) EggRenderState:: +get_material_attrib(const EggMaterial *egg_mat, bool bface) { + Materials &materials = + bface ? _loader._materials_bface : _loader._materials; + + // First, check whether we've seen this material before. + Materials::const_iterator mi; + mi = materials.find(egg_mat); + if (mi != materials.end()) { + return (*mi).second; + } + + // Ok, this is the first time we've seen this particular + // EggMaterial. Create a new Material that matches it. + PT(Material) mat = new Material; + if (egg_mat->has_diff()) { + mat->set_diffuse(egg_mat->get_diff()); + // By default, ambient is the same as diffuse, if diffuse is + // specified but ambient is not. + mat->set_ambient(egg_mat->get_diff()); + } + if (egg_mat->has_amb()) { + mat->set_ambient(egg_mat->get_amb()); + } + if (egg_mat->has_emit()) { + mat->set_emission(egg_mat->get_emit()); + } + if (egg_mat->has_spec()) { + mat->set_specular(egg_mat->get_spec()); + } + if (egg_mat->has_shininess()) { + mat->set_shininess(egg_mat->get_shininess()); + } + if (egg_mat->has_local()) { + mat->set_local(egg_mat->get_local()); + } + + mat->set_twoside(bface); + + // Now get a global Material pointer, shared with other models. + const Material *shared_mat = MaterialPool::get_material(mat); + + // And create a MaterialAttrib for this Material. + CPT(RenderAttrib) mt = MaterialAttrib::make(shared_mat); + materials.insert(Materials::value_type(egg_mat, mt)); + + return mt; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggRenderState::get_tex_gen +// Access: Private, Static +// Description: Extracts the tex_gen from the given egg texture, +// and returns its corresponding TexGenAttrib mode. +//////////////////////////////////////////////////////////////////// +TexGenAttrib::Mode EggRenderState:: +get_tex_gen(const EggTexture *egg_tex) { + switch (egg_tex->get_tex_gen()) { + case EggTexture::TG_unspecified: + return TexGenAttrib::M_off; + + case EggTexture::TG_eye_sphere_map: + return TexGenAttrib::M_eye_sphere_map; + + case EggTexture::TG_world_cube_map: + return TexGenAttrib::M_world_cube_map; + + case EggTexture::TG_eye_cube_map: + return TexGenAttrib::M_eye_cube_map; + + case EggTexture::TG_world_normal: + return TexGenAttrib::M_world_normal; + + case EggTexture::TG_eye_normal: + return TexGenAttrib::M_eye_normal; + + case EggTexture::TG_world_position: + return TexGenAttrib::M_world_position; + + case EggTexture::TG_object_position: + return TexGenAttrib::M_object_position; + + case EggTexture::TG_eye_position: + return TexGenAttrib::M_eye_position; + }; + + return TexGenAttrib::M_off; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggRenderState::apply_tex_mat +// Access: Private, Static +// Description: Applies the texture matrix from the indicated egg +// texture to the given TexMatrixAttrib, and returns the +// new attrib. +//////////////////////////////////////////////////////////////////// +CPT(RenderAttrib) EggRenderState:: +apply_tex_mat(CPT(RenderAttrib) tex_mat_attrib, + TextureStage *stage, const EggTexture *egg_tex) { + if (egg_tex->has_transform()) { + const LMatrix3d &tex_mat = egg_tex->get_transform(); + LMatrix4f mat4(tex_mat(0, 0), tex_mat(0, 1), 0.0f, tex_mat(0, 2), + tex_mat(1, 0), tex_mat(1, 1), 0.0f, tex_mat(1, 2), + 0.0f, 0.0f, 1.0f, 0.0f, + tex_mat(2, 0), tex_mat(2, 1), 0.0f, tex_mat(2, 2)); + CPT(TransformState) transform; + + LVecBase3f scale, shear, hpr, translate; + if (decompose_matrix(mat4, scale, shear, hpr, translate)) { + // If the texture matrix can be represented componentwise, do + // so. + transform = TransformState::make_pos_hpr_scale_shear + (translate, hpr, scale, shear); + + } else { + // Otherwise, make a matrix transform. + transform = TransformState::make_mat(mat4); + } + + if (tex_mat_attrib == (const RenderAttrib *)NULL) { + tex_mat_attrib = TexMatrixAttrib::make(); + } + tex_mat_attrib = DCAST(TexMatrixAttrib, tex_mat_attrib)-> + add_stage(stage, transform); + } + + return tex_mat_attrib; +} diff --git a/panda/src/egg2pg/eggRenderState.h b/panda/src/egg2pg/eggRenderState.h new file mode 100644 index 0000000000..4304849d9f --- /dev/null +++ b/panda/src/egg2pg/eggRenderState.h @@ -0,0 +1,84 @@ +// Filename: eggRenderState.h +// Created by: drose (12Mar05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#ifndef EGGRENDERSTATE_H +#define EGGRENDERSTATE_H + +#include "pandabase.h" + +#include "eggUserData.h" +#include "eggLoader.h" +#include "renderState.h" +#include "renderAttrib.h" +#include "internalName.h" +#include "luse.h" +#include "pointerTo.h" +#include "pvector.h" +#include "pmap.h" + +class EggPrimitive; +class EggTexture; +class EggMaterial; + +/////////////////////////////////////////////////////////////////// +// Class : EggRenderState +// Description : This class is used within this package only to record +// the render state that should be assigned to each +// primitive. It is assigned to EggPrimitive objects +// via the EggBinner. +//////////////////////////////////////////////////////////////////// +class EggRenderState : public EggUserData { +public: + INLINE EggRenderState(EggLoader &loader); + INLINE void add_attrib(const RenderAttrib *attrib); + + void fill_state(EggPrimitive *egg_prim); + + INLINE bool operator < (const EggRenderState &other) const; + +private: + CPT(RenderAttrib) get_material_attrib(const EggMaterial *egg_mat, + bool bface); + static TexGenAttrib::Mode get_tex_gen(const EggTexture *egg_tex); + + static CPT(RenderAttrib) + apply_tex_mat(CPT(RenderAttrib) tex_mat_attrib, + TextureStage *stage, const EggTexture *egg_tex); + +public: + CPT(RenderState) _state; + bool _hidden; + + typedef EggLoader::BakeInUVs BakeInUVs; + typedef EggLoader::TextureDef TextureDef; + typedef EggLoader::Materials Materials; + + BakeInUVs _bake_in_uvs; + +private: + EggLoader &_loader; + + typedef pvector TexMatTextures; + typedef pmap TexMatTransforms; + typedef pmap TexMats; +}; + +#include "eggRenderState.I" + +#endif + diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index d6e969fd89..711cd1255b 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -547,6 +547,12 @@ reset() { << "max texture dimension = " << _max_texture_dimension << ", max 3d texture = " << _max_3d_texture_dimension << ", max cube map = " << _max_cube_map_dimension << "\n"; + GLint max_elements_vertices, max_elements_indices; + GLP(GetIntegerv)(GL_MAX_ELEMENTS_VERTICES, &max_elements_vertices); + GLP(GetIntegerv)(GL_MAX_ELEMENTS_INDICES, &max_elements_indices); + GLCAT.debug() + << "max_elements_vertices = " << max_elements_vertices + << ", max_elements_indices = " << max_elements_indices << "\n"; } report_my_gl_errors(); @@ -2020,7 +2026,10 @@ bool CLP(GraphicsStateGuardian):: begin_draw_primitives(const qpGeomVertexData *vertex_data) { DO_PSTATS_STUFF(_draw_primitive_pcollector.start()); - GraphicsStateGuardian::begin_draw_primitives(vertex_data); + if (!GraphicsStateGuardian::begin_draw_primitives(vertex_data)) { + return false; + } + nassertr(_vertex_data != (qpGeomVertexData *)NULL, false); CPTA_uchar array_data; int num_components; diff --git a/panda/src/gobj/qpgeomPrimitive.cxx b/panda/src/gobj/qpgeomPrimitive.cxx index 61c6048c03..7a6e4c572f 100644 --- a/panda/src/gobj/qpgeomPrimitive.cxx +++ b/panda/src/gobj/qpgeomPrimitive.cxx @@ -90,12 +90,13 @@ add_vertex(int vertex) { unsigned short short_vertex = vertex; nassertv((int)short_vertex == vertex); + clear_cache(); CDWriter cdata(_cycler); cdata->_vertices.push_back(short_vertex); if (cdata->_got_minmax) { cdata->_min_vertex = min(cdata->_min_vertex, short_vertex); - cdata->_max_vertex = max(cdata->_min_vertex, short_vertex); + cdata->_max_vertex = max(cdata->_max_vertex, short_vertex); } } @@ -107,6 +108,7 @@ add_vertex(int vertex) { //////////////////////////////////////////////////////////////////// void qpGeomPrimitive:: add_consecutive_vertices(int start, int num_vertices) { + clear_cache(); int end = (start + num_vertices) - 1; unsigned short short_start = start; unsigned short short_end = end; @@ -119,7 +121,7 @@ add_consecutive_vertices(int start, int num_vertices) { if (cdata->_got_minmax) { cdata->_min_vertex = min(cdata->_min_vertex, short_start); - cdata->_max_vertex = max(cdata->_min_vertex, short_end); + cdata->_max_vertex = max(cdata->_max_vertex, short_end); } } @@ -132,6 +134,7 @@ add_consecutive_vertices(int start, int num_vertices) { //////////////////////////////////////////////////////////////////// void qpGeomPrimitive:: close_primitive() { + clear_cache(); int num_vertices_per_primitive = get_num_vertices_per_primitive(); CDWriter cdata(_cycler); @@ -159,6 +162,7 @@ close_primitive() { //////////////////////////////////////////////////////////////////// void qpGeomPrimitive:: clear_vertices() { + clear_cache(); CDWriter cdata(_cycler); cdata->_vertices.clear(); cdata->_lengths.clear(); @@ -174,6 +178,7 @@ clear_vertices() { //////////////////////////////////////////////////////////////////// PTA_ushort qpGeomPrimitive:: modify_vertices() { + clear_cache(); CDWriter cdata(_cycler); return cdata->_vertices; } @@ -187,6 +192,7 @@ modify_vertices() { //////////////////////////////////////////////////////////////////// void qpGeomPrimitive:: set_vertices(PTA_ushort vertices) { + clear_cache(); CDWriter cdata(_cycler); cdata->_vertices = vertices; } @@ -205,6 +211,7 @@ set_vertices(PTA_ushort vertices) { //////////////////////////////////////////////////////////////////// PTA_int qpGeomPrimitive:: modify_lengths() { + clear_cache(); CDWriter cdata(_cycler); return cdata->_lengths; } @@ -223,6 +230,7 @@ modify_lengths() { //////////////////////////////////////////////////////////////////// void qpGeomPrimitive:: set_lengths(PTA_int lengths) { + clear_cache(); CDWriter cdata(_cycler); cdata->_lengths = lengths; } @@ -565,7 +573,7 @@ recompute_minmax() { ++ii; while (ii != cdata->_vertices.end()) { cdata->_min_vertex = min(cdata->_min_vertex, (*ii)); - cdata->_max_vertex = max(cdata->_min_vertex, (*ii)); + cdata->_max_vertex = max(cdata->_max_vertex, (*ii)); ++ii; } diff --git a/panda/src/gobj/qpgeomVertexArrayFormat.cxx b/panda/src/gobj/qpgeomVertexArrayFormat.cxx index 733a8b31eb..aef1b9f8d5 100644 --- a/panda/src/gobj/qpgeomVertexArrayFormat.cxx +++ b/panda/src/gobj/qpgeomVertexArrayFormat.cxx @@ -174,9 +174,6 @@ operator = (const qpGeomVertexArrayFormat ©) { //////////////////////////////////////////////////////////////////// qpGeomVertexArrayFormat:: ~qpGeomVertexArrayFormat() { - // Once registered, these things should not be deallocated. - nassertv(!_is_registered); - DataTypes::iterator dti; for (dti = _data_types.begin(); dti != _data_types.end(); ++dti) { delete (*dti); diff --git a/panda/src/gobj/qpgeomVertexData.cxx b/panda/src/gobj/qpgeomVertexData.cxx index 060de66918..ed6df1bb3c 100644 --- a/panda/src/gobj/qpgeomVertexData.cxx +++ b/panda/src/gobj/qpgeomVertexData.cxx @@ -455,25 +455,61 @@ set_data(int array, const qpGeomVertexDataType *data_type, switch (data_type->get_numeric_type()) { case qpGeomVertexDataType::NT_uint8: { - nassertv(num_values <= data_type->get_num_values()); - for (int i = 0; i < num_values; i++) { + int i = 0; + int min_values = min(num_values, data_type->get_num_values()); + while (i < min_values) { int value = (int)(data[i] * 255.0f); - *(unsigned char *)&array_data[element] = value; + array_data[element] = value; element += 1; + ++i; } + while (i < data_type->get_num_values()) { + array_data[element] = 0; + element += 1; + ++i; + } } break; case qpGeomVertexDataType::NT_packed_argb: { - nassertv(num_values == 4); - *(PN_uint32 *)&array_data[element] = pack_argb(data); + if (num_values == 4) { + *(PN_uint32 *)&array_data[element] = pack_argb(data); + } else { + // Elevate (or truncate) to 4 components. + float data4[4]; + memset(data4, 0, 4 * sizeof(float)); + memcpy(data4, data, min(4, num_values) * sizeof(float)); + *(PN_uint32 *)&array_data[element] = pack_argb(data4); + } } break; case qpGeomVertexDataType::NT_float: - nassertv(num_values == data_type->get_num_values()); - memcpy(&array_data[element], data, data_type->get_total_bytes()); + if (num_values == 4 && sizeof(float) == sizeof(PN_float32)) { + // The easy way: we can memcpy the data directly in. + memcpy(&array_data[element], data, data_type->get_total_bytes()); + + } else { + // Elevate or truncate to the right number of components. + int i = 0; + int min_values = min(num_values, data_type->get_num_values()); + while (i < min_values) { + *(PN_float32 *)&array_data[element] = data[i]; + element += sizeof(PN_float32); + ++i; + } + while (i < data_type->get_num_values()) { + if (i == 3 && data_type->get_num_values() == 4) { + *(PN_float32 *)&array_data[element] = 1.0f; + } else { + *(PN_float32 *)&array_data[element] = 0.0f; + } + element += sizeof(PN_float32); + ++i; + } + } + break; } } @@ -500,25 +536,56 @@ get_data(int array, const qpGeomVertexDataType *data_type, switch (data_type->get_numeric_type()) { case qpGeomVertexDataType::NT_uint8: { - nassertv(num_values <= data_type->get_num_values()); - for (int i = 0; i < num_values; i++) { + int i = 0; + int min_values = min(num_values, data_type->get_num_values()); + while (i < min_values) { int value = *(unsigned char *)&array_data[element]; element += 1; data[i] = (float)value / 255.0f; + ++i; + } + while (i < num_values) { + data[i] = 0.0f; + ++i; } } break; case qpGeomVertexDataType::NT_packed_argb: { - nassertv(num_values == 4); - unpack_argb(data, *(PN_uint32 *)&array_data[element]); + if (num_values == 4) { + unpack_argb(data, *(PN_uint32 *)&array_data[element]); + } else { + float data4[4]; + unpack_argb(data4, *(PN_uint32 *)&array_data[element]); + memset(data, 0, num_values * sizeof(float)); + memcpy(data, data4, min(num_values, 4) * sizeof(float)); + } } break; case qpGeomVertexDataType::NT_float: - nassertv(num_values <= data_type->get_num_values()); - memcpy(data, &array_data[element], num_values * sizeof(PN_float32)); + if (num_values == data_type->get_num_values() && + sizeof(float) == sizeof(PN_float32)) { + memcpy(data, &array_data[element], num_values * sizeof(PN_float32)); + } else { + int i = 0; + int min_values = min(num_values, data_type->get_num_values()); + while (i < min_values) { + data[i] = *(PN_float32 *)&array_data[element]; + element += sizeof(PN_float32); + ++i; + } + while (i < num_values) { + if (i == 3 && num_values == 4) { + data[i] = 1.0f; + } else { + data[i] = 0.0f; + } + ++i; + } + } + break; } } diff --git a/panda/src/pgraph/cullResult.cxx b/panda/src/pgraph/cullResult.cxx index 07187e4a9f..d5dd122f36 100644 --- a/panda/src/pgraph/cullResult.cxx +++ b/panda/src/pgraph/cullResult.cxx @@ -85,10 +85,6 @@ make_next() const { //////////////////////////////////////////////////////////////////// void CullResult:: add_object(CullableObject *object) { - // Munge vertices as needed for the GSG's requirements, and the - // object's current state. - object->munge_vertices(_gsg); - // Check to see if there's a special transparency setting. const RenderState *state = object->_state; nassertv(state != (const RenderState *)NULL); @@ -132,6 +128,7 @@ add_object(CullableObject *object) { get_dual_transparent_state_decals() : get_dual_transparent_state(); transparent_part->_state = state->compose(transparent_state); + transparent_part->munge_vertices(_gsg); CullBin *bin = get_bin(transparent_part->_state->get_bin_index()); nassertv(bin != (CullBin *)NULL); bin->add_object(transparent_part); @@ -155,6 +152,10 @@ add_object(CullableObject *object) { break; } } + + // Munge vertices as needed for the GSG's requirements, and the + // object's current state. + object->munge_vertices(_gsg); CullBin *bin = get_bin(object->_state->get_bin_index()); nassertv(bin != (CullBin *)NULL); diff --git a/panda/src/pgraph/cullableObject.I b/panda/src/pgraph/cullableObject.I index 2ce6ae678b..3eb8cb1c61 100644 --- a/panda/src/pgraph/cullableObject.I +++ b/panda/src/pgraph/cullableObject.I @@ -114,9 +114,12 @@ INLINE void CullableObject:: munge_vertices(GraphicsStateGuardianBase *gsg) { // Temporary test until the experimental Geom rewrite becomes the // actual Geom implementation. - if (_geom->is_exact_type(qpGeom::get_class_type())) { + if (_geom == (Geom *)NULL || _geom->is_exact_type(qpGeom::get_class_type())) { CPT(qpGeomMunger) munger = gsg->get_geom_munger(_state); _munged_data = munger->munge_data(((const qpGeom *)_geom.p())->get_vertex_data()); + if (_next != (CullableObject *)NULL) { + _next->munge_vertices(gsg); + } } }