diff --git a/panda/metalibs/pandaegg/Sources.pp b/panda/metalibs/pandaegg/Sources.pp index 499efba5b7..a2eb79ff1a 100644 --- a/panda/metalibs/pandaegg/Sources.pp +++ b/panda/metalibs/pandaegg/Sources.pp @@ -8,7 +8,7 @@ #define BUILDING_DLL BUILDING_PANDAEGG #define COMPONENT_LIBS \ - egg2sg egg builder + egg2pg egg2sg egg builder #define LOCAL_LIBS putil express #define OTHER_LIBS dtoolconfig dtool diff --git a/panda/src/builder/builder.cxx b/panda/src/builder/builder.cxx index f8b5a49bf8..3ba20b29e5 100644 --- a/panda/src/builder/builder.cxx +++ b/panda/src/builder/builder.cxx @@ -18,12 +18,14 @@ #include "builderFuncs.h" #include "builderMisc.h" -#include -#include -#include +#include "notify.h" +#include "namedNode.h" +#include "geomNode.h" #include "pmap.h" #include "builder.h" -#include +#include "renderRelation.h" +#include "pandaNode.h" +#include "qpgeomNode.h" //////////////////////////////////////////////////////////////////// @@ -92,6 +94,31 @@ public: const BuilderBucket *_bucket; }; +class qpNodeMap : public Namable { +public: + qpNodeMap(PandaNode *node, const BuilderBucket *bucket) + : _node(node), _bucket(bucket) { } + + bool operator < (const qpNodeMap &other) const { + if (_node != other._node) { + return _node < other._node; + } + if (_bucket->get_name() != other._bucket->get_name()) { + return _bucket->get_name() < other._bucket->get_name(); + } + return (_bucket->_state < other._bucket->_state); + } + + PandaNode *_node; + + // Although a bucket pointer is stored here in the NodeMap class, + // you should not use it except to extract the name and/or the + // _trans member. Remember, this bucket pointer stands for any of + // possibly several bucket pointers, all different, except that they + // share the same name. + const BuilderBucket *_bucket; +}; + //////////////////////////////////////////////////////////////////// @@ -215,6 +242,126 @@ build(const string &default_name) { return base_geom_node; } +//////////////////////////////////////////////////////////////////// +// Function: Builder::build +// Access: Public +// Description: Creates Geoms for all the primitives added to all +// buckets, and adds them where appropriate to place +// them in the scene graph under their respective +// parents, and/or returns a single GeomNode that +// contains all geometry whose bucket did not reference +// a particular scene graph node to parent them to. +// +// If a bucket's _node pointer was a GeomNode, the +// geometry will be added directly to that node. If the +// _node pointer was any other kind of node, a GeomNode +// will be created and parented to that node, and its +// name will be the name of the bucket. In this case, +// the name of the bucket can also be used to different +// nodes: if two buckets reference the same node, but +// have different names, then two different GeomNodes +// are created, one with each name. +//////////////////////////////////////////////////////////////////// +qpGeomNode *Builder:: +qpbuild(const string &default_name) { + typedef pmap GeomNodeMap; + GeomNodeMap geom_nodes; + + // First, build all the Geoms and create GeomNodes for them. Each + // unique Node gets its own GeomNode. If the Node is itself a + // GeomNode, that GeomNode is used directly. + Buckets::iterator i; + for (i = _buckets.begin(); + i != _buckets.end(); + ++i) { + BuilderBucket *bucket = (*i).get_bucket(); + PandaNode *node = bucket->_qpnode; + // const string &name = bucket->get_name(); + qpGeomNode *geom_node = NULL; + + if (node!=NULL && node->is_of_type(qpGeomNode::get_class_type())) { + // The node is a GeomNode. In this case, we simply use that + // node. We can't separate them out by name in this case; we'll + // just assign to it the first nonempty name we encounter. + geom_node = DCAST(qpGeomNode, node); + + // Since the caller already created this GeomNode and passed it + // in, we'll leave it up to the caller to name the node and set + // up the state transitions leading into it. + + } else { + // The node is not a GeomNode, so look it up in the map. + GeomNodeMap::iterator f = geom_nodes.find(qpNodeMap(node, bucket)); + if (f != geom_nodes.end()) { + geom_node = (*f).second; + + } else { + // No such node/name combination. Create a new one. + geom_node = bucket->qpmake_geom_node(); + if (geom_node != NULL) { + geom_nodes[qpNodeMap(node, bucket)] = geom_node; + } + } + } + + if (geom_node != NULL) { + (*i).build(geom_node); + } + } + + // Now go through and parent the geom_nodes under their respective + // group nodes. Save out the geom_node associated with a NULL Node; + // this one is returned from this function. + + qpGeomNode *base_geom_node = NULL; + + GeomNodeMap::iterator gi; + + for (gi = geom_nodes.begin(); + gi != geom_nodes.end(); + ++gi) { + const qpNodeMap &nm = (*gi).first; + qpGeomNode *geom_node = (*gi).second; + + PandaNode *node = nm._node; + const string &name = nm._bucket->get_name(); + CPT(RenderState) state = nm._bucket->_state; + + // Assign the name to the geom, if it doesn't have one already. + if (!geom_node->has_name()) { + if (!name.empty()) { + geom_node->set_name(name); + + } else if (!default_name.empty()) { + geom_node->set_name(default_name); + } + } + + // Only reparent the geom_node if it has no parent already. + int num_parents = geom_node->get_num_parents(); + if (num_parents == 0) { + if (geom_node->get_num_geoms() == 0) { + // If there was nothing added, never mind. + delete geom_node; + + } else if (node==NULL) { + nassertr(base_geom_node == NULL, NULL); + base_geom_node = geom_node; + + } else { + node->add_child(geom_node); + // Now, this is our only opportunity to apply the scene-graph + // state specified in the bucket to the node: we have created + // our own geom_node for these buckets, and we have parented + // it to the scene graph. + geom_node->set_state(state); + } + } + } + + return base_geom_node; +} + //////////////////////////////////////////////////////////////////// // Function: Builder::add_bucket diff --git a/panda/src/builder/builder.h b/panda/src/builder/builder.h index 4cc0b6c286..93a5d968e2 100644 --- a/panda/src/builder/builder.h +++ b/panda/src/builder/builder.h @@ -176,6 +176,7 @@ class GeomNode; +class qpGeomNode; /////////////////////////////////////////////////////////////////// @@ -196,6 +197,7 @@ public: const BuilderPrimI &prim); GeomNode *build(const string &default_name = ""); + qpGeomNode *qpbuild(const string &default_name = ""); protected: void add_bucket(const BuilderBucket &bucket); diff --git a/panda/src/builder/builderBucket.cxx b/panda/src/builder/builderBucket.cxx index fc844da68c..46c21e271b 100644 --- a/panda/src/builder/builderBucket.cxx +++ b/panda/src/builder/builderBucket.cxx @@ -21,8 +21,9 @@ #include "builderBucket.h" #include "builderFuncs.h" #include "builderMisc.h" -#include -#include +#include "namedNode.h" +#include "geomNode.h" +#include "qpgeomNode.h" BuilderBucket *BuilderBucket::_default_bucket = NULL; @@ -36,6 +37,7 @@ BuilderBucket *BuilderBucket::_default_bucket = NULL; BuilderBucket:: BuilderBucket() { _node = NULL; + _qpnode = NULL; (*this) = (*get_default_bucket()); } @@ -48,6 +50,7 @@ BuilderBucket() { BuilderBucket:: BuilderBucket(const BuilderBucket ©) { _node = NULL; + _qpnode = NULL; (*this) = copy; } @@ -69,10 +72,12 @@ operator = (const BuilderBucket ©) { set_colors(copy._colors); _node = copy._node; + _qpnode = copy._qpnode; _drawBin = copy._drawBin; _drawOrder = copy._drawOrder; _trans = copy._trans; + _state = copy._state; return *this; } @@ -117,6 +122,20 @@ make_geom_node() { return new GeomNode; } +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucket::qpmake_geom_node +// Access: Public, Virtual +// Description: Called by the builder when it is time to create a new +// GeomNode. This function should allocate and return a +// new GeomNode suitable for adding geometry to. You +// may redefine it to return a subclass of GeomNode, or +// to do some initialization to the node. +//////////////////////////////////////////////////////////////////// +qpGeomNode *BuilderBucket:: +qpmake_geom_node() { + return new qpGeomNode(""); +} + //////////////////////////////////////////////////////////////////// // Function: BuilderBucket::done_geom // Access: Public, Virtual @@ -231,6 +250,7 @@ output(ostream &out) const { BuilderBucket:: BuilderBucket(int) { _node = NULL; + _qpnode = NULL; _drawBin = -1; _drawOrder = 0; @@ -251,4 +271,6 @@ BuilderBucket(int) { _consider_fans = true; _max_tfan_angle = 40.0; _min_tfan_tris = 0; + + _state = RenderState::make_empty(); } diff --git a/panda/src/builder/builderBucket.h b/panda/src/builder/builderBucket.h index f0bb9511a1..417dbb1985 100644 --- a/panda/src/builder/builderBucket.h +++ b/panda/src/builder/builderBucket.h @@ -19,25 +19,28 @@ #ifndef BUILDERBUCKET_H #define BUILDERBUCKET_H -#include +#include "pandabase.h" #include "builderProperties.h" -#include -#include -#include -#include -#include -#include -#include -#include +#include "namable.h" +#include "pointerToArray.h" +#include "luse.h" +#include "nodeTransitions.h" +#include "pta_Vertexf.h" +#include "pta_Normalf.h" +#include "pta_Colorf.h" +#include "pta_TexCoordf.h" +#include "renderState.h" -#include +#include "stdlib.h" class NamedNode; class Geom; class GeomNode; +class PandaNode; +class qpGeomNode; /////////////////////////////////////////////////////////////////// @@ -67,6 +70,7 @@ public: virtual BuilderBucket *make_copy() const; virtual GeomNode *make_geom_node(); + virtual qpGeomNode *qpmake_geom_node(); virtual Geom *done_geom(Geom *geom); virtual bool operator < (const BuilderBucket &other) const; @@ -88,11 +92,13 @@ public: virtual void output(ostream &out) const; NamedNode *_node; + PandaNode *_qpnode; short _drawBin; unsigned int _drawOrder; NodeTransitions _trans; + CPT(RenderState) _state; protected: PTA_Vertexf _coords; diff --git a/panda/src/builder/builderBucketNode.cxx b/panda/src/builder/builderBucketNode.cxx index 62ebb89eb5..08ee63bd71 100644 --- a/panda/src/builder/builderBucketNode.cxx +++ b/panda/src/builder/builderBucketNode.cxx @@ -17,9 +17,11 @@ //////////////////////////////////////////////////////////////////// #include "builderFuncs.h" -#include #include "builderBucketNode.h" +#include "geomNode.h" +#include "qpgeomNode.h" + //////////////////////////////////////////////////////////////////// // Function: BuilderBucketNode::add_prim // Access: Public @@ -98,3 +100,54 @@ build(GeomNode *geom_node) const { return count; } + +//////////////////////////////////////////////////////////////////// +// Function: BuilderBucketNode::build +// Access: Public +// Description: Builds all the geometry assigned to this particular +// bucket, and assigns it to the indicated GeomNode. +// Returns the number of Geoms created. +//////////////////////////////////////////////////////////////////// +int BuilderBucketNode:: +build(qpGeomNode *geom_node) const { + int count = 0; + + { + // First, the nonindexed. + Prims::const_iterator pi, last_pi; + last_pi = _prims.begin(); + + for (pi = _prims.begin(); + pi != _prims.end(); + ++pi) { + if ((*last_pi) < (*pi)) { + count += mesh_and_build(last_pi, pi, *_bucket, geom_node, + (BuilderPrim *)0); + last_pi = pi; + } + } + count += mesh_and_build(last_pi, pi, *_bucket, geom_node, + (BuilderPrim *)0); + } + + { + // Then, the indexed. + IPrims::const_iterator pi, last_pi; + last_pi = _iprims.begin(); + + for (pi = _iprims.begin(); + pi != _iprims.end(); + ++pi) { + if ((*last_pi) < (*pi)) { + count += mesh_and_build(last_pi, pi, *_bucket, geom_node, + (BuilderPrimI *)0); + last_pi = pi; + } + } + count += mesh_and_build(last_pi, pi, *_bucket, geom_node, + (BuilderPrimI *)0); + } + + return count; +} + diff --git a/panda/src/builder/builderBucketNode.h b/panda/src/builder/builderBucketNode.h index 7720cb0cf3..fed672f62c 100644 --- a/panda/src/builder/builderBucketNode.h +++ b/panda/src/builder/builderBucketNode.h @@ -27,6 +27,7 @@ #include "pset.h" class GeomNode; +class qpGeomNode; /////////////////////////////////////////////////////////////////// @@ -57,6 +58,7 @@ public: INLINE bool operator != (const BuilderBucketNode &other) const; int build(GeomNode *geom_node) const; + int build(qpGeomNode *geom_node) const; protected: typedef pmultiset > Prims; diff --git a/panda/src/builder/builderFuncs.I b/panda/src/builder/builderFuncs.I index 929980b883..edb07ee861 100644 --- a/panda/src/builder/builderFuncs.I +++ b/panda/src/builder/builderFuncs.I @@ -23,7 +23,6 @@ #include #include -#include #include @@ -485,7 +484,7 @@ expand(const PrimType &prim, BuilderBucket &bucket, OutputIterator result) { // creates corresponding geometry for them in the // indicated GeomNode. //////////////////////////////////////////////////////////////////// -template +template static int build_geoms(InputIterator first, InputIterator last, BuilderBucket &bucket, GeomNode *geom_node, @@ -796,7 +795,7 @@ public: // to infer the PrimType (BuilderPrim or BuilderPrimI) // from the iterator's value type, and template on that. //////////////////////////////////////////////////////////////////// -template +template static int __mesh_and_build(InputIterator first, InputIterator last, BuilderBucket &bucket, GeomNode *geom_node, @@ -879,7 +878,7 @@ __mesh_and_build(InputIterator first, InputIterator last, // runs them through the mesher if specified by the // bucket, and builds them into the indicated GeomNode. //////////////////////////////////////////////////////////////////// -template +template int mesh_and_build(InputIterator first, InputIterator last, BuilderBucket &bucket, GeomNode *geom_node, diff --git a/panda/src/builder/builderFuncs.h b/panda/src/builder/builderFuncs.h index 3f382b673b..6d3e3860fd 100644 --- a/panda/src/builder/builderFuncs.h +++ b/panda/src/builder/builderFuncs.h @@ -25,7 +25,6 @@ #include class BuilderBucket; -class GeomNode; //////////////////////////////////////////////////////////////////// @@ -57,7 +56,7 @@ expand(const PrimType &prim, BuilderBucket &bucket, // runs them through the mesher if specified by the // bucket, and builds them into the indicated GeomNode. //////////////////////////////////////////////////////////////////// -template +template int mesh_and_build(InputIterator first, InputIterator last, BuilderBucket &bucket, GeomNode *geom_node); diff --git a/panda/src/builder/builderNormalVisualizer.cxx b/panda/src/builder/builderNormalVisualizer.cxx index 62c8f77dbf..04efef8b7b 100644 --- a/panda/src/builder/builderNormalVisualizer.cxx +++ b/panda/src/builder/builderNormalVisualizer.cxx @@ -18,6 +18,8 @@ #include "builderFuncs.h" #include "builderNormalVisualizer.h" +#include "geomNode.h" +#include "qpgeomNode.h" #ifdef SUPPORT_SHOW_NORMALS @@ -61,6 +63,12 @@ show_normals(GeomNode *node) { mesh_and_build(_lines.begin(), _lines.end(), _bucket, node, (BuilderPrim *)0); } +void BuilderNormalVisualizer:: +show_normals(qpGeomNode *node) { + // Ok, now we've got a bunch of normals saved up; create some geometry. + mesh_and_build(_lines.begin(), _lines.end(), _bucket, node, (BuilderPrim *)0); +} + void BuilderNormalVisualizer:: add_normal(const BuilderV ¢er, const BuilderN &normal) { BuilderV to = center + normal * _bucket._normal_scale; diff --git a/panda/src/builder/builderNormalVisualizer.h b/panda/src/builder/builderNormalVisualizer.h index f269fe56d0..f4a607fdf4 100644 --- a/panda/src/builder/builderNormalVisualizer.h +++ b/panda/src/builder/builderNormalVisualizer.h @@ -48,6 +48,7 @@ public: void add_prim(const BuilderPrimI &prim); void show_normals(GeomNode *node); + void show_normals(qpGeomNode *node); private: void add_normal(const BuilderV ¢er, const BuilderN &normal); diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index 1bbd5704fa..bedee4d0f5 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -20,6 +20,7 @@ #include "pipeline.h" #include "drawCullHandler.h" #include "qpcullTraverser.h" +#include "clockObject.h" //////////////////////////////////////////////////////////////////// // Function: GraphicsEngine::Constructor @@ -77,6 +78,10 @@ remove_window(GraphicsWindow *window) { void GraphicsEngine:: render_frame() { cull_and_draw_together(); + + // **** This doesn't belong here; it really belongs in the Pipeline, + // but here it is for now. + ClockObject::get_global_clock()->tick(); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/egg2pg/config_egg2pg.cxx b/panda/src/egg2pg/config_egg2pg.cxx new file mode 100644 index 0000000000..89913ef005 --- /dev/null +++ b/panda/src/egg2pg/config_egg2pg.cxx @@ -0,0 +1,107 @@ +// Filename: config_egg2pg.cxx +// Created by: drose (26Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, 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://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "config_egg2pg.h" + +#include "dconfig.h" +#include "get_config_path.h" + +ConfigureDef(config_egg2pg); +NotifyCategoryDef(egg2pg, ""); + +bool egg_mesh = config_egg2pg.GetBool("egg-mesh", true); +bool egg_retesselate_coplanar = config_egg2pg.GetBool("egg-retesselate-coplanar", true); +bool egg_unroll_fans = config_egg2pg.GetBool("egg-unroll-fans", true); +bool egg_show_tstrips = config_egg2pg.GetBool("egg-show-tstrips", false); +bool egg_show_qsheets = config_egg2pg.GetBool("egg-show-qsheets", false); +bool egg_show_quads = config_egg2pg.GetBool("egg-show-quads", false); +bool egg_false_color = (egg_show_tstrips | egg_show_qsheets | egg_show_quads); +bool egg_show_normals = config_egg2pg.GetBool("egg-show-normals", false); +double egg_normal_scale = config_egg2pg.GetDouble("egg-normal-scale", 1.0); +bool egg_subdivide_polys = config_egg2pg.GetBool("egg-subdivide-polys", true); +bool egg_consider_fans = config_egg2pg.GetBool("egg-consider-fans", true); +double egg_max_tfan_angle = config_egg2pg.GetDouble("egg-max-tfan-angle", 40.0); +int egg_min_tfan_tris = config_egg2pg.GetInt("egg-min-tfan-tris", 4); +double egg_coplanar_threshold = config_egg2pg.GetDouble("egg-coplanar-threshold", 0.01); +bool egg_ignore_mipmaps = config_egg2pg.GetBool("egg-ignore-mipmaps", false); +bool egg_ignore_filters = config_egg2pg.GetBool("egg-ignore-filters", false); +bool egg_ignore_clamp = config_egg2pg.GetBool("egg-ignore-clamp", false); +bool egg_always_decal_textures = config_egg2pg.GetBool("egg-always-decal-textures", false); +bool egg_ignore_decals = config_egg2pg.GetBool("egg-ignore-decals", false); +bool egg_flatten = config_egg2pg.GetBool("egg-flatten", true); + +// It is almost always a bad idea to set this true. +bool egg_flatten_siblings = config_egg2pg.GetBool("egg-flatten-siblings", false); + +bool egg_show_collision_solids = config_egg2pg.GetBool("egg-show-collision-solids", false); + +// When this is true, keep texture pathnames exactly the same as they +// appeared in the egg file, in particular leaving them as relative +// paths, rather than letting them reflect the full path at which they +// were found. This is particularly useful when generating bam files. +// However, if the same texture is named by two different relative +// paths, these will still be collapsed into one texture (using one of +// the relative paths, chosen arbitrarily). +bool egg_keep_texture_pathnames = config_egg2pg.GetBool("egg-keep-texture-pathnames", false); + +// When this is true, a entry appearing in an egg file +// will load a ClassicNurbsCurve object instead of the default, a +// NurbsCurve object. This only makes a difference when the NURBS++ +// library is available, in which case the default, NurbsCurve, is +// actually a NurbsPPCurve object. +bool egg_load_classic_nurbs_curves = config_egg2pg.GetBool("egg-load-classic-nurbs-curves", false); + +// When this is true, certain kinds of recoverable errors (not syntax +// errors) in an egg file will be allowed and ignored when an egg file +// is loaded. When it is false, only perfectly pristine egg files may +// be loaded. +bool egg_accept_errors = config_egg2pg.GetBool("egg-accept-errors", true); + +CoordinateSystem egg_coordinate_system; + +ConfigureFn(config_egg2pg) { + init_libegg2pg(); +} + +//////////////////////////////////////////////////////////////////// +// Function: init_libegg2pg +// Description: Initializes the library. This must be called at +// least once before any of the functions or classes in +// this library can be used. Normally it will be +// called by the static initializers and need not be +// called explicitly, but special cases exist. +//////////////////////////////////////////////////////////////////// +void +init_libegg2pg() { + static bool initialized = false; + if (initialized) { + return; + } + initialized = true; + + string csstr = config_egg2pg.GetString("egg-coordinate-system", "default"); + CoordinateSystem cs = parse_coordinate_system_string(csstr); + + if (cs == CS_invalid) { + egg2pg_cat.error() + << "Unexpected egg-coordinate-system string: " << csstr << "\n"; + cs = CS_default; + } + egg_coordinate_system = (cs == CS_default) ? + default_coordinate_system : cs; +} diff --git a/panda/src/egg2pg/config_egg2pg.h b/panda/src/egg2pg/config_egg2pg.h new file mode 100644 index 0000000000..e19be5ae85 --- /dev/null +++ b/panda/src/egg2pg/config_egg2pg.h @@ -0,0 +1,60 @@ +// Filename: config_egg2pg.h +// Created by: drose (26Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, 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://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_EGG2PG_H +#define CONFIG_EGG2PG_H + +#include "pandabase.h" + +#include "coordinateSystem.h" +#include "notifyCategoryProxy.h" +#include "dconfig.h" + +ConfigureDecl(config_egg2pg, EXPCL_PANDAEGG, EXPTP_PANDAEGG); +NotifyCategoryDecl(egg2pg, EXPCL_PANDAEGG, EXPTP_PANDAEGG); + +extern EXPCL_PANDAEGG bool egg_mesh; +extern EXPCL_PANDAEGG bool egg_retesselate_coplanar; +extern EXPCL_PANDAEGG bool egg_unroll_fans; +extern EXPCL_PANDAEGG bool egg_show_tstrips; +extern EXPCL_PANDAEGG bool egg_show_qsheets; +extern EXPCL_PANDAEGG bool egg_show_quads; +extern EXPCL_PANDAEGG bool egg_false_color; +extern EXPCL_PANDAEGG bool egg_show_normals; +extern EXPCL_PANDAEGG double egg_normal_scale; +extern EXPCL_PANDAEGG bool egg_subdivide_polys; +extern EXPCL_PANDAEGG bool egg_consider_fans; +extern EXPCL_PANDAEGG double egg_max_tfan_angle; +extern EXPCL_PANDAEGG int egg_min_tfan_tris; +extern EXPCL_PANDAEGG double egg_coplanar_threshold; +extern EXPCL_PANDAEGG CoordinateSystem egg_coordinate_system; +extern EXPCL_PANDAEGG bool egg_ignore_mipmaps; +extern EXPCL_PANDAEGG bool egg_ignore_filters; +extern EXPCL_PANDAEGG bool egg_ignore_clamp; +extern EXPCL_PANDAEGG bool egg_always_decal_textures; +extern EXPCL_PANDAEGG bool egg_ignore_decals; +extern EXPCL_PANDAEGG bool egg_flatten; +extern EXPCL_PANDAEGG bool egg_flatten_siblings; +extern EXPCL_PANDAEGG bool egg_show_collision_solids; +extern EXPCL_PANDAEGG bool egg_keep_texture_pathnames; +extern EXPCL_PANDAEGG bool egg_load_classic_nurbs_curves; +extern EXPCL_PANDAEGG bool egg_accept_errors; + +extern EXPCL_PANDAEGG void init_libegg2pg(); + +#endif diff --git a/panda/src/egg2pg/egg2sg_composite1.cxx b/panda/src/egg2pg/egg2sg_composite1.cxx new file mode 100644 index 0000000000..0dec6bfa70 --- /dev/null +++ b/panda/src/egg2pg/egg2sg_composite1.cxx @@ -0,0 +1,2 @@ +#include "config_egg2pg.cxx" +#include "qpeggLoader.cxx" diff --git a/panda/src/egg2pg/qpeggLoader.cxx b/panda/src/egg2pg/qpeggLoader.cxx new file mode 100644 index 0000000000..78157caf4b --- /dev/null +++ b/panda/src/egg2pg/qpeggLoader.cxx @@ -0,0 +1,2026 @@ +// Filename: qpqpEggLoader.cxx +// Created by: drose (26Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, 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://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "pandabase.h" + +#include "qpeggLoader.h" +#include "config_egg2pg.h" +#include "renderState.h" +#include "transformState.h" +#include "textureAttrib.h" +#include "texturePool.h" +#include "qpgeomNode.h" +#include "string_utils.h" +#include "eggPrimitive.h" +#include "eggPoint.h" +#include "eggTextureCollection.h" +#include "eggNurbsCurve.h" +#include "eggGroupNode.h" +#include "eggGroup.h" +#include "eggPolygon.h" +#include "eggBin.h" +#include "eggTable.h" + +#include +#include + +/* +// This class is used in make_node(EggBin *) to sort LOD instances in +// order by switching distance. +class LODInstance { +public: + LODInstance(EggNode *egg_node, RenderRelation *arc); + bool operator < (const LODInstance &other) const { + return _d->_switch_in < other._d->_switch_in; + } + + RenderRelation *_arc; + const EggSwitchConditionDistance *_d; +}; + +LODInstance:: +LODInstance(EggNode *egg_node, RenderRelation *arc) { + assert(arc != NULL); + _arc = arc; + + // We expect this egg node to be an EggGroup with an LOD + // specification. That's what the EggBinner collected together, + // after all. + EggGroup *egg_group = DCAST(EggGroup, egg_node); + assert(egg_group->has_lod()); + const EggSwitchCondition &sw = egg_group->get_lod(); + + // For now, this is the only kind of switch condition there is. + _d = DCAST(EggSwitchConditionDistance, &sw); +} +*/ + + +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +qpEggLoader:: +qpEggLoader() { + // We need to enforce whatever coordinate system the user asked for. + _data.set_coordinate_system(egg_coordinate_system); + _error = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +qpEggLoader:: +qpEggLoader(const EggData &data) : + _data(data) +{ + _error = false; +} + + +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::build_graph +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void qpEggLoader:: +build_graph() { + // _deferred_arcs.clear(); + + /* + // First, bin up the LOD nodes. + EggBinner binner; + binner.make_bins(&_data); + */ + + // Then load up all of the textures. + load_textures(); + + // Now build up the scene graph. + _root = new PandaNode(_data.get_egg_filename().get_basename()); + make_node(&_data, _root); + _builder.qpbuild(); + + /* + reset_directs(); + reparent_decals(); + + apply_deferred_arcs(_root); + */ +} + + +/* +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::reparent_decals +// Access: Public +// Description: For each node representing a decal base geometry +// (i.e. a node corresponding to an EggGroup with the +// decal flag set), move all of its nested geometry +// directly below the GeomNode representing the group. +//////////////////////////////////////////////////////////////////// +void qpEggLoader:: +reparent_decals() { + Decals::const_iterator di; + for (di = _decals.begin(); di != _decals.end(); ++di) { + RenderRelation *arc = (*di); + nassertv(arc != (RenderRelation *)NULL); + PandaNode *node = DCAST(PandaNode, arc->get_child()); + nassertv(node != (PandaNode *)NULL); + + // First, search for the GeomNode. + GeomNode *geom = NULL; + int num_children = + node->get_num_children(RenderRelation::get_class_type()); + for (int i = 0; i < num_children; i++) { + NodeRelation *child_arc = + node->get_child(RenderRelation::get_class_type(), i); + nassertv(child_arc != (NodeRelation *)NULL); + Node *child = child_arc->get_child(); + nassertv(child != (Node *)NULL); + + if (child->is_of_type(GeomNode::get_class_type())) { + if (geom != (GeomNode *)NULL) { + // Oops, too many GeomNodes. + egg2pg_cat.error() + << "Decal onto " << node->get_name() + << " uses base geometry with multiple states.\n"; + _error = true; + } + DCAST_INTO_V(geom, child); + } + } + + if (geom == (GeomNode *)NULL) { + // No children were GeomNodes. + egg2pg_cat.error() + << "Ignoring decal onto " << node->get_name() + << "; no geometry within group.\n"; + _error = true; + } else { + // Now reparent all of the non-GeomNodes to this node. We have + // to be careful so we don't get lost as we self-modify this + // list. + int i = 0; + while (i < num_children) { + NodeRelation *child_arc = + node->get_child(RenderRelation::get_class_type(), i); + nassertv(child_arc != (NodeRelation *)NULL); + Node *child = child_arc->get_child(); + nassertv(child != (Node *)NULL); + + if (child->is_of_type(GeomNode::get_class_type())) { + i++; + } else { + child_arc->change_parent(geom); + num_children--; + } + } + } + } +} +*/ + +/* +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::reset_directs +// Access: Public +// Description: This applies to all of the nodes marked with the +// "render" flag, i.e. direct rendering of a subgraph, +// in depth-first order, as opposed to state-sorting +// within the subgraph. For each such node, it moves +// all the transitions from the first GeomNode under +// that node up to the node itself, just so we'll be +// able to state-sort at least the tops of the +// subgraphs. +//////////////////////////////////////////////////////////////////// +void qpEggLoader:: +reset_directs() { + Directs::const_iterator di; + for (di = _directs.begin(); di != _directs.end(); ++di) { + RenderRelation *arc = (*di); + nassertv(arc != (RenderRelation *)NULL); + PandaNode *node = DCAST(PandaNode, arc->get_child()); + nassertv(node != (PandaNode *)NULL); + + // First, search for the first GeomNode. + GeomNode *geom = NULL; + NodeRelation *child_arc = NULL; + + int num_children = + node->get_num_children(RenderRelation::get_class_type()); + for (int i = 0; i < num_children && geom == (GeomNode *)NULL; i++) { + child_arc = node->get_child(RenderRelation::get_class_type(), i); + nassertv(child_arc != (NodeRelation *)NULL); + Node *child = child_arc->get_child(); + nassertv(child != (Node *)NULL); + + if (child->is_of_type(GeomNode::get_class_type())) { + DCAST_INTO_V(geom, child); + } + } + + if (geom != (GeomNode *)NULL) { + // Now copy all of the GeomNode's transitions up to its parent. + nassertv(child_arc != (NodeRelation *)NULL); + arc->copy_transitions_from(child_arc); + } + } +} +*/ + + +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::make_nonindexed_primitive +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void qpEggLoader:: +make_nonindexed_primitive(EggPrimitive *egg_prim, PandaNode *parent, + const LMatrix4d *transform) { + BuilderBucket bucket; + setup_bucket(bucket, parent, egg_prim); + + LMatrix4d mat; + + if (transform != NULL) { + mat = (*transform); + } else { + mat = egg_prim->get_vertex_to_node(); + } + + BuilderPrim bprim; + bprim.set_type(BPT_poly); + if (egg_prim->is_of_type(EggPoint::get_class_type())) { + bprim.set_type(BPT_point); + } + + if (egg_prim->has_normal()) { + Normald norm = egg_prim->get_normal() * mat; + norm.normalize(); + bprim.set_normal(LCAST(float, norm)); + } + if (egg_prim->has_color() && !egg_false_color) { + bprim.set_color(egg_prim->get_color()); + } + + bool has_vert_color = true; + EggPrimitive::const_iterator vi; + for (vi = egg_prim->begin(); vi != egg_prim->end(); ++vi) { + EggVertex *egg_vert = *vi; + BuilderVertex bvert(LCAST(float, egg_vert->get_pos3() * mat)); + + if (egg_vert->has_normal()) { + Normald norm = egg_vert->get_normal() * mat; + norm.normalize(); + bvert.set_normal(LCAST(float, norm)); + } + if (egg_vert->has_color() && !egg_false_color) { + bvert.set_color(egg_vert->get_color()); + } else { + // If any vertex doesn't have a color, we can't use any of the + // vertex colors. + has_vert_color = false; + } + if (egg_vert->has_uv()) { + TexCoordd uv = egg_vert->get_uv(); + if (egg_prim->has_texture() && + egg_prim->get_texture()->has_transform()) { + // If we have a texture matrix, apply it. + uv = uv * egg_prim->get_texture()->get_transform(); + } + bvert.set_texcoord(LCAST(float, uv)); + } + + bprim.add_vertex(bvert); + } + + // Finally, if the primitive didn't have a color, and it didn't have + // vertex color, make it white. + if (!egg_prim->has_color() && !has_vert_color && !egg_false_color) { + bprim.set_color(Colorf(1.0, 1.0, 1.0, 1.0)); + } + + _builder.add_prim(bucket, bprim); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::make_indexed_primitive +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void qpEggLoader:: +make_indexed_primitive(EggPrimitive *egg_prim, PandaNode *parent, + const LMatrix4d *transform, + ComputedVerticesMaker &_comp_verts_maker) { + /* + BuilderBucket bucket; + setup_bucket(bucket, parent, egg_prim); + + bucket.set_coords(_comp_verts_maker._coords); + bucket.set_normals(_comp_verts_maker._norms); + bucket.set_texcoords(_comp_verts_maker._texcoords); + bucket.set_colors(_comp_verts_maker._colors); + + LMatrix4d mat; + + if (transform != NULL) { + mat = (*transform); + } else { + mat = egg_prim->get_vertex_to_node(); + } + + BuilderPrimI bprim; + bprim.set_type(BPT_poly); + if (egg_prim->is_of_type(EggPoint::get_class_type())) { + bprim.set_type(BPT_point); + } + + if (egg_prim->has_normal()) { + // Define the transform space of the polygon normal. This will be + // the average of all the vertex transform spaces. + _comp_verts_maker.begin_new_space(); + EggPrimitive::const_iterator vi; + for (vi = egg_prim->begin(); vi != egg_prim->end(); ++vi) { + EggVertex *egg_vert = *vi; + _comp_verts_maker.add_vertex_joints(egg_vert, egg_prim); + } + _comp_verts_maker.mark_space(); + + int nindex = + _comp_verts_maker.add_normal(egg_prim->get_normal(), + egg_prim->_dnormals, mat); + + bprim.set_normal(nindex); + } + + if (egg_prim->has_color() && !egg_false_color) { + int cindex = + _comp_verts_maker.add_color(egg_prim->get_color(), + egg_prim->_drgbas); + bprim.set_color(cindex); + } + + bool has_vert_color = true; + EggPrimitive::const_iterator vi; + for (vi = egg_prim->begin(); vi != egg_prim->end(); ++vi) { + EggVertex *egg_vert = *vi; + + // Set up the ComputedVerticesMaker for the coordinate space of + // the vertex. + _comp_verts_maker.begin_new_space(); + _comp_verts_maker.add_vertex_joints(egg_vert, egg_prim); + _comp_verts_maker.mark_space(); + + int vindex = + _comp_verts_maker.add_vertex(egg_vert->get_pos3(), + egg_vert->_dxyzs, mat); + BuilderVertexI bvert(vindex); + + if (egg_vert->has_normal()) { + int nindex = + _comp_verts_maker.add_normal(egg_vert->get_normal(), + egg_vert->_dnormals, + mat); + bvert.set_normal(nindex); + } + + if (egg_vert->has_color() && !egg_false_color) { + int cindex = + _comp_verts_maker.add_color(egg_vert->get_color(), + egg_vert->_drgbas); + bvert.set_color(cindex); + } else { + // If any vertex doesn't have a color, we can't use any of the + // vertex colors. + has_vert_color = false; + } + + if (egg_vert->has_uv()) { + TexCoordd uv = egg_vert->get_uv(); + LMatrix3d mat; + + if (egg_prim->has_texture() && + egg_prim->get_texture()->has_transform()) { + // If we have a texture matrix, apply it. + mat = egg_prim->get_texture()->get_transform(); + } else { + mat = LMatrix3d::ident_mat(); + } + + int tindex = + _comp_verts_maker.add_texcoord(uv, egg_vert->_duvs, mat); + bvert.set_texcoord(tindex); + } + + bprim.add_vertex(bvert); + } + + // Finally, if the primitive didn't have a color, and it didn't have + // vertex color, make it white. + if (!egg_prim->has_color() && !has_vert_color && !egg_false_color) { + int cindex = + _comp_verts_maker.add_color(Colorf(1.0, 1.0, 1.0, 1.0), + EggMorphColorList()); + bprim.set_color(cindex); + } + + _builder.add_prim(bucket, bprim); + */ +} + +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::load_textures +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void qpEggLoader:: +load_textures() { + // First, collect all the textures that are referenced. + EggTextureCollection tc; + tc.find_used_textures(&_data); + + // Collapse the textures down by filename only. Should we also + // differentiate by attributes? Maybe. + EggTextureCollection::TextureReplacement replace; + tc.collapse_equivalent_textures(EggTexture::E_complete_filename, + replace); + + EggTextureCollection::iterator ti; + for (ti = tc.begin(); ti != tc.end(); ++ti) { + PT(EggTexture) egg_tex = (*ti); + + TextureDef def; + if (load_texture(def, egg_tex)) { + // Now associate the pointers, so we'll be able to look up the + // Texture pointer given an EggTexture pointer, later. + _textures[egg_tex] = def; + } + } + + // Finally, associate all of the removed texture references back to + // the same pointers as the others. + EggTextureCollection::TextureReplacement::const_iterator ri; + for (ri = replace.begin(); ri != replace.end(); ++ri) { + PT(EggTexture) orig = (*ri).first; + PT(EggTexture) repl = (*ri).second; + + _textures[orig] = _textures[repl]; + } +} + + +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::load_texture +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +bool qpEggLoader:: +load_texture(TextureDef &def, const EggTexture *egg_tex) { + Texture *tex; + if (egg_tex->has_alpha_file()) { + tex = TexturePool::load_texture(egg_tex->get_filename(), + egg_tex->get_alpha_file()); + } else { + tex = TexturePool::load_texture(egg_tex->get_filename()); + } + if (tex == (Texture *)NULL) { + return false; + } + + if (egg_keep_texture_pathnames) { + tex->set_name(egg_tex->get_filename()); + if (egg_tex->has_alpha_file()) { + tex->set_alpha_name(egg_tex->get_alpha_file()); + } else { + tex->clear_alpha_name(); + } + } + + /* + PT(TextureApplyTransition) apply = + new TextureApplyTransition(TextureApplyProperty::M_modulate); + */ + + apply_texture_attributes(tex, egg_tex); + // apply_texture_apply_attributes(apply, egg_tex); + + def._texture = TextureAttrib::make(tex); + // def._apply = *(_texture_applies.insert(apply).first); + + return true; +} + + +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::apply_texture_attributes +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void qpEggLoader:: +apply_texture_attributes(Texture *tex, const EggTexture *egg_tex) { + tex->set_name(egg_tex->get_filename().get_fullpath()); + + switch (egg_tex->determine_wrap_u()) { + case EggTexture::WM_repeat: + tex->set_wrapu(Texture::WM_repeat); + break; + + case EggTexture::WM_clamp: + if (egg_ignore_clamp) { + egg2pg_cat.warning() + << "Ignoring clamp request\n"; + tex->set_wrapu(Texture::WM_repeat); + } else { + tex->set_wrapu(Texture::WM_clamp); + } + break; + + case EggTexture::WM_unspecified: + break; + + default: + egg2pg_cat.warning() + << "Unexpected texture wrap flag: " + << (int)egg_tex->determine_wrap_u() << "\n"; + } + + switch (egg_tex->determine_wrap_v()) { + case EggTexture::WM_repeat: + tex->set_wrapv(Texture::WM_repeat); + break; + + case EggTexture::WM_clamp: + if (egg_ignore_clamp) { + egg2pg_cat.warning() + << "Ignoring clamp request\n"; + tex->set_wrapv(Texture::WM_repeat); + } else { + tex->set_wrapv(Texture::WM_clamp); + } + break; + + case EggTexture::WM_unspecified: + break; + + default: + egg2pg_cat.warning() + << "Unexpected texture wrap flag: " + << (int)egg_tex->determine_wrap_v() << "\n"; + } + + switch (egg_tex->get_minfilter()) { + case EggTexture::FT_nearest: + tex->set_minfilter(Texture::FT_nearest); + break; + + case EggTexture::FT_linear: + if (egg_ignore_filters) { + egg2pg_cat.warning() + << "Ignoring minfilter request\n"; + tex->set_minfilter(Texture::FT_nearest); + } else { + tex->set_minfilter(Texture::FT_linear); + } + break; + + case EggTexture::FT_nearest_mipmap_nearest: + if (egg_ignore_filters) { + egg2pg_cat.warning() + << "Ignoring minfilter request\n"; + tex->set_minfilter(Texture::FT_nearest); + } else if (egg_ignore_mipmaps) { + egg2pg_cat.warning() + << "Ignoring mipmap request\n"; + tex->set_minfilter(Texture::FT_nearest); + } else { + tex->set_minfilter(Texture::FT_nearest_mipmap_nearest); + } + break; + + case EggTexture::FT_linear_mipmap_nearest: + if (egg_ignore_filters) { + egg2pg_cat.warning() + << "Ignoring minfilter request\n"; + tex->set_minfilter(Texture::FT_nearest); + } else if (egg_ignore_mipmaps) { + egg2pg_cat.warning() + << "Ignoring mipmap request\n"; + tex->set_minfilter(Texture::FT_linear); + } else { + tex->set_minfilter(Texture::FT_linear_mipmap_nearest); + } + break; + + case EggTexture::FT_nearest_mipmap_linear: + if (egg_ignore_filters) { + egg2pg_cat.warning() + << "Ignoring minfilter request\n"; + tex->set_minfilter(Texture::FT_nearest); + } else if (egg_ignore_mipmaps) { + egg2pg_cat.warning() + << "Ignoring mipmap request\n"; + tex->set_minfilter(Texture::FT_nearest); + } else { + tex->set_minfilter(Texture::FT_nearest_mipmap_linear); + } + break; + + case EggTexture::FT_linear_mipmap_linear: + if (egg_ignore_filters) { + egg2pg_cat.warning() + << "Ignoring minfilter request\n"; + tex->set_minfilter(Texture::FT_nearest); + } else if (egg_ignore_mipmaps) { + egg2pg_cat.warning() + << "Ignoring mipmap request\n"; + tex->set_minfilter(Texture::FT_linear); + } else { + tex->set_minfilter(Texture::FT_linear_mipmap_linear); + } + break; + + case EggTexture::FT_unspecified: + // Default is bilinear, unless egg_ignore_filters is specified. + if (egg_ignore_filters) { + tex->set_minfilter(Texture::FT_nearest); + } else { + tex->set_minfilter(Texture::FT_linear); + } + } + + switch (egg_tex->get_magfilter()) { + case EggTexture::FT_nearest: + case EggTexture::FT_nearest_mipmap_nearest: + case EggTexture::FT_nearest_mipmap_linear: + tex->set_magfilter(Texture::FT_nearest); + break; + + case EggTexture::FT_linear: + case EggTexture::FT_linear_mipmap_nearest: + case EggTexture::FT_linear_mipmap_linear: + if (egg_ignore_filters) { + egg2pg_cat.warning() + << "Ignoring magfilter request\n"; + tex->set_magfilter(Texture::FT_nearest); + } else { + tex->set_magfilter(Texture::FT_linear); + } + break; + + case EggTexture::FT_unspecified: + // Default is bilinear, unless egg_ignore_filters is specified. + if (egg_ignore_filters) { + tex->set_magfilter(Texture::FT_nearest); + } else { + tex->set_magfilter(Texture::FT_linear); + } + } + + if (egg_tex->has_anisotropic_degree()) { + tex->set_anisotropic_degree(egg_tex->get_anisotropic_degree()); + } + + if (tex->_pbuffer->get_num_components() == 1) { + switch (egg_tex->get_format()) { + case EggTexture::F_red: + tex->_pbuffer->set_format(PixelBuffer::F_red); + break; + case EggTexture::F_green: + tex->_pbuffer->set_format(PixelBuffer::F_green); + break; + case EggTexture::F_blue: + tex->_pbuffer->set_format(PixelBuffer::F_blue); + break; + case EggTexture::F_alpha: + tex->_pbuffer->set_format(PixelBuffer::F_alpha); + break; + case EggTexture::F_luminance: + tex->_pbuffer->set_format(PixelBuffer::F_luminance); + break; + + case EggTexture::F_unspecified: + break; + + default: + egg2pg_cat.warning() + << "Ignoring inappropriate format " << egg_tex->get_format() + << " for 1-component texture " << egg_tex->get_name() << "\n"; + } + + } else if (tex->_pbuffer->get_num_components() == 2) { + switch (egg_tex->get_format()) { + case EggTexture::F_luminance_alpha: + tex->_pbuffer->set_format(PixelBuffer::F_luminance_alpha); + break; + + case EggTexture::F_luminance_alphamask: + tex->_pbuffer->set_format(PixelBuffer::F_luminance_alphamask); + break; + + case EggTexture::F_unspecified: + break; + + default: + egg2pg_cat.warning() + << "Ignoring inappropriate format " << egg_tex->get_format() + << " for 2-component texture " << egg_tex->get_name() << "\n"; + } + + } else if (tex->_pbuffer->get_num_components() == 3) { + switch (egg_tex->get_format()) { + case EggTexture::F_rgb: + tex->_pbuffer->set_format(PixelBuffer::F_rgb); + break; + case EggTexture::F_rgb12: + if (tex->_pbuffer->get_component_width() >= 2) { + // Only do this if the component width supports it. + tex->_pbuffer->set_format(PixelBuffer::F_rgb12); + } else { + egg2pg_cat.warning() + << "Ignoring inappropriate format " << egg_tex->get_format() + << " for 8-bit texture " << egg_tex->get_name() << "\n"; + } + break; + case EggTexture::F_rgb8: + case EggTexture::F_rgba8: + // We'll quietly accept RGBA8 for a 3-component texture, since + // flt2egg generates these for 3-component as well as for + // 4-component textures. + tex->_pbuffer->set_format(PixelBuffer::F_rgb8); + break; + case EggTexture::F_rgb5: + tex->_pbuffer->set_format(PixelBuffer::F_rgb5); + break; + case EggTexture::F_rgb332: + tex->_pbuffer->set_format(PixelBuffer::F_rgb332); + break; + + case EggTexture::F_unspecified: + break; + + default: + egg2pg_cat.warning() + << "Ignoring inappropriate format " << egg_tex->get_format() + << " for 3-component texture " << egg_tex->get_name() << "\n"; + } + + } else if (tex->_pbuffer->get_num_components() == 4) { + switch (egg_tex->get_format()) { + case EggTexture::F_rgba: + tex->_pbuffer->set_format(PixelBuffer::F_rgba); + break; + case EggTexture::F_rgbm: + tex->_pbuffer->set_format(PixelBuffer::F_rgbm); + break; + case EggTexture::F_rgba12: + if (tex->_pbuffer->get_component_width() >= 2) { + // Only do this if the component width supports it. + tex->_pbuffer->set_format(PixelBuffer::F_rgba12); + } else { + egg2pg_cat.warning() + << "Ignoring inappropriate format " << egg_tex->get_format() + << " for 8-bit texture " << egg_tex->get_name() << "\n"; + } + break; + case EggTexture::F_rgba8: + tex->_pbuffer->set_format(PixelBuffer::F_rgba8); + break; + case EggTexture::F_rgba4: + tex->_pbuffer->set_format(PixelBuffer::F_rgba4); + break; + case EggTexture::F_rgba5: + tex->_pbuffer->set_format(PixelBuffer::F_rgba5); + break; + + case EggTexture::F_unspecified: + break; + + default: + egg2pg_cat.warning() + << "Ignoring inappropriate format " << egg_tex->get_format() + << " for 4-component texture " << egg_tex->get_name() << "\n"; + } + } +} + +/* +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::apply_texture_apply_attributes +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void qpEggLoader:: +apply_texture_apply_attributes(TextureApplyTransition *apply, + const EggTexture *egg_tex) { + if (egg_always_decal_textures) { + apply->set_mode(TextureApplyProperty::M_decal); + + } else { + switch (egg_tex->get_env_type()) { + case EggTexture::ET_modulate: + apply->set_mode(TextureApplyProperty::M_modulate); + break; + + case EggTexture::ET_decal: + apply->set_mode(TextureApplyProperty::M_decal); + break; + + case EggTexture::ET_unspecified: + break; + + default: + egg2pg_cat.warning() + << "Invalid texture environment " + << (int)egg_tex->get_env_type() << "\n"; + } + } +} +*/ + +/* +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::get_material_transition +// Access: Private +// Description: Returns a transition suitable for enabling the +// material indicated by the given EggMaterial, and with +// the indicated backface flag. +//////////////////////////////////////////////////////////////////// +MaterialTransition *qpEggLoader:: +get_material_transition(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 MaterialTransition for this Material. + PT(MaterialTransition) mt = new MaterialTransition(shared_mat); + materials.insert(Materials::value_type(egg_mat, mt)); + + return mt; +} +*/ + + +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::setup_bucket +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void qpEggLoader:: +setup_bucket(BuilderBucket &bucket, PandaNode *parent, + EggPrimitive *egg_prim) { + bucket._qpnode = parent; + bucket._mesh = egg_mesh; + bucket._retesselate_coplanar = egg_retesselate_coplanar; + bucket._unroll_fans = egg_unroll_fans; + bucket._show_tstrips = egg_show_tstrips; + bucket._show_qsheets = egg_show_qsheets; + bucket._show_quads = egg_show_quads; + bucket._show_normals = egg_show_normals; + bucket._normal_scale = egg_normal_scale; + bucket._subdivide_polys = egg_subdivide_polys; + bucket._consider_fans = egg_consider_fans; + bucket._max_tfan_angle = egg_max_tfan_angle; + bucket._min_tfan_tris = egg_min_tfan_tris; + bucket._coplanar_threshold = egg_coplanar_threshold; + + // If a primitive has a name that does not begin with a digit, it + // should be used to group primitives together--i.e. each primitive + // with the same name gets placed into the same GeomNode. However, + // if a prim's name begins with a digit, just ignore it. + if (egg_prim->has_name() && !isdigit(egg_prim->get_name()[0])) { + bucket.set_name(egg_prim->get_name()); + } + + // Assign the appropriate properties to the bucket. + + // 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; + 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_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._state = bucket._state->add(TextureAttrib::make_off()); + if (egg_prim->has_texture()) { + PT(EggTexture) egg_tex = egg_prim->get_texture(); + + const TextureDef &def = _textures[egg_tex]; + if (def._texture != (const RenderAttrib *)NULL) { + bucket._state = bucket._state->add(def._texture); + // bucket._trans.set_transition(def._apply); + + // If neither the primitive nor the texture specified an alpha + // mode, assume it should be alpha'ed if the texture has an + // alpha channel. + 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->_pbuffer->get_num_components(); + if (egg_tex->has_alpha_channel(num_components)) { + implicit_alpha = true; + } + } + } + } + + /* + if (egg_prim->has_material()) { + MaterialTransition *mt = get_material_transition(egg_prim->get_material(), + egg_prim->get_bface_flag()); + bucket._trans.set_transition(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; + } + } + + /* + switch (am) { + case EggRenderMode::AM_on: + case EggRenderMode::AM_blend: + bucket._trans.set_transition(new TransparencyTransition(TransparencyProperty::M_alpha)); + break; + + case EggRenderMode::AM_blend_no_occlude: + bucket._trans.set_transition(new TransparencyTransition(TransparencyProperty::M_alpha)); + bucket._trans.set_transition(new DepthWriteTransition(DepthWriteTransition::off())); + break; + + case EggRenderMode::AM_ms: + bucket._trans.set_transition(new TransparencyTransition(TransparencyProperty::M_multisample)); + break; + + case EggRenderMode::AM_ms_mask: + bucket._trans.set_transition(new TransparencyTransition(TransparencyProperty::M_multisample_mask)); + break; + + default: + // bucket._trans.set_transition(new TransparencyTransition(TransparencyProperty::M_none)); + break; + } + */ + + /* + switch (dwm) { + case EggRenderMode::DWM_on: + bucket._trans.set_transition(new DepthWriteTransition); + break; + + case EggRenderMode::DWM_off: + bucket._trans.set_transition(new DepthWriteTransition(DepthWriteTransition::off())); + break; + + default: + break; + } + */ + + /* + switch (dtm) { + case EggRenderMode::DTM_on: + bucket._trans.set_transition(new DepthTestTransition(DepthTestProperty::M_less)); + break; + + case EggRenderMode::DTM_off: + bucket._trans.set_transition(new DepthTestTransition(DepthTestProperty::M_none)); + break; + + default: + break; + } + */ + + /* + if (has_bin) { + bucket._trans.set_transition(new GeomBinTransition(bin, draw_order)); + + } else if (has_draw_order) { + bucket._trans.set_transition(new GeomBinTransition("fixed", draw_order)); + } + */ + + /* + if (egg_prim->get_bface_flag()) { + // The primitive is marked with backface culling disabled--we want + // to see both sides. + bucket._trans.set_transition(new CullFaceTransition(CullFaceProperty::M_cull_none)); + + } else { + bucket._trans.set_transition(new CullFaceTransition(CullFaceProperty::M_cull_clockwise)); + } + */ +} + + + +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::make_node +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +PandaNode *qpEggLoader:: +make_node(EggNode *egg_node, PandaNode *parent) { + if (egg_node->is_of_type(EggNurbsCurve::get_class_type())) { + return make_node(DCAST(EggNurbsCurve, egg_node), parent); + } else 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())) { + 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); + } else if (egg_node->is_of_type(EggTable::get_class_type())) { + return make_node(DCAST(EggTable, egg_node), parent); + } else if (egg_node->is_of_type(EggGroupNode::get_class_type())) { + return make_node(DCAST(EggGroupNode, egg_node), parent); + } + + return (PandaNode *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::make_node (EggNurbsCurve) +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +PandaNode *qpEggLoader:: +make_node(EggNurbsCurve *egg_curve, PandaNode *parent) { + return (PandaNode *)NULL; + /* + assert(parent != NULL); + assert(!parent->is_of_type(GeomNode::get_class_type())); + + PT(ParametricCurve) curve; + + if (egg_load_classic_nurbs_curves) { + curve = new ClassicNurbsCurve; + } else { + curve = new NurbsCurve; + } + + NurbsCurveInterface *nurbs = curve->get_nurbs_interface(); + nassertr(nurbs != (NurbsCurveInterface *)NULL, (PandaNode *)NULL); + + if (egg_curve->get_order() < 1 || egg_curve->get_order() > 4) { + egg2pg_cat.error() + << "Invalid NURBSCurve order for " << egg_curve->get_name() << ": " + << egg_curve->get_order() << "\n"; + _error = true; + return (PandaNode *)NULL; + } + + nurbs->set_order(egg_curve->get_order()); + + EggPrimitive::const_iterator pi; + for (pi = egg_curve->begin(); pi != egg_curve->end(); ++pi) { + nurbs->append_cv(LCAST(float, (*pi)->get_pos4())); + } + + int num_knots = egg_curve->get_num_knots(); + if (num_knots != nurbs->get_num_knots()) { + egg2pg_cat.error() + << "Invalid NURBSCurve number of knots for " + << egg_curve->get_name() << ": got " << num_knots + << " knots, expected " << nurbs->get_num_knots() << "\n"; + _error = true; + return (PandaNode *)NULL; + } + + for (int i = 0; i < num_knots; i++) { + nurbs->set_knot(i, egg_curve->get_knot(i)); + } + + switch (egg_curve->get_curve_type()) { + case EggCurve::CT_xyz: + curve->set_curve_type(PCT_XYZ); + break; + + case EggCurve::CT_hpr: + curve->set_curve_type(PCT_HPR); + break; + + case EggCurve::CT_t: + curve->set_curve_type(PCT_T); + break; + + default: + break; + } + curve->set_name(egg_curve->get_name()); + + if (!curve->recompute()) { + egg2pg_cat.error() + << "Invalid NURBSCurve " << egg_curve->get_name() << "\n"; + _error = true; + return (PandaNode *)NULL; + } + + return new PandaNode(parent, curve); + */ +} + +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::make_node (EggPrimitive) +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +PandaNode *qpEggLoader:: +make_node(EggPrimitive *egg_prim, PandaNode *parent) { + assert(parent != NULL); + assert(!parent->is_of_type(qpGeomNode::get_class_type())); + + if (egg_prim->cleanup()) { + /* + if (parent->is_of_type(SwitchNode::get_class_type())) { + // If we're putting a primitive under a SwitchNode of some kind, + // its exact position within the group is relevant, so we need + // to create a placeholder now. + PandaNode *group = new PandaNode(egg_prim->get_name()); + parent->add_child(group); + make_nonindexed_primitive(egg_prim, group); + return group; + } + */ + + // Otherwise, we don't really care what the position of this + // primitive is within its parent's list of children, and in fact + // we want to allow it to be combined with other polygons added to + // the same parent. + make_nonindexed_primitive(egg_prim, parent); + } + return (PandaNode *)NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::make_node (EggBin) +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +PandaNode *qpEggLoader:: +make_node(EggBin *egg_bin, PandaNode *parent) { + return (PandaNode *)NULL; + /* + // 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. + + assert((EggBinner::BinNumber)egg_bin->get_bin_number() == EggBinner::BN_lod); + LODNode *lod_node = new LODNode; + lod_node->set_name(egg_bin->get_name()); + + pvector instances; + + EggGroup::const_iterator ci; + for (ci = egg_bin->begin(); ci != egg_bin->end(); ++ci) { + PandaNode *arc = make_node(*ci, lod_node); + assert(arc != (PandaNode *)NULL); + LODInstance instance(*ci, arc); + 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->_lod._center = LCAST(float, instances[0]._d->_center); + } + + for (size_t i = 0; i < instances.size(); i++) { + // Put the children in the proper order within the scene graph. + const LODInstance &instance = instances[i]; + + // All of the children should have the same center, because that's + // how we binned them. + assert(lod_node->_lod._center.almost_equal + (LCAST(float, instance._d->_center), 0.01)); + + instance._arc->set_sort(i); + + // Tell the LOD node about this child's switching distances. + lod_node->add_switch(instance._d->_switch_in, instance._d->_switch_out); + } + + return create_group_arc(egg_bin, parent, lod_node); + */ +} + + +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::make_node (EggGroup) +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +PandaNode *qpEggLoader:: +make_node(EggGroup *egg_group, PandaNode *parent) { + PandaNode *node = NULL; + + if (egg_group->has_objecttype()) { + // We'll allow recursive expansion of ObjectType strings--but we + // don't want to get caught in a cycle. Keep track of the strings + // we've expanded so far. + pset expanded; + pvector expanded_history; + + while (egg_group->has_objecttype()) { + string objecttype = egg_group->get_objecttype(); + if (!expanded.insert(objecttype).second) { + egg2pg_cat.error() + << "Cycle in ObjectType expansions:\n"; + copy(expanded_history.begin(), expanded_history.end(), + ostream_iterator(egg2pg_cat.error(false), " -> ")); + egg2pg_cat.error(false) << objecttype << "\n"; + _error = true; + break; + } + expanded_history.push_back(objecttype); + + // Now clear the group's ObjectType flag. We'll only loop back + // here again if we end up setting this during the ObjectType + // expansion; e.g. the expansion string itself contains an + // reference. + egg_group->clear_objecttype(); + + // Now try to find the egg syntax that the given objecttype is + // shorthand for. First, look in the config file. + + string egg_syntax = + config_egg2pg.GetString("egg-object-type-" + objecttype, "none"); + + if (egg_syntax == "none") { + // It wasn't defined in a config file. Maybe it's built in? + + if (cmp_nocase_uh(objecttype, "barrier") == 0) { + egg_syntax = " { Polyset descend }"; + + } else if (cmp_nocase_uh(objecttype, "solidpoly") == 0) { + egg_syntax = " { Polyset descend solid }"; + + } else if (cmp_nocase_uh(objecttype, "turnstile") == 0) { + egg_syntax = " { Polyset descend turnstile }"; + + } else if (cmp_nocase_uh(objecttype, "sphere") == 0) { + egg_syntax = " { Sphere descend }"; + + } else if (cmp_nocase_uh(objecttype, "trigger") == 0) { + egg_syntax = " { Polyset descend intangible }"; + + } else if (cmp_nocase_uh(objecttype, "trigger_sphere") == 0) { + egg_syntax = " { Sphere descend intangible }"; + + } else if (cmp_nocase_uh(objecttype, "eye_trigger") == 0) { + egg_syntax = " { Polyset descend intangible center }"; + + } else if (cmp_nocase_uh(objecttype, "bubble") == 0) { + egg_syntax = " { Sphere keep descend }"; + + } else if (cmp_nocase_uh(objecttype, "ghost") == 0) { + egg_syntax = " collide-mask { 0 }"; + + } else if (cmp_nocase_uh(objecttype, "backstage") == 0) { + // Ignore "backstage" geometry. + return NULL; + + } else { + egg2pg_cat.error() + << "Unknown ObjectType " << objecttype << "\n"; + _error = true; + break; + } + } + + if (!egg_syntax.empty()) { + if (!egg_group->parse_egg(egg_syntax)) { + egg2pg_cat.error() + << "Error while parsing definition for ObjectType " + << objecttype << "\n"; + _error = true; + } + } + } + } + + if (egg_group->get_dart_type() != EggGroup::DT_none) { + /* + // A group with the flag set means to create a character. + CharacterMaker char_maker(egg_group, *this); + node = char_maker.make_node(); + */ + + } else if (egg_group->get_cs_type() != EggGroup::CST_none && + egg_group->get_cs_type() != EggGroup::CST_geode) { + /* + // A collision group: create collision geometry. + node = new CollisionNode; + node->set_name(egg_group->get_name()); + + make_collision_solids(egg_group, egg_group, (CollisionNode *)node); + if ((egg_group->get_collide_flags() & EggGroup::CF_keep) != 0) { + // If we also specified to keep the geometry, continue the + // traversal. + EggGroup::const_iterator ci; + for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { + make_node(*ci, parent); + } + } + + PandaNode *arc = create_group_arc(egg_group, parent, node); + + if (!egg_show_collision_solids) { + arc->set_transition(new PruneTransition()); + } + return arc; + */ + + } else if (egg_group->get_switch_flag() && + egg_group->get_switch_fps() != 0.0) { + /* + // Create a sequence node. + node = new SequenceNode(1.0 / egg_group->get_switch_fps()); + node->set_name(egg_group->get_name()); + + EggGroup::const_iterator ci; + for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { + make_node(*ci, node); + } + */ + + } else if (egg_group->get_model_flag() || egg_group->get_dcs_flag()) { + /* + // A model or DCS flag; create a model node. + node = new ModelNode; + node->set_name(egg_group->get_name()); + + DCAST(ModelNode, node)->set_preserve_transform(egg_group->get_dcs_flag()); + + EggGroup::const_iterator ci; + for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { + make_node(*ci, node); + } + */ + + } else { + // A normal group; just create a normal node, and traverse. + node = new PandaNode(egg_group->get_name()); + + EggGroup::const_iterator ci; + for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { + make_node(*ci, node); + } + } + + if (node == (PandaNode *)NULL) { + return NULL; + } + return create_group_arc(egg_group, parent, node); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::create_group_arc +// Access: Private +// Description: Creates the arc parenting a new group to the scene +// graph, and applies any relevant transitions to the +// arc according to the EggGroup node that inspired the +// group. +//////////////////////////////////////////////////////////////////// +PandaNode *qpEggLoader:: +create_group_arc(EggGroup *egg_group, PandaNode *parent, PandaNode *node) { + parent->add_child(node); + + // If the group had a transform, apply it to the arc. + if (egg_group->has_transform()) { + LMatrix4f matf = LCAST(float, egg_group->get_transform()); + node->set_transform(TransformState::make_mat(matf)); + } + + /* + // If the group has a billboard flag, apply that. + switch (egg_group->get_billboard_type()) { + case EggGroup::BT_point_camera_relative: + arc->set_transition(new BillboardTransition(BillboardTransition::point_eye())); + break; + + case EggGroup::BT_point_world_relative: + arc->set_transition(new BillboardTransition(BillboardTransition::point_world())); + break; + + case EggGroup::BT_axis: + arc->set_transition(new BillboardTransition(BillboardTransition::axis())); + break; + + case EggGroup::BT_none: + break; + } + */ + + /* + if (egg_group->get_decal_flag()) { + if (egg_ignore_decals) { + egg2pg_cat.error() + << "Ignoring decal flag on " << egg_group->get_name() << "\n"; + _error = true; + } + + // If the group has the "decal" flag set, it means that all of the + // descendant groups will be decaled onto the geometry within + // this group. This means we'll need to reparent things a bit + // afterward. + _decals.insert(arc); + + // We'll also set up the DecalTransition now. + arc->set_transition(new DecalTransition); + } + */ + + /* + if (egg_group->get_direct_flag()) { + // If the group has the "direct" flag set, it means that + // everything at this node and below should be rendered in direct + // mode, i.e. in depth-first tree order, without state-sorting. + + arc->set_transition(new DirectRenderTransition); + + // We'll also want to set up the transitions on this arc to + // reflect the geometry at the top of the tree below this node, so + // we get good state-sorting behavior. We'll have to do this + // later. + _directs.insert(arc); + } + */ + + /* + // If the group specified some property that should propagate down + // to the leaves, we have to remember this arc and apply the + // property later, after we've created the actual geometry. + DeferredArcProperty def; + if (egg_group->has_collide_mask()) { + def._from_collide_mask = egg_group->get_collide_mask(); + def._into_collide_mask = egg_group->get_collide_mask(); + def._flags |= + DeferredArcProperty::F_has_from_collide_mask | + DeferredArcProperty::F_has_into_collide_mask; + } + if (egg_group->has_from_collide_mask()) { + def._from_collide_mask = egg_group->get_from_collide_mask(); + def._flags |= DeferredArcProperty::F_has_from_collide_mask; + } + if (egg_group->has_into_collide_mask()) { + def._into_collide_mask = egg_group->get_into_collide_mask(); + def._flags |= DeferredArcProperty::F_has_into_collide_mask; + } + + if (def._flags != 0) { + _deferred_arcs[arc] = def; + } + */ + + return node; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::make_node (EggTable) +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +PandaNode *qpEggLoader:: +make_node(EggTable *egg_table, PandaNode *parent) { + return (PandaNode *)NULL; + /* + if (egg_table->get_table_type() != EggTable::TT_bundle) { + // We only do anything with bundles. Isolated tables are treated + // as ordinary groups. + return make_node(DCAST(EggGroupNode, egg_table), parent); + } + + // It's an actual bundle, so make an AnimBundle from it and its + // descendants. + AnimBundleMaker bundle_maker(egg_table); + AnimBundleNode *node = bundle_maker.make_node(); + return new PandaNode(parent, node); + */ +} + + +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::make_node (EggGroupNode) +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +PandaNode *qpEggLoader:: +make_node(EggGroupNode *egg_group, PandaNode *parent) { + PandaNode *node = new PandaNode(egg_group->get_name()); + + EggGroupNode::const_iterator ci; + for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { + make_node(*ci, node); + } + + parent->add_child(node); + return node; +} + +/* +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::make_collision_solids +// Access: Private +// Description: Creates CollisionSolids corresponding to the +// collision geometry indicated at the given node and +// below. +//////////////////////////////////////////////////////////////////// +void qpEggLoader:: +make_collision_solids(EggGroup *start_group, EggGroup *egg_group, + CollisionNode *cnode) { + if (egg_group->get_cs_type() != EggGroup::CST_none) { + start_group = egg_group; + } + + switch (start_group->get_cs_type()) { + case EggGroup::CST_none: + case EggGroup::CST_geode: + // No collision flags; do nothing. Don't even traverse further. + return; + + case EggGroup::CST_inverse_sphere: + // These aren't presently supported. + egg2pg_cat.error() + << "Not presently supported: { " + << egg_group->get_cs_type() << " }\n"; + _error = true; + break; + + case EggGroup::CST_plane: + make_collision_plane(egg_group, cnode, start_group->get_collide_flags()); + break; + + case EggGroup::CST_polygon: + make_collision_polygon(egg_group, cnode, start_group->get_collide_flags()); + break; + + case EggGroup::CST_polyset: + make_collision_polyset(egg_group, cnode, start_group->get_collide_flags()); + break; + + case EggGroup::CST_sphere: + make_collision_sphere(egg_group, cnode, start_group->get_collide_flags()); + break; + } + + if ((start_group->get_collide_flags() & EggGroup::CF_descend) != 0) { + // Now pick up everything below. + EggGroup::const_iterator ci; + for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { + if ((*ci)->is_of_type(EggGroup::get_class_type())) { + make_collision_solids(start_group, DCAST(EggGroup, *ci), cnode); + } + } + } +} +*/ + +/* +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::make_collision_plane +// Access: Private +// Description: Creates a single CollisionPlane corresponding +// to the first polygon associated with this group. +//////////////////////////////////////////////////////////////////// +void qpEggLoader:: +make_collision_plane(EggGroup *egg_group, CollisionNode *cnode, + EggGroup::CollideFlags flags) { + EggGroup *geom_group = find_collision_geometry(egg_group); + if (geom_group != (EggGroup *)NULL) { + EggGroup::const_iterator ci; + for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) { + if ((*ci)->is_of_type(EggPolygon::get_class_type())) { + CollisionPlane *csplane = + create_collision_plane(DCAST(EggPolygon, *ci), egg_group); + if (csplane != (CollisionPlane *)NULL) { + apply_collision_flags(csplane, flags); + cnode->add_solid(csplane); + return; + } + } + } + } +} +*/ + +/* +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::make_collision_polygon +// Access: Private +// Description: Creates a single CollisionPolygon corresponding +// to the first polygon associated with this group. +//////////////////////////////////////////////////////////////////// +void qpEggLoader:: +make_collision_polygon(EggGroup *egg_group, CollisionNode *cnode, + EggGroup::CollideFlags flags) { + + EggGroup *geom_group = find_collision_geometry(egg_group); + if (geom_group != (EggGroup *)NULL) { + EggGroup::const_iterator ci; + for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) { + if ((*ci)->is_of_type(EggPolygon::get_class_type())) { + create_collision_polygons(cnode, DCAST(EggPolygon, *ci), + egg_group, flags); + } + } + } +} +*/ + +/* +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::make_collision_polyset +// Access: Private +// Description: Creates a series of CollisionPolygons corresponding +// to the polygons associated with this group. +//////////////////////////////////////////////////////////////////// +void qpEggLoader:: +make_collision_polyset(EggGroup *egg_group, CollisionNode *cnode, + EggGroup::CollideFlags flags) { + EggGroup *geom_group = find_collision_geometry(egg_group); + if (geom_group != (EggGroup *)NULL) { + EggGroup::const_iterator ci; + for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) { + if ((*ci)->is_of_type(EggPolygon::get_class_type())) { + create_collision_polygons(cnode, DCAST(EggPolygon, *ci), + egg_group, flags); + } + } + } +} +*/ + +/* +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::make_collision_sphere +// Access: Private +// Description: Creates a single CollisionSphere corresponding +// to the polygons associated with this group. +//////////////////////////////////////////////////////////////////// +void qpEggLoader:: +make_collision_sphere(EggGroup *egg_group, CollisionNode *cnode, + EggGroup::CollideFlags flags) { + EggGroup *geom_group = find_collision_geometry(egg_group); + if (geom_group != (EggGroup *)NULL) { + // Collect all of the vertices. + pset vertices; + + EggGroup::const_iterator ci; + for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) { + if ((*ci)->is_of_type(EggPrimitive::get_class_type())) { + EggPrimitive *prim = DCAST(EggPrimitive, *ci); + EggPrimitive::const_iterator pi; + for (pi = prim->begin(); pi != prim->end(); ++pi) { + vertices.insert(*pi); + } + } + } + + // Now average together all of the vertices to get a center. + int num_vertices = 0; + LPoint3d center(0.0, 0.0, 0.0); + pset::const_iterator vi; + + for (vi = vertices.begin(); vi != vertices.end(); ++vi) { + EggVertex *vtx = (*vi); + if (vtx->get_num_dimensions() == 3) { + center += vtx->get_pos3(); + num_vertices++; + + } else if (vtx->get_num_dimensions() == 4) { + LPoint4d p4 = vtx->get_pos4(); + if (p4[3] != 0.0) { + center += LPoint3d(p4[0], p4[1], p4[2]) / p4[3]; + num_vertices++; + } + } + } + + if (num_vertices > 0) { + center /= (double)num_vertices; + + // And the furthest vertex determines the radius. + double radius2 = 0.0; + for (vi = vertices.begin(); vi != vertices.end(); ++vi) { + EggVertex *vtx = (*vi); + if (vtx->get_num_dimensions() == 3) { + LVector3d v = vtx->get_pos3() - center; + radius2 = max(radius2, v.length_squared()); + + } else if (vtx->get_num_dimensions() == 4) { + LPoint4d p = vtx->get_pos4(); + if (p[3] != 0.0) { + LVector3d v = LPoint3d(p[0], p[1], p[2]) / p[3] - center; + radius2 = max(radius2, v.length_squared()); + } + } + } + + float radius = sqrtf(radius2); + CollisionSphere *cssphere = + new CollisionSphere(LCAST(float, center), radius); + apply_collision_flags(cssphere, flags); + cnode->add_solid(cssphere); + } + } +} +*/ + +/* +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::apply_collision_flags +// Access: Private +// Description: Does funny stuff to the CollisionSolid as +// appropriate, based on the settings of the given +// CollideFlags. +//////////////////////////////////////////////////////////////////// +void qpEggLoader:: +apply_collision_flags(CollisionSolid *solid, + EggGroup::CollideFlags flags) { + if ((flags & EggGroup::CF_intangible) != 0) { + solid->set_tangible(false); + } +} +*/ + +/* +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::find_collision_geometry +// Access: Private +// Description: Looks for the node, at or below the indicated node, +// that contains the associated collision geometry. +//////////////////////////////////////////////////////////////////// +EggGroup *qpEggLoader:: +find_collision_geometry(EggGroup *egg_group) { + if ((egg_group->get_collide_flags() & EggGroup::CF_descend) != 0) { + // If we have the "descend" instruction, we'll get to it when we + // get to it. Don't worry about it now. + return egg_group; + } + + // Does this group have any polygons? + EggGroup::const_iterator ci; + for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { + if ((*ci)->is_of_type(EggPolygon::get_class_type())) { + // Yes! Use this group. + return egg_group; + } + } + + // Well, the group had no polygons; look for a child group that has + // the same collision type. + for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { + if ((*ci)->is_of_type(EggGroup::get_class_type())) { + EggGroup *child_group = DCAST(EggGroup, *ci); + if (child_group->get_cs_type() == egg_group->get_cs_type()) { + return child_group; + } + } + } + + // We got nothing. + return NULL; +} +*/ + +/* +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::create_collision_plane +// Access: Private +// Description: Creates a single CollisionPlane from the indicated +// EggPolygon. +//////////////////////////////////////////////////////////////////// +CollisionPlane *qpEggLoader:: +create_collision_plane(EggPolygon *egg_poly, EggGroup *parent_group) { + if (!egg_poly->cleanup()) { + egg2pg_cat.error() + << "Degenerate collision plane in " << parent_group->get_name() + << "\n"; + _error = true; + return NULL; + } + + pvector vertices; + if (!egg_poly->empty()) { + EggPolygon::const_iterator vi; + vi = egg_poly->begin(); + + Vertexd vert = (*vi)->get_pos3(); + vertices.push_back(LCAST(float, vert)); + + Vertexd last_vert = vert; + ++vi; + while (vi != egg_poly->end()) { + vert = (*vi)->get_pos3(); + if (!vert.almost_equal(last_vert)) { + vertices.push_back(LCAST(float, vert)); + } + + last_vert = vert; + ++vi; + } + } + + if (vertices.size() < 3) { + return NULL; + } + Planef plane(vertices[0], vertices[1], vertices[2]); + return new CollisionPlane(plane); +} +*/ + +/* +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::create_collision_polygons +// Access: Private +// Description: Creates one or more CollisionPolygons from the +// indicated EggPolygon, and adds them to the indicated +// CollisionNode. +//////////////////////////////////////////////////////////////////// +void qpEggLoader:: +create_collision_polygons(CollisionNode *cnode, EggPolygon *egg_poly, + EggGroup *parent_group, + EggGroup::CollideFlags flags) { + + PT(EggGroup) group = new EggGroup; + + if (!egg_poly->triangulate_into(group, false)) { + egg2pg_cat.error() + << "Degenerate collision polygon in " << parent_group->get_name() + << "\n"; + _error = true; + return; + } + + if (group->size() != 1) { + egg2pg_cat.error() + << "Concave collision polygon in " << parent_group->get_name() + << "\n"; + _error = true; + } + + EggGroup::iterator ci; + for (ci = group->begin(); ci != group->end(); ++ci) { + EggPolygon *poly = DCAST(EggPolygon, *ci); + + pvector vertices; + if (!poly->empty()) { + EggPolygon::const_iterator vi; + vi = poly->begin(); + + Vertexd vert = (*vi)->get_pos3(); + vertices.push_back(LCAST(float, vert)); + + Vertexd last_vert = vert; + ++vi; + while (vi != poly->end()) { + vert = (*vi)->get_pos3(); + if (!vert.almost_equal(last_vert)) { + vertices.push_back(LCAST(float, vert)); + } + + last_vert = vert; + ++vi; + } + } + + if (vertices.size() >= 3) { + const Vertexf *vertices_begin = &vertices[0]; + const Vertexf *vertices_end = vertices_begin + vertices.size(); + CollisionPolygon *cspoly = + new CollisionPolygon(vertices_begin, vertices_end); + apply_collision_flags(cspoly, flags); + cnode->add_solid(cspoly); + } + } +} +*/ + + +/* +//////////////////////////////////////////////////////////////////// +// Function: qpEggLoader::apply_deferred_arcs +// Access: Private +// Description: Walks back over the tree and applies the +// DeferredArcProperties that were saved up along the +// way. +//////////////////////////////////////////////////////////////////// +void qpEggLoader:: +apply_deferred_arcs(Node *root) { + DeferredArcTraverser trav(_deferred_arcs); + + df_traverse(root, trav, NullTransitionWrapper(), DeferredArcProperty(), + PandaNode::get_class_type()); +} +*/ diff --git a/panda/src/egg2pg/qpeggLoader.h b/panda/src/egg2pg/qpeggLoader.h new file mode 100644 index 0000000000..ca7d617262 --- /dev/null +++ b/panda/src/egg2pg/qpeggLoader.h @@ -0,0 +1,159 @@ +// Filename: qpeggLoader.h +// Created by: drose (26Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, 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://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef qpEGGLOADER_H +#define qpEGGLOADER_H + +#include "pandabase.h" + +#include "eggData.h" +#include "eggTexture.h" +#include "pt_EggTexture.h" +#include "eggGroup.h" +#include "eggMaterial.h" +#include "pt_EggMaterial.h" +#include "texture.h" +#include "pandaNode.h" +#include "pointerTo.h" +#include "builder.h" +#include "lmatrix.h" +#include "indirectCompareTo.h" +#include "textureAttrib.h" + +class EggNode; +class EggBin; +class EggTable; +class EggNurbsCurve; +class EggPrimitive; +class EggPolygon; +class EggMaterial; +class ComputedVerticesMaker; +class RenderRelation; +class CollisionSolid; +class CollisionNode; +class CollisionPlane; +class CollisionPolygon; + +/////////////////////////////////////////////////////////////////// +// Class : qpEggLoader +// Description : Converts an egg data structure, possibly read from an +// egg file but not necessarily, into a scene graph +// suitable for rendering. +// +// This class isn't exported from this package. +//////////////////////////////////////////////////////////////////// +class qpEggLoader { +public: + qpEggLoader(); + qpEggLoader(const EggData &data); + + void build_graph(); + void reparent_decals(); + void reset_directs(); + + void make_nonindexed_primitive(EggPrimitive *egg_prim, PandaNode *parent, + const LMatrix4d *transform = NULL); + + void make_indexed_primitive(EggPrimitive *egg_prim, PandaNode *parent, + const LMatrix4d *transform, + ComputedVerticesMaker &_comp_verts_maker); + +private: + class TextureDef { + public: + CPT(RenderAttrib) _texture; + // PT(TextureApplyTransition) _apply; + }; + + void load_textures(); + bool load_texture(TextureDef &def, const EggTexture *egg_tex); + void apply_texture_attributes(Texture *tex, const EggTexture *egg_tex); + void apply_texture_apply_attributes(TextureApplyTransition *apply, + const EggTexture *egg_tex); + + /* + MaterialTransition *get_material_transition(const EggMaterial *egg_mat, + bool bface); + */ + + void setup_bucket(BuilderBucket &bucket, PandaNode *parent, + EggPrimitive *egg_prim); + + PandaNode *make_node(EggNode *egg_node, PandaNode *parent); + PandaNode *make_node(EggNurbsCurve *egg_curve, PandaNode *parent); + PandaNode *make_node(EggPrimitive *egg_prim, PandaNode *parent); + PandaNode *make_node(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 make_collision_solids(EggGroup *start_group, EggGroup *egg_group, + CollisionNode *cnode); + void make_collision_plane(EggGroup *egg_group, CollisionNode *cnode, + EggGroup::CollideFlags flags); + void make_collision_polygon(EggGroup *egg_group, CollisionNode *cnode, + EggGroup::CollideFlags flags); + void make_collision_polyset(EggGroup *egg_group, CollisionNode *cnode, + EggGroup::CollideFlags flags); + void make_collision_sphere(EggGroup *egg_group, CollisionNode *cnode, + EggGroup::CollideFlags flags); + void apply_collision_flags(CollisionSolid *solid, + EggGroup::CollideFlags flags); + EggGroup *find_collision_geometry(EggGroup *egg_group); + CollisionPlane *create_collision_plane(EggPolygon *egg_poly, + EggGroup *parent_group); + void create_collision_polygons(CollisionNode *cnode, EggPolygon *egg_poly, + EggGroup *parent_group, + EggGroup::CollideFlags flags); + + void apply_deferred_arcs(PandaNode *root); + */ + + + Builder _builder; + + typedef pmap Textures; + Textures _textures; + + /* + typedef pmap Materials; + Materials _materials; + Materials _materials_bface; + */ + + /* + typedef pset Decals; + Decals _decals; + + typedef pset Directs; + Directs _directs; + + DeferredArcs _deferred_arcs; + */ + +public: + PT(PandaNode) _root; + EggData _data; + bool _error; +}; + + +#endif diff --git a/panda/src/egg2pg/qpload_egg_file.cxx b/panda/src/egg2pg/qpload_egg_file.cxx new file mode 100644 index 0000000000..f49a98acd1 --- /dev/null +++ b/panda/src/egg2pg/qpload_egg_file.cxx @@ -0,0 +1,112 @@ +// Filename: qpload_egg_file.cxx +// Created by: drose (26Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, 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://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "qpload_egg_file.h" +#include "qpeggLoader.h" +#include "config_egg2pg.h" + +/* +#include "sceneGraphReducer.h" +#include "renderRelation.h" +*/ + +static PT(PandaNode) +load_from_loader(qpEggLoader &loader) { + loader._data.resolve_externals(); + + loader.build_graph(); + + if (loader._error && !egg_accept_errors) { + egg2pg_cat.error() + << "Errors in egg file.\n"; + return NULL; + } + + /* + if (loader._root != (NamedNode *)NULL && egg_flatten) { + SceneGraphReducer gr(RenderRelation::get_class_type()); + int num_reduced = gr.flatten(loader._root, egg_flatten_siblings); + egg2pg_cat.info() << "Flattened " << num_reduced << " arcs.\n"; + } + */ + + return loader._root; +} + +//////////////////////////////////////////////////////////////////// +// Function: load_egg_file +// Description: A convenience function. Loads up the indicated egg +// file, and returns the root of a scene graph. Returns +// NULL if the file cannot be read for some reason. +// Does not search along the egg path for the filename +// first; use EggData::resolve_egg_filename() if this is +// required. +//////////////////////////////////////////////////////////////////// +PT(PandaNode) +qpload_egg_file(const string &filename, CoordinateSystem cs) { + Filename egg_filename = Filename::text_filename(filename); + if (!egg_filename.exists()) { + egg2pg_cat.error() + << "Could not find " << egg_filename << "\n"; + return NULL; + } + + egg2pg_cat.info() + << "Reading " << egg_filename << "\n"; + + ifstream file; + if (!egg_filename.open_read(file)) { + egg2pg_cat.error() + << "Could not open " << egg_filename << " for reading.\n"; + return NULL; + } + + qpEggLoader loader; + loader._data.set_egg_filename(egg_filename); + if (cs != CS_default) { + loader._data.set_coordinate_system(cs); + } + + if (!loader._data.read(file)) { + egg2pg_cat.error() + << "Error reading " << egg_filename << "\n"; + return NULL; + } + + return load_from_loader(loader); +} + +//////////////////////////////////////////////////////////////////// +// Function: load_egg_data +// Description: Another convenience function; works like +// load_egg_file() but starts from an already-filled +// EggData structure. The structure is destroyed in the +// loading. +//////////////////////////////////////////////////////////////////// +PT(PandaNode) +qpload_egg_data(EggData &data) { + // We temporarily shuttle the children to a holding node so we can + // copy them into the EggLoader's structure without it complaining. + EggGroupNode children_holder; + children_holder.steal_children(data); + + qpEggLoader loader(data); + loader._data.steal_children(children_holder); + + return load_from_loader(loader); +} diff --git a/panda/src/egg2pg/qpload_egg_file.h b/panda/src/egg2pg/qpload_egg_file.h new file mode 100644 index 0000000000..2a60374cd1 --- /dev/null +++ b/panda/src/egg2pg/qpload_egg_file.h @@ -0,0 +1,52 @@ +// Filename: qpload_egg_file.h +// Created by: drose (26Feb02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, 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://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef qpLOAD_EGG_FILE_H +#define qpLOAD_EGG_FILE_H + +#include "pandabase.h" + +#include "pandaNode.h" +#include "coordinateSystem.h" + +class EggData; + +//////////////////////////////////////////////////////////////////// +// Function: load_egg_file +// Description: A convenience function; the primary interface to this +// package. Loads up the indicated egg file, and +// returns the root of a scene graph. Returns NULL if +// the file cannot be read for some reason. +// +// Also see the EggLoader class, which can exercise a +// bit more manual control over the loading process. +//////////////////////////////////////////////////////////////////// +EXPCL_PANDAEGG PT(PandaNode) +qpload_egg_file(const string &filename, CoordinateSystem cs = CS_default); + +//////////////////////////////////////////////////////////////////// +// Function: load_egg_data +// Description: Another convenience function; works like +// load_egg_file() but starts from an already-filled +// EggData structure. The structure is destroyed in the +// loading. +//////////////////////////////////////////////////////////////////// +EXPCL_PANDAEGG PT(PandaNode) +qpload_egg_data(EggData &data); + +#endif diff --git a/panda/src/egg2sg/Sources.pp b/panda/src/egg2sg/Sources.pp index 3d3697b478..9db6835e4c 100644 --- a/panda/src/egg2sg/Sources.pp +++ b/panda/src/egg2sg/Sources.pp @@ -6,7 +6,7 @@ #begin lib_target #define TARGET egg2sg #define LOCAL_LIBS \ - parametrics cull collide egg builder loader chan char switchnode + parametrics cull collide egg2pg egg builder loader chan char switchnode #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx diff --git a/panda/src/egg2sg/config_egg2sg.cxx b/panda/src/egg2sg/config_egg2sg.cxx index 42bae36d75..232415890f 100644 --- a/panda/src/egg2sg/config_egg2sg.cxx +++ b/panda/src/egg2sg/config_egg2sg.cxx @@ -26,56 +26,6 @@ ConfigureDef(config_egg2sg); NotifyCategoryDef(egg2sg, ""); -bool egg_mesh = config_egg2sg.GetBool("egg-mesh", true); -bool egg_retesselate_coplanar = config_egg2sg.GetBool("egg-retesselate-coplanar", true); -bool egg_unroll_fans = config_egg2sg.GetBool("egg-unroll-fans", true); -bool egg_show_tstrips = config_egg2sg.GetBool("egg-show-tstrips", false); -bool egg_show_qsheets = config_egg2sg.GetBool("egg-show-qsheets", false); -bool egg_show_quads = config_egg2sg.GetBool("egg-show-quads", false); -bool egg_false_color = (egg_show_tstrips | egg_show_qsheets | egg_show_quads); -bool egg_show_normals = config_egg2sg.GetBool("egg-show-normals", false); -double egg_normal_scale = config_egg2sg.GetDouble("egg-normal-scale", 1.0); -bool egg_subdivide_polys = config_egg2sg.GetBool("egg-subdivide-polys", true); -bool egg_consider_fans = config_egg2sg.GetBool("egg-consider-fans", true); -double egg_max_tfan_angle = config_egg2sg.GetDouble("egg-max-tfan-angle", 40.0); -int egg_min_tfan_tris = config_egg2sg.GetInt("egg-min-tfan-tris", 4); -double egg_coplanar_threshold = config_egg2sg.GetDouble("egg-coplanar-threshold", 0.01); -bool egg_ignore_mipmaps = config_egg2sg.GetBool("egg-ignore-mipmaps", false); -bool egg_ignore_filters = config_egg2sg.GetBool("egg-ignore-filters", false); -bool egg_ignore_clamp = config_egg2sg.GetBool("egg-ignore-clamp", false); -bool egg_always_decal_textures = config_egg2sg.GetBool("egg-always-decal-textures", false); -bool egg_ignore_decals = config_egg2sg.GetBool("egg-ignore-decals", false); -bool egg_flatten = config_egg2sg.GetBool("egg-flatten", true); - -// It is almost always a bad idea to set this true. -bool egg_flatten_siblings = config_egg2sg.GetBool("egg-flatten-siblings", false); - -bool egg_show_collision_solids = config_egg2sg.GetBool("egg-show-collision-solids", false); - -// When this is true, keep texture pathnames exactly the same as they -// appeared in the egg file, in particular leaving them as relative -// paths, rather than letting them reflect the full path at which they -// were found. This is particularly useful when generating bam files. -// However, if the same texture is named by two different relative -// paths, these will still be collapsed into one texture (using one of -// the relative paths, chosen arbitrarily). -bool egg_keep_texture_pathnames = config_egg2sg.GetBool("egg-keep-texture-pathnames", false); - -// When this is true, a entry appearing in an egg file -// will load a ClassicNurbsCurve object instead of the default, a -// NurbsCurve object. This only makes a difference when the NURBS++ -// library is available, in which case the default, NurbsCurve, is -// actually a NurbsPPCurve object. -bool egg_load_classic_nurbs_curves = config_egg2sg.GetBool("egg-load-classic-nurbs-curves", false); - -// When this is true, certain kinds of recoverable errors (not syntax -// errors) in an egg file will be allowed and ignored when an egg file -// is loaded. When it is false, only perfectly pristine egg files may -// be loaded. -bool egg_accept_errors = config_egg2sg.GetBool("egg-accept-errors", true); - -CoordinateSystem egg_coordinate_system; - ConfigureFn(config_egg2sg) { init_libegg2sg(); } @@ -98,17 +48,6 @@ init_libegg2sg() { LoaderFileTypeEgg::init_type(); - string csstr = config_egg2sg.GetString("egg-coordinate-system", "default"); - CoordinateSystem cs = parse_coordinate_system_string(csstr); - - if (cs == CS_invalid) { - egg2sg_cat.error() - << "Unexpected egg-coordinate-system string: " << csstr << "\n"; - cs = CS_default; - } - egg_coordinate_system = (cs == CS_default) ? - default_coordinate_system : cs; - LoaderFileTypeRegistry *reg = LoaderFileTypeRegistry::get_ptr(); reg->register_type(new LoaderFileTypeEgg); diff --git a/panda/src/egg2sg/config_egg2sg.h b/panda/src/egg2sg/config_egg2sg.h index 856cc4ce6f..35b41e3a6f 100644 --- a/panda/src/egg2sg/config_egg2sg.h +++ b/panda/src/egg2sg/config_egg2sg.h @@ -26,36 +26,11 @@ #include #include +#include "config_egg2pg.h" // temp to declare the global consts + ConfigureDecl(config_egg2sg, EXPCL_PANDAEGG, EXPTP_PANDAEGG); NotifyCategoryDecl(egg2sg, EXPCL_PANDAEGG, EXPTP_PANDAEGG); -extern EXPCL_PANDAEGG bool egg_mesh; -extern EXPCL_PANDAEGG bool egg_retesselate_coplanar; -extern EXPCL_PANDAEGG bool egg_unroll_fans; -extern EXPCL_PANDAEGG bool egg_show_tstrips; -extern EXPCL_PANDAEGG bool egg_show_qsheets; -extern EXPCL_PANDAEGG bool egg_show_quads; -extern EXPCL_PANDAEGG bool egg_false_color; -extern EXPCL_PANDAEGG bool egg_show_normals; -extern EXPCL_PANDAEGG double egg_normal_scale; -extern EXPCL_PANDAEGG bool egg_subdivide_polys; -extern EXPCL_PANDAEGG bool egg_consider_fans; -extern EXPCL_PANDAEGG double egg_max_tfan_angle; -extern EXPCL_PANDAEGG int egg_min_tfan_tris; -extern EXPCL_PANDAEGG double egg_coplanar_threshold; -extern EXPCL_PANDAEGG CoordinateSystem egg_coordinate_system; -extern EXPCL_PANDAEGG bool egg_ignore_mipmaps; -extern EXPCL_PANDAEGG bool egg_ignore_filters; -extern EXPCL_PANDAEGG bool egg_ignore_clamp; -extern EXPCL_PANDAEGG bool egg_always_decal_textures; -extern EXPCL_PANDAEGG bool egg_ignore_decals; -extern EXPCL_PANDAEGG bool egg_flatten; -extern EXPCL_PANDAEGG bool egg_flatten_siblings; -extern EXPCL_PANDAEGG bool egg_show_collision_solids; -extern EXPCL_PANDAEGG bool egg_keep_texture_pathnames; -extern EXPCL_PANDAEGG bool egg_load_classic_nurbs_curves; -extern EXPCL_PANDAEGG bool egg_accept_errors; - extern EXPCL_PANDAEGG void init_libegg2sg(); #endif diff --git a/panda/src/pgraph/pandaNode.I b/panda/src/pgraph/pandaNode.I index b4dd9173d1..015bad2f79 100644 --- a/panda/src/pgraph/pandaNode.I +++ b/panda/src/pgraph/pandaNode.I @@ -369,6 +369,28 @@ clear_transform() { cdata->_transform = TransformState::make_identity(); } +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::ls +// Access: Published +// Description: Lists all the nodes at and below the current path +// hierarchically. +//////////////////////////////////////////////////////////////////// +INLINE void PandaNode:: +ls() const { + ls(nout); +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::ls +// Access: Published +// Description: Lists all the nodes at and below the current path +// hierarchically. +//////////////////////////////////////////////////////////////////// +INLINE void PandaNode:: +ls(ostream &out, int indent_level) const { + r_list_descendants(out, indent_level); +} + //////////////////////////////////////////////////////////////////// // Function: PandaNode::get_children // Access: Public diff --git a/panda/src/pgraph/pandaNode.cxx b/panda/src/pgraph/pandaNode.cxx index 3a2de2eddf..955f12d98b 100644 --- a/panda/src/pgraph/pandaNode.cxx +++ b/panda/src/pgraph/pandaNode.cxx @@ -349,7 +349,7 @@ write(ostream &out, int indent_level) const { out << " T"; } if (!cdata->_state->is_empty()) { - out << " (" << *cdata->_state << ")"; + out << " " << *cdata->_state; } out << "\n"; } @@ -650,6 +650,30 @@ fix_chain_lengths() { } } +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::r_list_descendants +// Access: Private +// Description: The recursive implementation of ls(). +//////////////////////////////////////////////////////////////////// +void PandaNode:: +r_list_descendants(ostream &out, int indent_level) const { + write(out, indent_level); + + CDReader cdata(_cycler); + Down::const_iterator di; + for (di = cdata->_down.begin(); di != cdata->_down.end(); ++di) { + (*di).get_child()->r_list_descendants(out, indent_level + 2); + } + + // Also report the number of stashed nodes at this level. + /* + int num_stashed = get_num_stashed(); + if (num_stashed != 0) { + indent(out, indent_level) << "(" << num_stashed << " stashed)\n"; + } + */ +} + //////////////////////////////////////////////////////////////////// // Function: PandaNode::register_with_read_factory // Access: Public, Static diff --git a/panda/src/pgraph/pandaNode.h b/panda/src/pgraph/pandaNode.h index 14e40eab24..4c1163a258 100644 --- a/panda/src/pgraph/pandaNode.h +++ b/panda/src/pgraph/pandaNode.h @@ -35,6 +35,7 @@ #include "luse.h" #include "ordered_vector.h" #include "pointerTo.h" +#include "notify.h" class NodeChainComponent; @@ -90,6 +91,9 @@ PUBLISHED: virtual void output(ostream &out) const; virtual void write(ostream &out, int indent_level) const; + INLINE void ls() const; + INLINE void ls(ostream &out, int indent_level = 0) const; + public: virtual bool is_geom_node() const; @@ -107,6 +111,7 @@ private: PT(NodeChainComponent) get_generic_component(); void delete_component(NodeChainComponent *component); void fix_chain_lengths(); + void r_list_descendants(ostream &out, int indent_level) const; private: class EXPCL_PANDA DownConnection { diff --git a/panda/src/pgraph/qpgeomNode.I b/panda/src/pgraph/qpgeomNode.I index f3b81f5339..49db002992 100644 --- a/panda/src/pgraph/qpgeomNode.I +++ b/panda/src/pgraph/qpgeomNode.I @@ -79,7 +79,7 @@ get_geom_state(int n) const { } //////////////////////////////////////////////////////////////////// -// Function: qpGeomNode::set_state +// Function: qpGeomNode::set_geom_state // Access: Public // Description: Changes the RenderState associated with the nth geom // of the node. This is just the RenderState directly @@ -89,7 +89,7 @@ get_geom_state(int n) const { // above this GeomNode. //////////////////////////////////////////////////////////////////// INLINE void qpGeomNode:: -set_state(int n, const RenderState *state) { +set_geom_state(int n, const RenderState *state) { CDWriter cdata(_cycler); nassertv(n >= 0 && n < (int)cdata->_geoms.size()); cdata->_geoms[n]._state = state; diff --git a/panda/src/pgraph/qpgeomNode.cxx b/panda/src/pgraph/qpgeomNode.cxx index 4dea7a5a07..f076d3aba2 100644 --- a/panda/src/pgraph/qpgeomNode.cxx +++ b/panda/src/pgraph/qpgeomNode.cxx @@ -96,6 +96,24 @@ qpGeomNode:: ~qpGeomNode() { } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomNode::write_geoms +// Access: Published +// Description: Writes a short description of all the Geoms in the +// node. +//////////////////////////////////////////////////////////////////// +void qpGeomNode:: +write_geoms(ostream &out, int indent_level) const { + CDReader cdata(_cycler); + write(out, indent_level); + Geoms::const_iterator gi; + for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) { + const GeomEntry &entry = (*gi); + indent(out, indent_level + 2) + << *entry._geom << " (" << *entry._state << ")\n"; + } +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomNode::write_verbose // Access: Published @@ -105,7 +123,7 @@ qpGeomNode:: void qpGeomNode:: write_verbose(ostream &out, int indent_level) const { CDReader cdata(_cycler); - PandaNode::write(out, indent_level); + write(out, indent_level); Geoms::const_iterator gi; for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) { const GeomEntry &entry = (*gi); @@ -126,23 +144,6 @@ output(ostream &out) const { out << " (" << get_num_geoms() << " geoms)"; } -//////////////////////////////////////////////////////////////////// -// Function: qpGeomNode::write -// Access: Public, Virtual -// Description: -//////////////////////////////////////////////////////////////////// -void qpGeomNode:: -write(ostream &out, int indent_level) const { - CDReader cdata(_cycler); - PandaNode::write(out, indent_level); - Geoms::const_iterator gi; - for (gi = cdata->_geoms.begin(); gi != cdata->_geoms.end(); ++gi) { - const GeomEntry &entry = (*gi); - indent(out, indent_level + 2) - << *entry._geom << " (" << *entry._state << ")\n"; - } -} - //////////////////////////////////////////////////////////////////// // Function: qpGeomNode::is_geom_node // Access: Public, Virtual diff --git a/panda/src/pgraph/qpgeomNode.h b/panda/src/pgraph/qpgeomNode.h index e97daa663c..829364da10 100644 --- a/panda/src/pgraph/qpgeomNode.h +++ b/panda/src/pgraph/qpgeomNode.h @@ -48,17 +48,17 @@ PUBLISHED: INLINE int get_num_geoms() const; INLINE Geom *get_geom(int n) const; INLINE const RenderState *get_geom_state(int n) const; - INLINE void set_state(int n, const RenderState *state); + INLINE void set_geom_state(int n, const RenderState *state); - INLINE int add_geom(Geom *geom, const RenderState *state); + INLINE int add_geom(Geom *geom, const RenderState *state = RenderState::make_empty()); INLINE void remove_geom(int n); INLINE void remove_all_geoms(); + void write_geoms(ostream &out, int indent_level) const; void write_verbose(ostream &out, int indent_level) const; public: virtual void output(ostream &out) const; - virtual void write(ostream &out, int indent_level) const; virtual bool is_geom_node() const; diff --git a/panda/src/pgraph/renderState.cxx b/panda/src/pgraph/renderState.cxx index acc8cc68b6..da9ff3b10b 100644 --- a/panda/src/pgraph/renderState.cxx +++ b/panda/src/pgraph/renderState.cxx @@ -423,6 +423,14 @@ add(const RenderAttrib *attrib, int override) const { *result = new_attribute; ++result; + if (ai != _attributes.end() && !(new_attribute < (*ai))) { + // At this point we know: + // !((*ai) < new_attribute) && !(new_attribute < (*ai)) + // which means (*ai) == new_attribute--so we should leave it out, + // to avoid duplicating attributes in the set. + ++ai; + } + while (ai != _attributes.end()) { *result = *ai; ++ai; diff --git a/panda/src/testbed/Sources.pp b/panda/src/testbed/Sources.pp index a29d8be370..f2bb058a75 100644 --- a/panda/src/testbed/Sources.pp +++ b/panda/src/testbed/Sources.pp @@ -32,7 +32,7 @@ #define SOURCES \ pview.cxx - #define LOCAL_LIBS pgraph $[LOCAL_LIBS] + #define LOCAL_LIBS egg2pg pgraph $[LOCAL_LIBS] #end test_bin_target #begin test_bin_target diff --git a/panda/src/testbed/pview.cxx b/panda/src/testbed/pview.cxx index f0accd5c59..b4f54bc3b9 100644 --- a/panda/src/testbed/pview.cxx +++ b/panda/src/testbed/pview.cxx @@ -33,6 +33,10 @@ #include "texture.h" #include "texturePool.h" +// To load egg files directly. +#include "qpload_egg_file.h" +#include "eggData.h" + // These are in support of legacy data graph operations. #include "namedNode.h" #include "mouse.h" @@ -59,6 +63,28 @@ static const int win_height = config_pview.GetInt("win-height", 480); // As long as this is true, the main loop will continue running. bool run_flag = true; +// These are used by report_frame_rate(). +static double start_time = 0.0; +static int start_frame_count = 0; + +void +report_frame_rate() { + double now = ClockObject::get_global_clock()->get_frame_time(); + double delta = now - start_time; + + int frame_count = ClockObject::get_global_clock()->get_frame_count(); + int num_frames = frame_count - start_frame_count; + if (num_frames > 0) { + nout << endl << num_frames << " frames in " << delta << " seconds" << endl; + double x = ((double)num_frames) / delta; + nout << x << " fps average (" << 1000.0 / x << "ms)" << endl; + + // Reset the frame rate counter for the next press of 'f'. + start_time = now; + start_frame_count = frame_count; + } +} + PT(GraphicsPipe) make_pipe() { // We use the GraphicsPipe factory to make us a renderable pipe @@ -166,22 +192,28 @@ make_default_geometry(PandaNode *parent) { void get_models(PandaNode *parent, int argc, char *argv[]) { - make_default_geometry(parent); - /* - Loader loader; + if (argc < 2) { + // In the absence of any models on the command line, load up a + // default triangle so we at least have something to look at. + make_default_geometry(parent); - for (int i = 1; i < argc; i++) { - Filename filename = argv[i]; + } else { - cerr << "Loading " << filename << "\n"; - PT_Node node = loader.load_sync(filename); - if (node == (Node *)NULL) { - cerr << "Unable to load " << filename << "\n"; - } else { - new RenderRelation(parent, node); + for (int i = 1; i < argc; i++) { + Filename filename = argv[i]; + + cerr << "Loading " << filename << "\n"; + EggData::resolve_egg_filename(filename); + PT(PandaNode) node = qpload_egg_file(filename); + if (node == (PandaNode *)NULL) { + cerr << "Unable to load " << filename << "\n"; + + } else { + node->ls(); + parent->add_child(node); + } } } - */ } NamedNode * @@ -218,6 +250,11 @@ event_esc(CPT_Event) { run_flag = false; } +void +event_f(CPT_Event) { + report_frame_rate(); +} + int main(int argc, char *argv[]) { // First, we need a GraphicsPipe, before we can open a window. @@ -245,11 +282,18 @@ main(int argc, char *argv[]) { EventHandler event_handler(EventQueue::get_global_event_queue()); event_handler.add_hook("escape", event_esc); event_handler.add_hook("q", event_esc); + event_handler.add_hook("f", event_f); // Put something in the scene graph to look at. get_models(render, argc, argv); + // Tick the clock once so we won't count the time spent loading up + // files, above, in our frame rate average. + ClockObject::get_global_clock()->tick(); + start_time = ClockObject::get_global_clock()->get_frame_time(); + start_frame_count = ClockObject::get_global_clock()->get_frame_count(); + // This is our main update loop. Loop here until someone // (e.g. event_esc) sets run_flag to false. @@ -261,6 +305,7 @@ main(int argc, char *argv[]) { engine->render_frame(); } + report_frame_rate(); delete engine; return (0); }