From d72e2da5c5d93b9815267259e8aff92419096b47 Mon Sep 17 00:00:00 2001 From: David Rose Date: Thu, 17 Mar 2005 01:16:28 +0000 Subject: [PATCH] support flat-shading properly in new geom --- panda/src/display/graphicsStateGuardian.cxx | 14 +- panda/src/display/graphicsStateGuardian.h | 6 +- panda/src/dxgsg8/dxGeomMunger8.I | 4 +- panda/src/dxgsg8/dxGeomMunger8.cxx | 24 + panda/src/dxgsg8/dxGeomMunger8.h | 5 +- panda/src/dxgsg8/dxGraphicsStateGuardian8.I | 13 - panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx | 127 ++++-- panda/src/dxgsg8/dxGraphicsStateGuardian8.h | 10 +- panda/src/egg/eggAttributes.I | 70 +++ panda/src/egg/eggAttributes.h | 4 + panda/src/egg/eggCompositePrimitive.cxx | 357 ++++++++------- panda/src/egg/eggCompositePrimitive.h | 7 +- panda/src/egg/eggGroupNode.cxx | 123 ++++- panda/src/egg/eggGroupNode.h | 11 +- panda/src/egg/eggPrimitive.I | 41 ++ panda/src/egg/eggPrimitive.cxx | 428 +++++++++++------- panda/src/egg/eggPrimitive.h | 25 +- panda/src/egg/eggTriangleStrip.cxx | 46 ++ panda/src/egg/eggTriangleStrip.h | 3 + panda/src/egg2pg/eggBinner.cxx | 9 - panda/src/egg2pg/eggLoader.I | 26 ++ panda/src/egg2pg/eggLoader.cxx | 57 ++- panda/src/egg2pg/eggLoader.h | 14 +- panda/src/egg2pg/eggRenderState.I | 6 +- panda/src/egg2pg/eggRenderState.cxx | 19 +- panda/src/egg2pg/eggRenderState.h | 2 +- panda/src/glstuff/glGeomMunger_src.I | 4 +- panda/src/glstuff/glGeomMunger_src.h | 3 +- .../glstuff/glGraphicsStateGuardian_src.cxx | 8 +- .../src/glstuff/glGraphicsStateGuardian_src.h | 2 +- panda/src/gobj/geom.cxx | 5 - panda/src/gobj/qpgeom.I | 6 +- panda/src/gobj/qpgeom.cxx | 165 ++++++- panda/src/gobj/qpgeom.h | 32 +- panda/src/gobj/qpgeomMunger.I | 28 +- panda/src/gobj/qpgeomMunger.cxx | 56 ++- panda/src/gobj/qpgeomMunger.h | 13 +- panda/src/gobj/qpgeomPrimitive.I | 103 +++++ panda/src/gobj/qpgeomPrimitive.cxx | 84 +++- panda/src/gobj/qpgeomPrimitive.h | 33 +- panda/src/gobj/qpgeomTriangles.cxx | 61 ++- panda/src/gobj/qpgeomTriangles.h | 7 +- panda/src/gobj/qpgeomTrifans.cxx | 32 +- panda/src/gobj/qpgeomTrifans.h | 7 +- panda/src/gobj/qpgeomTristrips.cxx | 165 +++++-- panda/src/gobj/qpgeomTristrips.h | 7 +- panda/src/gobj/qpgeomVertexCacheManager.I | 219 +++++++-- panda/src/gobj/qpgeomVertexCacheManager.cxx | 88 +++- panda/src/gobj/qpgeomVertexCacheManager.h | 80 +++- panda/src/gobj/qpgeomVertexData.cxx | 6 +- panda/src/gobj/qpgeomVertexData.h | 2 +- panda/src/gsgbase/graphicsStateGuardianBase.h | 6 +- panda/src/pgraph/cullResult.cxx | 4 +- panda/src/pgraph/cullableObject.I | 18 +- panda/src/pgraph/cullableObject.cxx | 22 + panda/src/pgraph/cullableObject.h | 3 +- panda/src/pgraph/drawCullHandler.cxx | 2 +- panda/src/pgraph/renderState.I | 18 + panda/src/pgraph/renderState.cxx | 21 + panda/src/pgraph/renderState.h | 4 + panda/src/pstatclient/pStatProperties.cxx | 1 + pandatool/src/egg-qtess/eggQtess.cxx | 2 +- pandatool/src/eggbase/eggMultiBase.cxx | 6 +- pandatool/src/eggbase/eggWriter.cxx | 6 +- pandatool/src/eggprogs/eggCrop.cxx | 2 +- pandatool/src/eggprogs/eggToC.cxx | 4 +- pandatool/src/eggprogs/eggTrans.cxx | 8 +- pandatool/src/lwoegg/lwoToEggConverter.cxx | 2 +- pandatool/src/xfileegg/xFileMaker.cxx | 2 +- 69 files changed, 2069 insertions(+), 729 deletions(-) diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index c459854a06..68c88d12b7 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -274,12 +274,12 @@ release_geom(GeomContext *) { // appropriate to this GSG for the indicated state. //////////////////////////////////////////////////////////////////// CPT(qpGeomMunger) GraphicsStateGuardian:: -get_geom_munger(const RenderState *) { +get_geom_munger(const RenderState *state) { // The default implementation returns a munger that does nothing, // but presumably, every kind of GSG needs some special munging // action, so real GSG's will override this to return something more // useful. - return qpGeomMunger::register_munger(new qpGeomMunger); + return qpGeomMunger::register_munger(new qpGeomMunger(this, state)); } //////////////////////////////////////////////////////////////////// @@ -632,7 +632,7 @@ begin_draw_primitives(const qpGeomVertexData *data) { // Description: Draws a series of disconnected triangles. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: -draw_triangles(qpGeomTriangles *) { +draw_triangles(const qpGeomTriangles *) { } //////////////////////////////////////////////////////////////////// @@ -641,8 +641,8 @@ draw_triangles(qpGeomTriangles *) { // Description: Draws a series of triangle strips. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: -draw_tristrips(qpGeomTristrips *primitive) { - PT(qpGeomPrimitive) new_prim = primitive->decompose(); +draw_tristrips(const qpGeomTristrips *primitive) { + CPT(qpGeomPrimitive) new_prim = primitive->decompose(); if (!new_prim->is_of_type(qpGeomTristrips::get_class_type())) { new_prim->draw(this); } @@ -654,8 +654,8 @@ draw_tristrips(qpGeomTristrips *primitive) { // Description: Draws a series of triangle fans. //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: -draw_trifans(qpGeomTrifans *primitive) { - PT(qpGeomPrimitive) new_prim = primitive->decompose(); +draw_trifans(const qpGeomTrifans *primitive) { + CPT(qpGeomPrimitive) new_prim = primitive->decompose(); if (!new_prim->is_of_type(qpGeomTrifans::get_class_type())) { new_prim->draw(this); } diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index b8ce577c0e..9355d061bc 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -144,9 +144,9 @@ public: virtual void finish_decal(); virtual bool begin_draw_primitives(const qpGeomVertexData *vertex_data); - virtual void draw_triangles(qpGeomTriangles *primitive); - virtual void draw_tristrips(qpGeomTristrips *primitive); - virtual void draw_trifans(qpGeomTrifans *primitive); + virtual void draw_triangles(const qpGeomTriangles *primitive); + virtual void draw_tristrips(const qpGeomTristrips *primitive); + virtual void draw_trifans(const qpGeomTrifans *primitive); virtual void end_draw_primitives(); virtual bool framebuffer_bind_to_texture(GraphicsOutput *win, Texture *tex); diff --git a/panda/src/dxgsg8/dxGeomMunger8.I b/panda/src/dxgsg8/dxGeomMunger8.I index 533ce77e2f..0c1a63f6ca 100644 --- a/panda/src/dxgsg8/dxGeomMunger8.I +++ b/panda/src/dxgsg8/dxGeomMunger8.I @@ -23,5 +23,7 @@ // Description: //////////////////////////////////////////////////////////////////// INLINE DXGeomMunger8:: -DXGeomMunger8() { +DXGeomMunger8(GraphicsStateGuardian *gsg, const RenderState *state) : + qpGeomMunger(gsg, state) +{ } diff --git a/panda/src/dxgsg8/dxGeomMunger8.cxx b/panda/src/dxgsg8/dxGeomMunger8.cxx index 679e65cd62..5197f68e55 100644 --- a/panda/src/dxgsg8/dxGeomMunger8.cxx +++ b/panda/src/dxgsg8/dxGeomMunger8.cxx @@ -72,3 +72,27 @@ munge_format_impl(const qpGeomVertexFormat *orig) { PT(qpGeomVertexFormat) new_format = new qpGeomVertexFormat(new_array_format); return qpGeomVertexFormat::register_format(new_format); } + +//////////////////////////////////////////////////////////////////// +// Function: DXGeomMunger8::munge_geom_impl +// Access: Protected, Virtual +// Description: Converts a Geom and/or its data as necessary. +//////////////////////////////////////////////////////////////////// +void DXGeomMunger8:: +munge_geom_impl(CPT(qpGeom) &geom, CPT(qpGeomVertexData) &data) { +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGeomMunger8::compare_to_impl +// Access: Protected, Virtual +// Description: Called to compare two GeomMungers who are known to be +// of the same type, for an apples-to-apples comparison. +// This will never be called on two pointers of a +// different type. +//////////////////////////////////////////////////////////////////// +int DXGeomMunger8:: +compare_to_impl(const qpGeomMunger *other) const { + // const DXGeomMunger8 *om = DCAST(DXGeomMunger8, other); + + return 0; +} diff --git a/panda/src/dxgsg8/dxGeomMunger8.h b/panda/src/dxgsg8/dxGeomMunger8.h index cebe425ce3..267bcc7298 100644 --- a/panda/src/dxgsg8/dxGeomMunger8.h +++ b/panda/src/dxgsg8/dxGeomMunger8.h @@ -21,6 +21,7 @@ #include "pandabase.h" #include "qpgeomMunger.h" +#include "graphicsStateGuardian.h" //////////////////////////////////////////////////////////////////// // Class : DXGeomMunger8 @@ -32,10 +33,12 @@ //////////////////////////////////////////////////////////////////// class EXPCL_PANDADX DXGeomMunger8 : public qpGeomMunger { public: - INLINE DXGeomMunger8(); + INLINE DXGeomMunger8(GraphicsStateGuardian *gsg, const RenderState *state); protected: virtual CPT(qpGeomVertexFormat) munge_format_impl(const qpGeomVertexFormat *orig); + virtual void munge_geom_impl(CPT(qpGeom) &geom, CPT(qpGeomVertexData) &data); + virtual int compare_to_impl(const qpGeomMunger *other) const; public: static TypeHandle get_class_type() { diff --git a/panda/src/dxgsg8/dxGraphicsStateGuardian8.I b/panda/src/dxgsg8/dxGraphicsStateGuardian8.I index 66890e259b..0df8e34edb 100644 --- a/panda/src/dxgsg8/dxGraphicsStateGuardian8.I +++ b/panda/src/dxgsg8/dxGraphicsStateGuardian8.I @@ -188,19 +188,6 @@ set_color_writemask(UINT color_writemask) { } } -//////////////////////////////////////////////////////////////////// -// Function: DXGraphicsStateGuardian8::enable_blend -// Access: -// Description: -//////////////////////////////////////////////////////////////////// -INLINE void DXGraphicsStateGuardian8:: -enable_gouraud_shading(bool val) { - if (_bGouraudShadingOn != val) { - _bGouraudShadingOn = val; - _pD3DDevice->SetRenderState(D3DRS_SHADEMODE, (val ? D3DSHADE_GOURAUD : D3DSHADE_FLAT)); - } -} - INLINE void DXGraphicsStateGuardian8:: enable_primitive_clipping(bool val) { if (_clipping_enabled != val) { diff --git a/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx b/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx index 215c5a1be4..4d97dafa17 100644 --- a/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx +++ b/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx @@ -34,6 +34,7 @@ #include "textureAttrib.h" #include "texGenAttrib.h" #include "lightAttrib.h" +#include "shadeModelAttrib.h" #include "cullFaceAttrib.h" #include "transparencyAttrib.h" #include "alphaTestAttrib.h" @@ -323,8 +324,8 @@ dx_init(void) { _color_writemask = 0xFFFFFFFF; _CurFVFType = 0x0; // guards SetVertexShader fmt - _bGouraudShadingOn = false; - _pD3DDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT); + _bGouraudShadingOn = true; + _pD3DDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD); // this specifies if lighting model uses material color or vertex color // (not related to gouraud/flat shading) @@ -520,21 +521,8 @@ dx_init(void) { } #endif -#ifndef NDEBUG - if(dx_force_backface_culling!=0) { - if((dx_force_backface_culling > 0) && - (dx_force_backface_culling < D3DCULL_FORCE_DWORD)) { - _pD3DDevice->SetRenderState(D3DRS_CULLMODE, dx_force_backface_culling); - } else { - dx_force_backface_culling=0; - if(dxgsg8_cat.is_debug()) - dxgsg8_cat.debug() << "error, invalid value for dx-force-backface-culling\n"; - } - } - _pD3DDevice->SetRenderState(D3DRS_CULLMODE, dx_force_backface_culling); -#else + _cull_face_mode = CullFaceAttrib::M_cull_none; _pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); -#endif _alpha_func = D3DCMP_ALWAYS; _alpha_func_refval = 1.0f; @@ -730,6 +718,22 @@ prepare_lens() { return SUCCEEDED(hr); } +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian8::enable_gouraud_shading +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian8:: +enable_gouraud_shading(bool val) { + if (_bGouraudShadingOn != val) { + if (val) { + modify_state(get_smooth_state()); + } else { + modify_state(get_flat_state()); + } + } +} + //////////////////////////////////////////////////////////////////// // Function: DXGraphicsStateGuardian8::get_blend_func // Access: Protected, Static @@ -1053,9 +1057,6 @@ draw_prim_setup(const Geom *geom) { vertex_size += sizeof(float) * 2; } - // If we have per-vertex colors or normals, we need smooth shading. - // Otherwise we want flat shading for performance reasons. - // Note on fogging: // the fogging expression should really be || (_fog_enabled && (_doFogType==PerVertexFog)) // instead of just || (_fog_enabled), since GOURAUD shading should not be required for PerPixel @@ -1063,10 +1064,17 @@ draw_prim_setup(const Geom *geom) { // but dont force the shading mode to gouraud internally, so you end up with flat-shaded fog colors // (note, TNT does the right thing tho). So I guess we must do gouraud shading for all fog rendering for now // note that if _doFogType==None, _fog_enabled will always be false - - bool need_gouraud_shading = ((_perVertex & (PER_COLOR | (wants_normals() ? PER_NORMAL : 0))) || _fog_enabled); - - enable_gouraud_shading(need_gouraud_shading); + + if ((_perVertex & (PER_COLOR | (wants_normals() ? PER_NORMAL : 0))) || _fog_enabled) { + // We definitely need gouraud shading. + enable_gouraud_shading(true); + } else if ((_perComp & (PER_COLOR | (wants_normals() ? PER_NORMAL : 0)))) { + // We definitely need flat shading. + enable_gouraud_shading(false); + } else { + // Don't care about gouraud shading. + } + set_vertex_format(newFVFflags); return vertex_size; @@ -1517,6 +1525,12 @@ draw_sprite(GeomSprite *geom, GeomContext *gc) { // Note: for DX8, try to use the PointSprite primitive instead of doing all the stuff below + + // see note on fog and gouraud-shading in draw_prim_setup + bool bUseGouraudShadedColor=_fog_enabled; + if (_fog_enabled) { + enable_gouraud_shading(true); + } #ifdef GSG_VERBOSE dxgsg8_cat.debug() << "draw_sprite()" << endl; @@ -1727,9 +1741,6 @@ draw_sprite(GeomSprite *geom, GeomContext *gc) { CurColor = _curD3Dcolor; } - // see note on fog and gouraud-shading in draw_prim_setup - bool bUseGouraudShadedColor=_fog_enabled; - enable_gouraud_shading(_fog_enabled); set_vertex_format(FVFType); #ifdef _DEBUG @@ -2665,13 +2676,13 @@ begin_draw_primitives(const qpGeomVertexData *vertex_data) { // Description: Draws a series of disconnected triangles. //////////////////////////////////////////////////////////////////// void DXGraphicsStateGuardian8:: -draw_triangles(qpGeomTriangles *primitive) { +draw_triangles(const qpGeomTriangles *primitive) { HRESULT hr = _pD3DDevice->DrawIndexedPrimitiveUP (D3DPT_TRIANGLELIST, primitive->get_min_vertex(), primitive->get_max_vertex() - primitive->get_min_vertex() + 1, primitive->get_num_primitives(), - primitive->get_vertices(), + primitive->get_flat_first_vertices(), D3DFMT_INDEX16, _vertex_data->get_array_data(0), _vertex_data->get_format()->get_array(0)->get_stride()); @@ -2903,8 +2914,8 @@ release_texture(TextureContext *tc) { // appropriate to this GSG for the indicated state. //////////////////////////////////////////////////////////////////// CPT(qpGeomMunger) DXGraphicsStateGuardian8:: -get_geom_munger(const RenderState *) { - PT(DXGeomMunger8) munger = new DXGeomMunger8; +get_geom_munger(const RenderState *state) { + PT(DXGeomMunger8) munger = new DXGeomMunger8(this, state); return qpGeomMunger::register_munger(munger); } @@ -3304,6 +3315,26 @@ issue_tex_gen(const TexGenAttrib *attrib) { } } +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian8::issue_shade_model +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void DXGraphicsStateGuardian8:: +issue_shade_model(const ShadeModelAttrib *attrib) { + switch (attrib->get_mode()) { + case ShadeModelAttrib::M_smooth: + _pD3DDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD); + _bGouraudShadingOn = true; + break; + + case ShadeModelAttrib::M_flat: + _pD3DDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT); + _bGouraudShadingOn = false; + break; + } +} + //////////////////////////////////////////////////////////////////// // Function: DXGraphicsStateGuardian8::issue_texture // Access: Public, Virtual @@ -3464,9 +3495,9 @@ issue_depth_write(const DepthWriteAttrib *attrib) { //////////////////////////////////////////////////////////////////// void DXGraphicsStateGuardian8:: issue_cull_face(const CullFaceAttrib *attrib) { - CullFaceAttrib::Mode mode = attrib->get_effective_mode(); + _cull_face_mode = attrib->get_effective_mode(); - switch (mode) { + switch (_cull_face_mode) { case CullFaceAttrib::M_cull_none: _pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); break; @@ -3478,7 +3509,7 @@ issue_cull_face(const CullFaceAttrib *attrib) { break; default: dxgsg8_cat.error() - << "invalid cull face mode " << (int)mode << endl; + << "invalid cull face mode " << (int)_cull_face_mode << endl; break; } } @@ -3881,6 +3912,36 @@ set_read_buffer(const RenderBuffer &rb) { return; } +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian8::get_smooth_state +// Access: Protected, Static +// Description: Returns a RenderState object that represents +// smooth, per-vertex shading. +//////////////////////////////////////////////////////////////////// +CPT(RenderState) DXGraphicsStateGuardian8:: +get_smooth_state() { + static CPT(RenderState) state; + if (state == (RenderState *)NULL) { + state = RenderState::make(ShadeModelAttrib::make(ShadeModelAttrib::M_smooth)); + } + return state; +} + +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian8::get_flat_state +// Access: Protected, Static +// Description: Returns a RenderState object that represents +// flat, per-primitive shading. +//////////////////////////////////////////////////////////////////// +CPT(RenderState) DXGraphicsStateGuardian8:: +get_flat_state() { + static CPT(RenderState) state; + if (state == (RenderState *)NULL) { + state = RenderState::make(ShadeModelAttrib::make(ShadeModelAttrib::M_flat)); + } + return state; +} + //////////////////////////////////////////////////////////////////// // Function: DXGraphicsStateGuardian8::do_auto_rescale_normal // Access: Protected diff --git a/panda/src/dxgsg8/dxGraphicsStateGuardian8.h b/panda/src/dxgsg8/dxGraphicsStateGuardian8.h index adfb916d73..d9ab3a99b0 100644 --- a/panda/src/dxgsg8/dxGraphicsStateGuardian8.h +++ b/panda/src/dxgsg8/dxGraphicsStateGuardian8.h @@ -32,6 +32,7 @@ #include "displayRegion.h" #include "material.h" #include "depthTestAttrib.h" +#include "cullFaceAttrib.h" #include "renderModeAttrib.h" #include "textureApplyAttrib.h" #include "fog.h" @@ -90,7 +91,7 @@ public: virtual void draw_sphere(GeomSphere *geom, GeomContext *gc); virtual bool begin_draw_primitives(const qpGeomVertexData *vertex_data); - virtual void draw_triangles(qpGeomTriangles *primitive); + virtual void draw_triangles(const qpGeomTriangles *primitive); virtual void end_draw_primitives(); virtual TextureContext *prepare_texture(Texture *tex); @@ -122,6 +123,7 @@ public: virtual void issue_fog(const FogAttrib *attrib); virtual void issue_depth_offset(const DepthOffsetAttrib *attrib); virtual void issue_tex_gen(const TexGenAttrib *attrib); + virtual void issue_shade_model(const ShadeModelAttrib *attrib); virtual void bind_light(PointLight *light, int light_id); virtual void bind_light(DirectionalLight *light, int light_id); @@ -163,6 +165,9 @@ protected: INLINE void add_to_FVFBuf(void *data, size_t bytes) ; + static CPT(RenderState) get_smooth_state(); + static CPT(RenderState) get_flat_state(); + void do_auto_rescale_normal(); bool _bDXisReady; @@ -194,7 +199,7 @@ protected: INLINE void enable_fog(bool val); INLINE void enable_zwritemask(bool val); INLINE void set_color_writemask(UINT color_writemask); - INLINE void enable_gouraud_shading(bool val); + void enable_gouraud_shading(bool val); INLINE void set_vertex_format(DWORD NewFvfType); INLINE D3DTEXTUREADDRESS get_texture_wrap_mode(Texture::WrapMode wm) const; @@ -291,6 +296,7 @@ protected: bool _depth_write_enabled; bool _alpha_test_enabled; DWORD _clip_plane_bits; + CullFaceAttrib::Mode _cull_face_mode; RenderModeAttrib::Mode _current_fill_mode; //poinr/wireframe/solid diff --git a/panda/src/egg/eggAttributes.I b/panda/src/egg/eggAttributes.I index 2c883b9862..f3d942d535 100644 --- a/panda/src/egg/eggAttributes.I +++ b/panda/src/egg/eggAttributes.I @@ -59,6 +59,41 @@ clear_normal() { _flags &= ~F_has_normal; } +//////////////////////////////////////////////////////////////////// +// Function: EggAttributes::matches_normal +// Access: Published +// Description: Returns true if this normal matches that of the other +// EggAttributes object, include the morph list. +//////////////////////////////////////////////////////////////////// +INLINE bool EggAttributes:: +matches_normal(const EggAttributes &other) const { + if (((_flags ^ other._flags) & F_has_normal) != 0) { + return false; + } + if (!has_normal()) { + return true; + } + return (get_normal() == other.get_normal() && + _dnormals.compare_to(other._dnormals) == 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggAttributes::copy_normal +// Access: Published +// Description: Sets this normal to be the same as the other's, +// include morphs. If the other has no normal, this +// clears the normal. +//////////////////////////////////////////////////////////////////// +INLINE void EggAttributes:: +copy_normal(const EggAttributes &other) { + if (!other.has_normal()) { + clear_normal(); + } else { + set_normal(other.get_normal()); + _dnormals = other._dnormals; + } +} + //////////////////////////////////////////////////////////////////// // Function: EggAttributes::has_color // Access: Published @@ -105,6 +140,41 @@ clear_color() { _flags &= ~F_has_color; } +//////////////////////////////////////////////////////////////////// +// Function: EggAttributes::matches_color +// Access: Published +// Description: Returns true if this color matches that of the other +// EggAttributes object, include the morph list. +//////////////////////////////////////////////////////////////////// +INLINE bool EggAttributes:: +matches_color(const EggAttributes &other) const { + if (((_flags ^ other._flags) & F_has_color) != 0) { + return false; + } + if (!has_color()) { + return true; + } + return (get_color() == other.get_color() && + _drgbas.compare_to(other._drgbas) == 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggAttributes::copy_color +// Access: Published +// Description: Sets this color to be the same as the other's, +// include morphs. If the other has no color, this +// clears the color. +//////////////////////////////////////////////////////////////////// +INLINE void EggAttributes:: +copy_color(const EggAttributes &other) { + if (!other.has_color()) { + clear_color(); + } else { + set_color(other.get_color()); + _drgbas = other._drgbas; + } +} + //////////////////////////////////////////////////////////////////// // Function: EggAttributes::sorts_less_than // Access: Published diff --git a/panda/src/egg/eggAttributes.h b/panda/src/egg/eggAttributes.h index 4d9ccf8594..6a31bf8c76 100644 --- a/panda/src/egg/eggAttributes.h +++ b/panda/src/egg/eggAttributes.h @@ -49,11 +49,15 @@ PUBLISHED: INLINE const Normald &get_normal() const; INLINE void set_normal(const Normald &normal); INLINE void clear_normal(); + INLINE bool matches_normal(const EggAttributes &other) const; + INLINE void copy_normal(const EggAttributes &other); INLINE bool has_color() const; INLINE Colorf get_color() const; INLINE void set_color(const Colorf &Color); INLINE void clear_color(); + INLINE bool matches_color(const EggAttributes &other) const; + INLINE void copy_color(const EggAttributes &other); void write(ostream &out, int indent_level) const; INLINE bool sorts_less_than(const EggAttributes &other) const; diff --git a/panda/src/egg/eggCompositePrimitive.cxx b/panda/src/egg/eggCompositePrimitive.cxx index ec7aea1136..d24a577aeb 100644 --- a/panda/src/egg/eggCompositePrimitive.cxx +++ b/panda/src/egg/eggCompositePrimitive.cxx @@ -22,6 +22,72 @@ TypeHandle EggCompositePrimitive::_type_handle; +//////////////////////////////////////////////////////////////////// +// Function: EggCompositePrimitive::get_shading +// Access: Published, Virtual +// Description: Returns the shading properties apparent on this +// particular primitive. This returns S_per_vertex if +// the vertices have colors or normals (and they are not +// all the same values), or for a simple primitive, +// S_overall otherwise. A composite primitive may also +// return S_per_face if the individual component +// primitives have colors or normals that are not all +// the same values. +// +// To get the most accurate results, you should call +// clear_shading() on all connected primitives (or on +// all primitives in the egg file), followed by +// determine_shading() on each primitive. You may find +// it easiest to call these methods on the EggData root +// node (they are defined on EggGroupNode). +//////////////////////////////////////////////////////////////////// +EggPrimitive::Shading EggCompositePrimitive:: +get_shading() const { + Shading basic_shading = EggPrimitive::get_shading(); + if (basic_shading == S_per_vertex) { + return basic_shading; + } + if (_components.empty()) { + return S_overall; + } + + // Check if the components all have the same normal. + { + const EggAttributes *first_component = get_component(0); + if (!first_component->has_normal()) { + first_component = this; + } + for (int i = 1; i < get_num_components(); i++) { + const EggAttributes *component = get_component(i); + if (!component->has_normal()) { + component = this; + } + if (!component->matches_normal(*first_component)) { + return S_per_face; + } + } + } + + // Check if the components all have the same color. + { + const EggAttributes *first_component = get_component(0); + if (!first_component->has_color()) { + first_component = this; + } + for (int i = 1; i < get_num_components(); i++) { + const EggAttributes *component = get_component(i); + if (!component->has_color()) { + component = this; + } + if (!component->matches_color(*first_component)) { + return S_per_face; + } + } + } + + return S_overall; +} + //////////////////////////////////////////////////////////////////// // Function: EggCompositePrimitive::triangulate_in_place // Access: Published @@ -52,10 +118,19 @@ triangulate_in_place() { //////////////////////////////////////////////////////////////////// // Function: EggCompositePrimitive::unify_attributes // Access: Published, Virtual -// Description: Applies per-vertex normal and color to all vertices, -// if they are in fact per-vertex (and actually -// different for each vertex), or moves them to the -// primitive if they are all the same. +// Description: If the shading property is S_per_vertex, ensures that +// all vertices have a normal and a color, and the +// overall primitive does not. +// +// If the shading property is S_per_face, and this is a +// composite primitive, ensures that all components have +// a normal and a color, and the vertices and overall +// primitive do not. (If this is a simple primitive, +// S_per_face works the same as S_overall, below). +// +// If the shading property is S_overall, ensures that no +// vertices or components have a normal or a color, and +// the overall primitive does (if any exists at all). // // After this call, either the primitive will have // normals or its vertices will, but not both. Ditto @@ -65,188 +140,114 @@ triangulate_in_place() { // pool. //////////////////////////////////////////////////////////////////// void EggCompositePrimitive:: -unify_attributes() { - // A composite primitive wants to do the same sort of thing with - // components that the fundamental primitive does with vertices. - // But first, we want to make sure that the primitive overall - // attributes have been correctly moved from the vertices, so call - // up to the base class. - EggPrimitive::unify_attributes(); - - // Now the rest of this body is more or less what an EggPrimitive - // does for vertices, modified to work on components. - - Components components; - - // First, go through the components and apply the primitive overall - // attributes to any component that omits them. - bool all_have_normal = true; - bool all_have_color = true; - Components::iterator ci; - for (ci = _components.begin(); ci != _components.end(); ++ci) { - EggAttributes *orig_component = (*ci); - EggAttributes *component = new EggAttributes(*orig_component); - - if (!component->has_normal()) { - if (has_normal()) { - component->set_normal(get_normal()); - component->_dnormals = _dnormals; - } else { - all_have_normal = false; - } - } - if (!component->has_color()) { - if (has_color()) { - component->set_color(get_color()); - component->_drgbas = _drgbas; - } else { - all_have_color = false; - } - } - - components.push_back(component); +unify_attributes(EggPrimitive::Shading shading) { + if (shading == S_unknown) { + shading = get_shading(); } - clear_normal(); - clear_color(); + switch (shading) { + case S_per_vertex: + // Propagate everything to the vertices. + { + iterator pi; + for (pi = begin(); pi != end(); ++pi) { + EggVertex *orig_vertex = (*pi); + PT(EggVertex) vertex = new EggVertex(*orig_vertex); + if (!vertex->has_normal() && has_normal()) { + vertex->copy_normal(*this); + } + if (!vertex->has_color() && has_color()) { + vertex->copy_color(*this); + } - // Now see if any ended up different. - EggAttributes overall; - bool normal_different = false; - bool color_different = false; + vertex = vertex->get_pool()->create_unique_vertex(*vertex); + replace(pi, vertex); + } + Components::iterator ci; + for (ci = _components.begin(); ci != _components.end(); ++ci) { + (*ci)->clear_normal(); + (*ci)->clear_color(); + } + clear_normal(); + clear_color(); + } + break; - Components::iterator vi; - for (vi = components.begin(); vi != components.end(); ++vi) { - EggAttributes *component = (*vi); - if (!all_have_normal) { - component->clear_normal(); - } else if (!normal_different) { - if (!overall.has_normal()) { - overall.set_normal(component->get_normal()); - overall._dnormals = component->_dnormals; - } else if (overall.get_normal() != component->get_normal() || - overall._dnormals.compare_to(component->_dnormals) != 0) { - normal_different = true; + case S_per_face: + // Propagate everything to the components. + { + Components::iterator ci; + for (ci = _components.begin(); ci != _components.end(); ++ci) { + EggAttributes *component = (*ci); + if (!component->has_normal() && has_normal()) { + component->copy_normal(*this); + } + if (!component->has_color() && has_color()) { + component->copy_color(*this); + } + } + iterator pi; + for (pi = begin(); pi != end(); ++pi) { + EggVertex *orig_vertex = (*pi); + if (orig_vertex->has_normal() || orig_vertex->has_color()) { + PT(EggVertex) vertex = new EggVertex(*orig_vertex); + vertex->clear_normal(); + vertex->clear_color(); + + vertex = vertex->get_pool()->create_unique_vertex(*vertex); + replace(pi, vertex); + } + } + clear_normal(); + clear_color(); + } + break; + + case S_overall: + // Remove everything from the vertices and components. + { + iterator pi; + for (pi = begin(); pi != end(); ++pi) { + EggVertex *vertex = (*pi); + if (vertex->has_normal()) { + if (!has_normal()) { + copy_normal(*vertex); + } + vertex->clear_normal(); + } + if (vertex->has_color()) { + if (!has_color()) { + copy_color(*vertex); + } + vertex->clear_color(); + } + } + Components::iterator ci; + for (ci = _components.begin(); ci != _components.end(); ++ci) { + EggAttributes *component = (*ci); + if (component->has_normal()) { + if (!has_normal()) { + copy_normal(*component); + } + component->clear_normal(); + } + if (component->has_color()) { + if (!has_color()) { + copy_color(*component); + } + component->clear_color(); + } } } - if (!all_have_color) { - component->clear_color(); - } else if (!color_different) { - if (!overall.has_color()) { - overall.set_color(component->get_color()); - overall._drgbas = component->_drgbas; - } else if (overall.get_color() != component->get_color() || - overall._drgbas.compare_to(component->_drgbas) != 0) { - color_different = true; - } - } - } + break; - if (!color_different || !normal_different) { - // Ok, it's flat-shaded after all. Go back through and remove - // this stuff from the components. - if (!normal_different) { - set_normal(overall.get_normal()); - _dnormals = overall._dnormals; - } - if (!color_different) { - set_color(overall.get_color()); - _drgbas = overall._drgbas; - } - for (vi = components.begin(); vi != components.end(); ++vi) { - EggAttributes *component = (*vi); - if (!normal_different) { - component->clear_normal(); - } - if (!color_different) { - component->clear_color(); - } - } - } - - // Finally, assign the new components. - for (size_t i = 0; i < components.size(); i++) { - EggAttributes *component = components[i]; - set_component(i, component); - delete component; + case S_unknown: + break; } } //////////////////////////////////////////////////////////////////// -// Function: EggCompositePrimitive::apply_last_attribute -// Access: Published, Virtual -// Description: Sets the last vertex of the triangle (or each -// component) to the primitive normal and/or color, if -// they exist. This reflects the Panda convention of -// storing flat-shaded properties on the last vertex, -// although it is not usually a convention in Egg. -// -// This may introduce redundant vertices to the vertex -// pool. -//////////////////////////////////////////////////////////////////// -void EggCompositePrimitive:: -apply_last_attribute() { - // The first component gets applied to the third vertex, and so on - // from there. - for (int i = 0; i < get_num_components(); i++) { - EggAttributes *component = get_component(i); - - // The significant_change flag is set if we have changed the - // vertex in some important way, that will invalidate it for other - // primitives that might share it. We don't consider *adding* a - // normal where there wasn't one before to be significant, but we - // do consider it significant to change a vertex's normal to - // something different. Similarly for color. - bool significant_change = false; - - EggVertex *orig_vertex = get_vertex(i + 2); - nassertv(orig_vertex != (EggVertex *)NULL); - PT(EggVertex) new_vertex = new EggVertex(*orig_vertex); - - if (component->has_normal() || has_normal()) { - if (component->has_normal()) { - new_vertex->set_normal(component->get_normal()); - new_vertex->_dnormals = component->_dnormals; - } else { - new_vertex->set_normal(get_normal()); - new_vertex->_dnormals = _dnormals; - } - - if (orig_vertex->has_normal() && - (orig_vertex->get_normal() != new_vertex->get_normal() || - orig_vertex->_dnormals.compare_to(new_vertex->_dnormals) != 0)) { - significant_change = true; - } - } - - if (component->has_color() || has_color()) { - if (component->has_color()) { - new_vertex->set_color(component->get_color()); - new_vertex->_drgbas = component->_drgbas; - } else { - new_vertex->set_color(get_color()); - new_vertex->_drgbas = _drgbas; - } - - if (orig_vertex->has_color() && - (orig_vertex->get_color() != new_vertex->get_color() || - orig_vertex->_drgbas.compare_to(new_vertex->_drgbas) != 0)) { - significant_change = true; - } - } - - if (significant_change) { - new_vertex = get_pool()->create_unique_vertex(*new_vertex); - set_vertex(i + 2, new_vertex); - } else { - // Just copy the new attributes back into the pool. - ((EggAttributes *)orig_vertex)->operator = (*new_vertex); - } - } -} - -//////////////////////////////////////////////////////////////////// -// Function: EggCompositePrimitive::post_apply_last_attribute +// Function: EggCompositePrimitive::post_apply_flat_attribute // Access: Published, Virtual // Description: Intended as a followup to apply_last_attribute(), // this also sets an attribute on the first vertices of @@ -254,12 +255,16 @@ apply_last_attribute() { // attribute set, just so they end up with *something*. //////////////////////////////////////////////////////////////////// void EggCompositePrimitive:: -post_apply_last_attribute() { +post_apply_flat_attribute() { if (!empty()) { for (int i = 0; i < (int)size(); i++) { EggVertex *vertex = get_vertex(i); EggAttributes *component = get_component(max(i - 2, 0)); + // Use set_normal() instead of copy_normal(), to avoid getting + // the morphs--we don't want them here, since we're just putting + // a bogus value on the normal anyway. + if (component->has_normal() && !vertex->has_normal()) { vertex->set_normal(component->get_normal()); } else if (has_normal() && !vertex->has_normal()) { diff --git a/panda/src/egg/eggCompositePrimitive.h b/panda/src/egg/eggCompositePrimitive.h index 56a1e24ef3..254370318d 100644 --- a/panda/src/egg/eggCompositePrimitive.h +++ b/panda/src/egg/eggCompositePrimitive.h @@ -37,6 +37,8 @@ PUBLISHED: INLINE EggCompositePrimitive &operator = (const EggCompositePrimitive ©); INLINE ~EggCompositePrimitive(); + virtual Shading get_shading() const; + INLINE int get_num_components() const; INLINE const EggAttributes *get_component(int i) const; INLINE EggAttributes *get_component(int i); @@ -45,9 +47,8 @@ PUBLISHED: INLINE bool triangulate_into(EggGroupNode *container) const; PT(EggCompositePrimitive) triangulate_in_place(); - virtual void unify_attributes(); - virtual void apply_last_attribute(); - virtual void post_apply_last_attribute(); + virtual void unify_attributes(Shading shading); + virtual void post_apply_flat_attribute(); virtual bool cleanup(); protected: diff --git a/panda/src/egg/eggGroupNode.cxx b/panda/src/egg/eggGroupNode.cxx index bebb74081d..a2235a7d9a 100644 --- a/panda/src/egg/eggGroupNode.cxx +++ b/panda/src/egg/eggGroupNode.cxx @@ -784,7 +784,7 @@ mesh_triangles(int flags) { // appear anywhere in the hierarchy. //////////////////////////////////////////////////////////////////// int EggGroupNode:: -remove_unused_vertices() { +remove_unused_vertices(bool recurse) { int num_removed = 0; Children::iterator ci, cnext; @@ -805,7 +805,9 @@ remove_unused_vertices() { } } else if (child->is_of_type(EggGroupNode::get_class_type())) { - num_removed += DCAST(EggGroupNode, child)->remove_unused_vertices(); + if (recurse) { + num_removed += DCAST(EggGroupNode, child)->remove_unused_vertices(recurse); + } } ci = cnext; @@ -823,7 +825,7 @@ remove_unused_vertices() { // primitives removed. //////////////////////////////////////////////////////////////////// int EggGroupNode:: -remove_invalid_primitives() { +remove_invalid_primitives(bool recurse) { int num_removed = 0; Children::iterator ci, cnext; @@ -841,7 +843,9 @@ remove_invalid_primitives() { } } else if (child->is_of_type(EggGroupNode::get_class_type())) { - num_removed += DCAST(EggGroupNode, child)->remove_invalid_primitives(); + if (recurse) { + num_removed += DCAST(EggGroupNode, child)->remove_invalid_primitives(recurse); + } } ci = cnext; @@ -850,6 +854,56 @@ remove_invalid_primitives() { return num_removed; } +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::clear_connected_shading +// Access: Published +// Description: Resets the connected_shading information on all +// primitives at this node and below, so that it may be +// accurately rederived by the next call to +// get_connected_shading(). +// +// It may be a good idea to call +// remove_unused_vertices() at the same time, to +// establish the correct connectivity. +//////////////////////////////////////////////////////////////////// +void EggGroupNode:: +clear_connected_shading() { + Children::iterator ci; + for (ci = _children.begin(); ci != _children.end(); ++ci) { + EggNode *child = *ci; + + if (child->is_of_type(EggPrimitive::get_class_type())) { + EggPrimitive *prim = DCAST(EggPrimitive, child); + prim->clear_connected_shading(); + } else if (child->is_of_type(EggGroupNode::get_class_type())) { + DCAST(EggGroupNode, child)->clear_connected_shading(); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::get_connected_shading +// Access: Published +// Description: Queries the connected_shading information on all +// primitives at this node and below, to ensure that it +// has been completely filled in before we start mucking +// around with vertices. +//////////////////////////////////////////////////////////////////// +void EggGroupNode:: +get_connected_shading() { + Children::iterator ci; + for (ci = _children.begin(); ci != _children.end(); ++ci) { + EggNode *child = *ci; + + if (child->is_of_type(EggPrimitive::get_class_type())) { + EggPrimitive *prim = DCAST(EggPrimitive, child); + prim->get_connected_shading(); + } else if (child->is_of_type(EggGroupNode::get_class_type())) { + DCAST(EggGroupNode, child)->get_connected_shading(); + } + } +} + //////////////////////////////////////////////////////////////////// // Function: EggGroupNode::rebuild_vertex_pool // Access: Published @@ -904,22 +958,31 @@ rebuild_vertex_pool(EggVertexPool *vertex_pool, bool recurse) { // normals or its vertices will, but not both. Ditto // for colors. // +// If use_connected_shading is true, each polygon is +// considered in conjunction with all connected +// polygons; otherwise, each polygon is considered +// individually. +// // This may create redundant vertices in the vertex // pool, so it may be a good idea to follow this up with // remove_unused_vertices(). //////////////////////////////////////////////////////////////////// void EggGroupNode:: -unify_attributes(bool recurse) { +unify_attributes(bool use_connected_shading, bool recurse) { Children::iterator ci; for (ci = _children.begin(); ci != _children.end(); ++ci) { EggNode *child = *ci; if (child->is_of_type(EggPrimitive::get_class_type())) { EggPrimitive *prim = DCAST(EggPrimitive, child); - prim->unify_attributes(); + if (use_connected_shading) { + prim->unify_attributes(prim->get_connected_shading()); + } else { + prim->unify_attributes(prim->get_shading()); + } } else if (child->is_of_type(EggGroupNode::get_class_type())) { if (recurse) { - DCAST(EggGroupNode, child)->unify_attributes(recurse); + DCAST(EggGroupNode, child)->unify_attributes(use_connected_shading, recurse); } } } @@ -930,9 +993,10 @@ unify_attributes(bool recurse) { // Access: Published // Description: Sets the last vertex of the triangle (or each // component) to the primitive normal and/or color, if -// they exist. This reflects the Panda convention of -// storing flat-shaded properties on the last vertex, -// although it is not usually a convention in Egg. +// the primitive is flat-shaded. This reflects the +// OpenGL convention of storing flat-shaded properties on +// the last vertex, although it is not usually a +// convention in Egg. // // This may create redundant vertices in the vertex // pool, so it may be a good idea to follow this up with @@ -956,7 +1020,38 @@ apply_last_attribute(bool recurse) { } //////////////////////////////////////////////////////////////////// -// Function: EggGroupNode::post_apply_last_attribute +// Function: EggGroupNode::apply_first_attribute +// Access: Published +// Description: Sets the first vertex of the triangle (or each +// component) to the primitive normal and/or color, if +// the primitive is flat-shaded. This reflects the +// DirectX convention of storing flat-shaded properties on +// the first vertex, although it is not usually a +// convention in Egg. +// +// This may create redundant vertices in the vertex +// pool, so it may be a good idea to follow this up with +// remove_unused_vertices(). +//////////////////////////////////////////////////////////////////// +void EggGroupNode:: +apply_first_attribute(bool recurse) { + Children::iterator ci; + for (ci = _children.begin(); ci != _children.end(); ++ci) { + EggNode *child = *ci; + + if (child->is_of_type(EggPrimitive::get_class_type())) { + EggPrimitive *prim = DCAST(EggPrimitive, child); + prim->apply_first_attribute(); + } else if (child->is_of_type(EggGroupNode::get_class_type())) { + if (recurse) { + DCAST(EggGroupNode, child)->apply_first_attribute(recurse); + } + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggGroupNode::post_apply_flat_attribute // Access: Published // Description: Intended as a followup to apply_last_attribute(), // this also sets an attribute on the first vertices of @@ -964,17 +1059,17 @@ apply_last_attribute(bool recurse) { // attribute set, just so they end up with *something*. //////////////////////////////////////////////////////////////////// void EggGroupNode:: -post_apply_last_attribute(bool recurse) { +post_apply_flat_attribute(bool recurse) { Children::iterator ci; for (ci = _children.begin(); ci != _children.end(); ++ci) { EggNode *child = *ci; if (child->is_of_type(EggPrimitive::get_class_type())) { EggPrimitive *prim = DCAST(EggPrimitive, child); - prim->post_apply_last_attribute(); + prim->post_apply_flat_attribute(); } else if (child->is_of_type(EggGroupNode::get_class_type())) { if (recurse) { - DCAST(EggGroupNode, child)->post_apply_last_attribute(recurse); + DCAST(EggGroupNode, child)->post_apply_flat_attribute(recurse); } } } diff --git a/panda/src/egg/eggGroupNode.h b/panda/src/egg/eggGroupNode.h index edbf7bbcf8..5bce37e08e 100644 --- a/panda/src/egg/eggGroupNode.h +++ b/panda/src/egg/eggGroupNode.h @@ -140,12 +140,15 @@ PUBLISHED: int triangulate_polygons(int flags); void mesh_triangles(int flags); - int remove_unused_vertices(); - int remove_invalid_primitives(); + int remove_unused_vertices(bool recurse); + int remove_invalid_primitives(bool recurse); + void clear_connected_shading(); + void get_connected_shading(); void rebuild_vertex_pool(EggVertexPool *vertex_pool, bool recurse); - void unify_attributes(bool recurse); + void unify_attributes(bool use_connected_shading, bool recurse); void apply_last_attribute(bool recurse); - void post_apply_last_attribute(bool recurse); + void apply_first_attribute(bool recurse); + void post_apply_flat_attribute(bool recurse); virtual bool has_primitives() const; virtual bool joint_has_primitives() const; virtual bool has_normals() const; diff --git a/panda/src/egg/eggPrimitive.I b/panda/src/egg/eggPrimitive.I index 1d723c4493..fa62fb3d66 100644 --- a/panda/src/egg/eggPrimitive.I +++ b/panda/src/egg/eggPrimitive.I @@ -25,6 +25,7 @@ INLINE EggPrimitive:: EggPrimitive(const string &name): EggNode(name) { _bface = false; + _connected_shading = S_unknown; } //////////////////////////////////////////////////////////////////// @@ -41,6 +42,7 @@ EggPrimitive(const EggPrimitive ©) : _bface(copy._bface) { copy_vertices(copy); + _connected_shading = S_unknown; } //////////////////////////////////////////////////////////////////// @@ -56,6 +58,7 @@ operator = (const EggPrimitive ©) { _textures = copy._textures; _material = copy._material; _bface = copy._bface; + _connected_shading = S_unknown; return *this; } @@ -69,6 +72,44 @@ INLINE EggPrimitive:: clear(); } +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::clear_connected_shading +// Access: Published +// Description: Resets the connected_shading member in this +// primitive, so that get_connected_shading() will +// recompute a new value. +//////////////////////////////////////////////////////////////////// +INLINE void EggPrimitive:: +clear_connected_shading() { + _connected_shading = S_unknown; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::get_connected_shading +// Access: Published +// Description: Determines what sort of shading properties this +// primitive's connected neighbors have. +// +// To get the most accurate results, you should first +// call clear_connected_shading() on all connected +// primitives (or on all primitives in the egg file). +// It might also be a good idea to call +// remove_unused_vertices() to ensure proper +// connectivity. +// +// You may find it easiest to call these other methods +// on the EggData root node (they are defined on +// EggGroupNode). +//////////////////////////////////////////////////////////////////// +INLINE EggPrimitive::Shading EggPrimitive:: +get_connected_shading() { + if (_connected_shading == S_unknown) { + set_connected_shading(S_unknown, this); + } + + return _connected_shading; +} + //////////////////////////////////////////////////////////////////// // Function: EggPrimitive::set_texture // Access: Published diff --git a/panda/src/egg/eggPrimitive.cxx b/panda/src/egg/eggPrimitive.cxx index f98ef941d7..ffa3bf4c41 100644 --- a/panda/src/egg/eggPrimitive.cxx +++ b/panda/src/egg/eggPrimitive.cxx @@ -190,6 +190,69 @@ determine_bin() { return result; } + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::get_shading +// Access: Published, Virtual +// Description: Returns the shading properties apparent on this +// particular primitive. This returns S_per_vertex if +// the vertices have colors or normals (and they are not +// all the same values), or for a simple primitive, +// S_overall otherwise. A composite primitive may also +// return S_per_face if the individual component +// primitives have colors or normals that are not all +// the same values. +// +// To get the most accurate results, you should call +// clear_shading() on all connected primitives (or on +// all primitives in the egg file), followed by +// determine_shading() on each primitive. You may find +// it easiest to call these methods on the EggData root +// node (they are defined on EggGroupNode). +//////////////////////////////////////////////////////////////////// +EggPrimitive::Shading EggPrimitive:: +get_shading() const { + if (empty()) { + return S_overall; + } + + if (has_vertex_normal()) { + // Check if the vertices all have the same normal. + const EggAttributes *first_vertex = get_vertex(0); + if (!first_vertex->has_normal()) { + first_vertex = this; + } + for (int i = 1; i < get_num_vertices(); i++) { + const EggAttributes *vertex = get_vertex(i); + if (!vertex->has_normal()) { + vertex = this; + } + if (!vertex->matches_normal(*first_vertex)) { + return S_per_vertex; + } + } + } + + if (has_vertex_color()) { + // Check if the vertices all have the same color. + const EggAttributes *first_vertex = get_vertex(0); + if (!first_vertex->has_color()) { + first_vertex = this; + } + for (int i = 1; i < get_num_vertices(); i++) { + const EggAttributes *vertex = get_vertex(i); + if (!vertex->has_color()) { + vertex = this; + } + if (!vertex->matches_color(*first_vertex)) { + return S_per_vertex; + } + } + } + + return S_overall; +} + //////////////////////////////////////////////////////////////////// // Function: EggPrimitive::copy_attributes // Access: Published @@ -221,8 +284,10 @@ copy_attributes(const EggPrimitive &other) { // Description: Returns true if any vertex on the primitive has a // specific normal set, false otherwise. // -// For the most accurate measure of this, call -// unify_attributes() first. +// If you call unify_attributes() first, this will also +// return false even if all the vertices were set to the +// same value (since unify_attributes() removes +// redundant vertex properties). //////////////////////////////////////////////////////////////////// bool EggPrimitive:: has_vertex_normal() const { @@ -241,8 +306,10 @@ has_vertex_normal() const { // Description: Returns true if any vertex on the primitive has a // specific color set, false otherwise. // -// For the most accurate measure of this, call -// unify_attributes() first. +// If you call unify_attributes() first, this will also +// return false even if all the vertices were set to the +// same value (since unify_attributes() removes +// redundant vertex properties). //////////////////////////////////////////////////////////////////// bool EggPrimitive:: has_vertex_color() const { @@ -255,29 +322,22 @@ has_vertex_color() const { return false; } -//////////////////////////////////////////////////////////////////// -// Function: EggPrimitive::is_flat_shaded -// Access: Published -// Description: Returns true if the primitive is flat shaded, meaning -// it has no per-vertex normal and no per-vertex color -// (but in the case of a composite primitive, it may -// still have per-component normal and color). -// -// For the most accurate measure of this, call -// unify_attributes() first. -//////////////////////////////////////////////////////////////////// -bool EggPrimitive:: -is_flat_shaded() const { - return !has_vertex_normal() && !has_vertex_color(); -} - //////////////////////////////////////////////////////////////////// // Function: EggPrimitive::unify_attributes // Access: Published, Virtual -// Description: Applies per-vertex normal and color to all vertices, -// if they are in fact per-vertex (and actually -// different for each vertex), or moves them to the -// primitive if they are all the same. +// Description: If the shading property is S_per_vertex, ensures that +// all vertices have a normal and a color, and the +// overall primitive does not. +// +// If the shading property is S_per_face, and this is a +// composite primitive, ensures that all components have +// a normal and a color, and the vertices and overall +// primitive do not. (If this is a simple primitive, +// S_per_face works the same as S_overall, below). +// +// If the shading property is S_overall, ensures that no +// vertices or components have a normal or a color, and +// the overall primitive does (if any exists at all). // // After this call, either the primitive will have // normals or its vertices will, but not both. Ditto @@ -287,109 +347,59 @@ is_flat_shaded() const { // pool. //////////////////////////////////////////////////////////////////// void EggPrimitive:: -unify_attributes() { - typedef pvector< PT(EggVertex) > Vertices; - Vertices vertices; - - // First, go through the vertices and apply the primitive overall - // attributes to any vertex that omits them. - bool all_have_normal = true; - bool all_have_color = true; - iterator pi; - for (pi = begin(); pi != end(); ++pi) { - EggVertex *orig_vertex = (*pi); - PT(EggVertex) vertex = new EggVertex(*orig_vertex); - - if (!vertex->has_normal()) { - if (has_normal()) { - vertex->set_normal(get_normal()); - vertex->_dnormals = _dnormals; - } else { - all_have_normal = false; - } - } - if (!vertex->has_color()) { - if (has_color()) { - vertex->set_color(get_color()); - vertex->_drgbas = _drgbas; - } else { - all_have_color = false; - } - } - - vertices.push_back(vertex); +unify_attributes(EggPrimitive::Shading shading) { + if (shading == S_unknown) { + shading = get_shading(); } - clear_normal(); - clear_color(); + switch (shading) { + case S_per_vertex: + // Propagate everything to the vertices. + { + iterator pi; + for (pi = begin(); pi != end(); ++pi) { + EggVertex *orig_vertex = (*pi); + PT(EggVertex) vertex = new EggVertex(*orig_vertex); + if (!vertex->has_normal() && has_normal()) { + vertex->copy_normal(*this); + } + if (!vertex->has_color() && has_color()) { + vertex->copy_color(*this); + } - // Now see if any ended up different. - EggAttributes overall; - bool normal_different = false; - bool color_different = false; + vertex = vertex->get_pool()->create_unique_vertex(*vertex); + replace(pi, vertex); + } + clear_normal(); + clear_color(); + } + break; - Vertices::iterator vi; - for (vi = vertices.begin(); vi != vertices.end(); ++vi) { - EggVertex *vertex = (*vi); - if (!all_have_normal) { - vertex->clear_normal(); - } else if (!normal_different) { - if (!overall.has_normal()) { - overall.set_normal(vertex->get_normal()); - overall._dnormals = vertex->_dnormals; - } else if (overall.get_normal() != vertex->get_normal() || - overall._dnormals.compare_to(vertex->_dnormals) != 0) { - normal_different = true; + case S_per_face: + case S_overall: + // Remove everything from the vertices. + { + iterator pi; + for (pi = begin(); pi != end(); ++pi) { + EggVertex *vertex = (*pi); + if (vertex->has_normal()) { + if (!has_normal()) { + copy_normal(*vertex); + } + vertex->clear_normal(); + } + if (vertex->has_color()) { + if (!has_color()) { + copy_color(*vertex); + } + vertex->clear_color(); + } } } - if (!all_have_color) { - vertex->clear_color(); - } else if (!color_different) { - if (!overall.has_color()) { - overall.set_color(vertex->get_color()); - overall._drgbas = vertex->_drgbas; - } else if (overall.get_color() != vertex->get_color() || - overall._drgbas.compare_to(vertex->_drgbas) != 0) { - color_different = true; - } - } - } + break; - if (!overall.has_normal()) { - normal_different = true; - } - if (!overall.has_color()) { - color_different = true; - } - - if (!color_different || !normal_different) { - // Ok, it's flat-shaded after all. Go back through and remove - // this stuff from the vertices. - if (!normal_different) { - set_normal(overall.get_normal()); - _dnormals = overall._dnormals; - } - if (!color_different) { - set_color(overall.get_color()); - _drgbas = overall._drgbas; - } - for (vi = vertices.begin(); vi != vertices.end(); ++vi) { - EggVertex *vertex = (*vi); - if (!normal_different) { - vertex->clear_normal(); - } - if (!color_different) { - vertex->clear_color(); - } - } - } - - // Finally, move the new vertices to the vertex pool. - EggVertexPool *vertex_pool = get_pool(); - for (size_t i = 0; i < vertices.size(); i++) { - EggVertex *vertex = vertices[i]; - vertex = vertex_pool->create_unique_vertex(*vertex); - set_vertex(i, vertex); + case S_unknown: + break; } } @@ -399,7 +409,7 @@ unify_attributes() { // Description: Sets the last vertex of the triangle (or each // component) to the primitive normal and/or color, if // the primitive is flat-shaded. This reflects the -// Panda convention of storing flat-shaded properties on +// OpenGL convention of storing flat-shaded properties on // the last vertex, although it is not usually a // convention in Egg. // @@ -409,50 +419,32 @@ unify_attributes() { void EggPrimitive:: apply_last_attribute() { if (!empty()) { - // The significant_change flag is set if we have changed the - // vertex in some important way, that will invalidate it for other - // primitives that might share it. We don't consider *adding* a - // normal where there wasn't one before to be significant, but we - // do consider it significant to change a vertex's normal to - // something different. Similarly for color. - bool significant_change = false; - - EggVertex *orig_vertex = get_vertex(size() - 1); - PT(EggVertex) new_vertex = new EggVertex(*orig_vertex); - - if (has_normal()) { - new_vertex->set_normal(get_normal()); - new_vertex->_dnormals = _dnormals; - - if (orig_vertex->has_normal() && - (orig_vertex->get_normal() != new_vertex->get_normal() || - orig_vertex->_dnormals.compare_to(new_vertex->_dnormals) != 0)) { - significant_change = true; - } - } - if (has_color()) { - new_vertex->set_color(get_color()); - new_vertex->_drgbas = _drgbas; - - if (orig_vertex->has_color() && - (orig_vertex->get_color() != new_vertex->get_color() || - orig_vertex->_drgbas.compare_to(new_vertex->_drgbas) != 0)) { - significant_change = true; - } - } - - if (significant_change) { - new_vertex = get_pool()->create_unique_vertex(*new_vertex); - set_vertex(size() - 1, new_vertex); - } else { - // Just copy the new attributes back into the pool. - ((EggAttributes *)orig_vertex)->operator = (*new_vertex); - } + do_apply_flat_attribute(size() - 1, this); } } //////////////////////////////////////////////////////////////////// -// Function: EggPrimitive::post_apply_last_attribute +// Function: EggPrimitive::apply_first_attribute +// Access: Published, Virtual +// Description: Sets the first vertex of the triangle (or each +// component) to the primitive normal and/or color, if +// the primitive is flat-shaded. This reflects the +// DirectX convention of storing flat-shaded properties on +// the first vertex, although it is not usually a +// convention in Egg. +// +// This may introduce redundant vertices to the vertex +// pool. +//////////////////////////////////////////////////////////////////// +void EggPrimitive:: +apply_first_attribute() { + if (!empty()) { + do_apply_flat_attribute(0, this); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::post_apply_flat_attribute // Access: Published, Virtual // Description: Intended as a followup to apply_last_attribute(), // this also sets an attribute on the first vertices of @@ -460,11 +452,15 @@ apply_last_attribute() { // attribute set, just so they end up with *something*. //////////////////////////////////////////////////////////////////// void EggPrimitive:: -post_apply_last_attribute() { +post_apply_flat_attribute() { if (!empty()) { for (int i = 0; i < (int)size(); i++) { EggVertex *vertex = get_vertex(i); + // Use set_normal() instead of copy_normal(), to avoid getting + // the morphs--we don't want them here, since we're just putting + // a bogus value on the normal anyway. + if (has_normal() && !vertex->has_normal()) { vertex->set_normal(get_normal()); } @@ -1042,3 +1038,111 @@ r_apply_texmats(EggTextureCollection &textures) { _textures.swap(new_textures); } + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::apply_attribute_to_vertex +// Access: Protected +// Description: This is used to implement apply_first_attribute() and +// apply_last_attribute(). It copies the indicated +// attributes to the specified vertex. +//////////////////////////////////////////////////////////////////// +void EggPrimitive:: +do_apply_flat_attribute(int vertex_index, EggAttributes *attrib) { + // The significant_change flag is set if we have changed the + // vertex in some important way, that will invalidate it for other + // primitives that might share it. We don't consider *adding* a + // normal where there wasn't one before to be significant, but we + // do consider it significant to change a vertex's normal to + // something different. Similarly for color. + bool significant_change = false; + + EggVertex *orig_vertex = get_vertex(vertex_index); + PT(EggVertex) new_vertex = new EggVertex(*orig_vertex); + + if (attrib->has_normal()) { + new_vertex->copy_normal(*attrib); + + if (orig_vertex->has_normal() && + !orig_vertex->matches_normal(*new_vertex)) { + significant_change = true; + } + } else if (has_normal()) { + new_vertex->copy_normal(*this); + + if (orig_vertex->has_normal() && + !orig_vertex->matches_normal(*new_vertex)) { + significant_change = true; + } + } + + if (attrib->has_color()) { + new_vertex->copy_color(*attrib); + + if (orig_vertex->has_color() && + !orig_vertex->matches_color(*new_vertex)) { + significant_change = true; + } + } else if (has_color()) { + new_vertex->copy_color(*this); + + if (orig_vertex->has_color() && + !orig_vertex->matches_color(*new_vertex)) { + significant_change = true; + } + } + + if (significant_change) { + new_vertex = get_pool()->create_unique_vertex(*new_vertex); + set_vertex(vertex_index, new_vertex); + } else { + // Just copy the new attributes back into the pool. + ((EggAttributes *)orig_vertex)->operator = (*new_vertex); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::set_connected_shading +// Access: Private +// Description: Recursively updates the connected_shading member in +// all connected primitives. +//////////////////////////////////////////////////////////////////// +void EggPrimitive:: +set_connected_shading(EggPrimitive::Shading shading, + const EggAttributes *neighbor) { + bool propagate = false; + + if (_connected_shading == S_unknown) { + // We haven't visited this node before; propagate now. + _connected_shading = get_shading(); + propagate = true; + } + + if (shading > _connected_shading) { + // More specific information just came in. Save it, and propagate + // it to all connected primitives. + _connected_shading = shading; + propagate = true; + + } else if (shading == S_overall && _connected_shading == S_overall) { + // If both neighbors are overall shaded, check if the two + // neighbors have different properties. If they do, elevate to + // per_face. + if (!matches_normal(*neighbor) || !matches_color(*neighbor)) { + _connected_shading = S_per_face; + propagate = true; + } + } + + if (propagate) { + Vertices::const_iterator vi; + for (vi = _vertices.begin(); vi != _vertices.end(); ++vi) { + EggVertex *vertex = (*vi); + EggVertex::PrimitiveRef::const_iterator pi; + for (pi = vertex->pref_begin(); + pi != vertex->pref_end(); + ++pi) { + (*pi)->set_connected_shading(_connected_shading, this); + } + } + } +} diff --git a/panda/src/egg/eggPrimitive.h b/panda/src/egg/eggPrimitive.h index 9cdc3e15b3..0393651d7f 100644 --- a/panda/src/egg/eggPrimitive.h +++ b/panda/src/egg/eggPrimitive.h @@ -66,6 +66,15 @@ private: // Here begins the actual public interface to EggPrimitive. PUBLISHED: + enum Shading { + // The order here is important. The later choices are more + // specific than the earlier ones. + S_unknown, + S_overall, + S_per_face, + S_per_vertex + }; + INLINE EggPrimitive(const string &name = ""); INLINE EggPrimitive(const EggPrimitive ©); INLINE EggPrimitive &operator = (const EggPrimitive ©); @@ -78,6 +87,10 @@ PUBLISHED: virtual EggRenderMode *determine_draw_order(); virtual EggRenderMode *determine_bin(); + virtual Shading get_shading() const; + INLINE void clear_connected_shading(); + INLINE Shading get_connected_shading(); + INLINE void set_texture(EggTexture *texture); INLINE bool has_texture() const; INLINE bool has_texture(EggTexture *texture) const; @@ -101,11 +114,11 @@ PUBLISHED: bool has_vertex_normal() const; bool has_vertex_color() const; - bool is_flat_shaded() const; - virtual void unify_attributes(); + virtual void unify_attributes(Shading shading); virtual void apply_last_attribute(); - virtual void post_apply_last_attribute(); + virtual void apply_first_attribute(); + virtual void post_apply_flat_attribute(); virtual void reverse_vertex_ordering(); virtual bool cleanup(); @@ -180,7 +193,6 @@ protected: virtual void prepare_add_vertex(EggVertex *vertex, int i, int n); virtual void prepare_remove_vertex(EggVertex *vertex, int i, int n); - protected: void write_body(ostream &out, int indent_level) const; @@ -190,12 +202,17 @@ protected: virtual void r_flatten_transforms(); virtual void r_apply_texmats(EggTextureCollection &textures); + void do_apply_flat_attribute(int vertex_index, EggAttributes *attrib); + +private: + void set_connected_shading(Shading shading, const EggAttributes *neighbor); private: typedef vector_PT_EggTexture Textures; Textures _textures; PT_EggMaterial _material; bool _bface; + Shading _connected_shading; public: diff --git a/panda/src/egg/eggTriangleStrip.cxx b/panda/src/egg/eggTriangleStrip.cxx index 2a94f1d614..6aae88b449 100644 --- a/panda/src/egg/eggTriangleStrip.cxx +++ b/panda/src/egg/eggTriangleStrip.cxx @@ -28,6 +28,52 @@ TypeHandle EggTriangleStrip::_type_handle; +//////////////////////////////////////////////////////////////////// +// Function: EggTriangleStrip::apply_last_attribute +// Access: Published, Virtual +// Description: Sets the last vertex of the triangle (or each +// component) to the primitive normal and/or color, if +// the primitive is flat-shaded. This reflects the +// OpenGL convention of storing flat-shaded properties on +// the last vertex, although it is not usually a +// convention in Egg. +// +// This may introduce redundant vertices to the vertex +// pool. +//////////////////////////////////////////////////////////////////// +void EggTriangleStrip:: +apply_last_attribute() { + // The first component gets applied to the third vertex, and so on + // from there. + for (int i = 0; i < get_num_components(); i++) { + EggAttributes *component = get_component(i); + do_apply_flat_attribute(i + 2, component); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggTriangleStrip::apply_first_attribute +// Access: Published, Virtual +// Description: Sets the first vertex of the triangle (or each +// component) to the primitive normal and/or color, if +// the primitive is flat-shaded. This reflects the +// DirectX convention of storing flat-shaded properties +// on the first vertex, although it is not usually a +// convention in Egg. +// +// This may introduce redundant vertices to the vertex +// pool. +//////////////////////////////////////////////////////////////////// +void EggTriangleStrip:: +apply_first_attribute() { + // The first component gets applied to the first vertex, and so on + // from there. + for (int i = 0; i < get_num_components(); i++) { + EggAttributes *component = get_component(i); + do_apply_flat_attribute(i, component); + } +} + //////////////////////////////////////////////////////////////////// // Function: EggTriangleStrip::write // Access: Published, Virtual diff --git a/panda/src/egg/eggTriangleStrip.h b/panda/src/egg/eggTriangleStrip.h index 02da896017..ca44f7fb40 100644 --- a/panda/src/egg/eggTriangleStrip.h +++ b/panda/src/egg/eggTriangleStrip.h @@ -35,6 +35,9 @@ PUBLISHED: INLINE EggTriangleStrip(const EggTriangleStrip ©); INLINE EggTriangleStrip &operator = (const EggTriangleStrip ©); + virtual void apply_last_attribute(); + virtual void apply_first_attribute(); + virtual void write(ostream &out, int indent_level) const; protected: diff --git a/panda/src/egg2pg/eggBinner.cxx b/panda/src/egg2pg/eggBinner.cxx index d385827d76..b8b7db3aa9 100644 --- a/panda/src/egg2pg/eggBinner.cxx +++ b/panda/src/egg2pg/eggBinner.cxx @@ -95,15 +95,6 @@ sorts_less(int bin_number, const EggNode *a, const EggNode *b) { return (compare < 0); } - // If the render state indicates indexed, meaning we keep the - // existing vertex pools, then different pools get sorted - // separately. - if (rsa->_indexed) { - if (pa->get_pool() != pb->get_pool()) { - return pa->get_pool() < pb->get_pool(); - } - } - return false; } diff --git a/panda/src/egg2pg/eggLoader.I b/panda/src/egg2pg/eggLoader.I index 5e5284cab4..04c4bf2a7b 100644 --- a/panda/src/egg2pg/eggLoader.I +++ b/panda/src/egg2pg/eggLoader.I @@ -17,6 +17,32 @@ //////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::PrimitiveUnifier::operator < +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggLoader::PrimitiveUnifier:: +PrimitiveUnifier(const qpGeomPrimitive *prim) : + _type(prim->get_type()), + _shade_model(prim->get_shade_model()) +{ +} + + +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::PrimitiveUnifier::operator < +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool EggLoader::PrimitiveUnifier:: +operator < (const PrimitiveUnifier &other) const { + if (_type != other._type) { + return _type < other._type; + } + return _shade_model < other._shade_model; +} + //////////////////////////////////////////////////////////////////// // Function: EggLoader::VertexPoolTransform::operator < // Access: Public diff --git a/panda/src/egg2pg/eggLoader.cxx b/panda/src/egg2pg/eggLoader.cxx index 3a41c8e69d..925af11abd 100644 --- a/panda/src/egg2pg/eggLoader.cxx +++ b/panda/src/egg2pg/eggLoader.cxx @@ -157,8 +157,13 @@ build_graph() { // First, load up all of the textures. load_textures(); + // Clean up the vertices. + _data->clear_connected_shading(); + _data->remove_unused_vertices(true); + _data->unify_attributes(true, true); + // Then bin up the polysets and LOD nodes. - _data->remove_invalid_primitives(); + _data->remove_invalid_primitives(true); EggBinner binner(*this); binner.make_bins(_data); @@ -1439,19 +1444,11 @@ make_polyset(EggBin *egg_bin, PandaNode *parent) { return NULL; } - // In the normal case--not indexed--we can generate an optimal - // vertex pool for the polygons in the bin (which translates - // directly to an optimal GeomVertexData structure). However, if - // the indexed flag is set on these polygons, we don't do this, - // since the indexed flag means to use the supplied vertex pool - // exactly as it is. - PT(EggVertexPool) vertex_pool; - if (!render_state->_indexed) { - vertex_pool = new EggVertexPool("bin"); - egg_bin->rebuild_vertex_pool(vertex_pool, false); - } else { - vertex_pool = first_prim->get_pool(); - } + // Generate an optimal vertex pool for the polygons within just the + // bin (which translates directly to an optimal GeomVertexData + // structure). + PT(EggVertexPool) vertex_pool = new EggVertexPool("bin"); + egg_bin->rebuild_vertex_pool(vertex_pool, false); if (egg_mesh) { // If we're using the mesher, mesh now. @@ -1463,13 +1460,11 @@ make_polyset(EggBin *egg_bin, PandaNode *parent) { egg_bin->triangulate_polygons(EggGroupNode::T_polygon | EggGroupNode::T_convex); } - if (!render_state->_indexed) { - // Now that we've meshed, apply the per-prim attributes onto the - // vertices, so we can copy them to the GeomVertexData. - egg_bin->apply_last_attribute(false); - egg_bin->post_apply_last_attribute(false); - vertex_pool->remove_unused_vertices(); - } + // Now that we've meshed, apply the per-prim attributes onto the + // vertices, so we can copy them to the GeomVertexData. + egg_bin->apply_first_attribute(false); + egg_bin->post_apply_flat_attribute(false); + vertex_pool->remove_unused_vertices(); // vertex_pool->write(cerr, 0); // egg_bin->write(cerr, 0); @@ -1480,7 +1475,7 @@ make_polyset(EggBin *egg_bin, PandaNode *parent) { for (ci = egg_bin->begin(); ci != egg_bin->end(); ++ci) { EggPrimitive *egg_prim; DCAST_INTO_R(egg_prim, (*ci), NULL); - make_primitive(egg_prim, primitives); + make_primitive(render_state, egg_prim, primitives); } if (!primitives.empty()) { @@ -1977,7 +1972,8 @@ make_vertex_data(EggVertexPool *vertex_pool, const LMatrix4d &transform) { // indicated EggPrimitive, and adds it to the set. //////////////////////////////////////////////////////////////////// void EggLoader:: -make_primitive(EggPrimitive *egg_prim, EggLoader::Primitives &primitives) { +make_primitive(const EggRenderState *render_state, EggPrimitive *egg_prim, + EggLoader::Primitives &primitives) { PT(qpGeomPrimitive) primitive; if (egg_prim->is_of_type(EggPolygon::get_class_type())) { if (egg_prim->size() == 3) { @@ -1989,13 +1985,26 @@ make_primitive(EggPrimitive *egg_prim, EggLoader::Primitives &primitives) { if (primitive == (qpGeomPrimitive *)NULL) { // Don't know how to make this kind of primitive. + egg2pg_cat.warning() + << "Ignoring " << egg_prim->get_class_type() << "\n"; return; } + if (render_state->_flat_shaded) { + primitive->set_shade_model(qpGeomPrimitive::SM_flat_first_vertex); + + } else if (egg_prim->get_shading() == EggPrimitive::S_overall) { + primitive->set_shade_model(qpGeomPrimitive::SM_uniform); + + } else { + primitive->set_shade_model(qpGeomPrimitive::SM_smooth); + } + // Insert the primitive into the set, but if we already have a // primitive of that type, reset the pointer to that one instead. + PrimitiveUnifier pu(primitive); pair result = - primitives.insert(Primitives::value_type(primitive->get_type(), primitive)); + primitives.insert(Primitives::value_type(pu, primitive)); primitive = (*result.first).second; // Now add the vertices. diff --git a/panda/src/egg2pg/eggLoader.h b/panda/src/egg2pg/eggLoader.h index 972dde6a1c..31edb8c8a7 100644 --- a/panda/src/egg2pg/eggLoader.h +++ b/panda/src/egg2pg/eggLoader.h @@ -57,6 +57,7 @@ class CollisionPlane; class CollisionPolygon; class PortalNode; class PolylightNode; +class EggRenderState; /////////////////////////////////////////////////////////////////// // Class : EggLoader @@ -99,7 +100,15 @@ private: typedef pmap BakeInUVs; // This is used by make_primitive(). - typedef pmap Primitives; + class PrimitiveUnifier { + public: + INLINE PrimitiveUnifier(const qpGeomPrimitive *prim); + INLINE bool operator < (const PrimitiveUnifier &other) const; + + TypeHandle _type; + qpGeomPrimitive::ShadeModel _shade_model; + }; + typedef pmap Primitives; void make_nurbs_curve(EggNurbsCurve *egg_curve, PandaNode *parent, const LMatrix4d &mat); @@ -130,7 +139,8 @@ private: bool &any_hidden); PT(qpGeomVertexData) make_vertex_data(EggVertexPool *vertex_pool, const LMatrix4d &transform); - void make_primitive(EggPrimitive *egg_prim, Primitives &primitives); + void make_primitive(const EggRenderState *render_state, + EggPrimitive *egg_prim, Primitives &primitives); void set_portal_polygon(EggGroup *egg_group, PortalNode *pnode); PT(EggPolygon) find_first_polygon(EggGroup *egg_group); diff --git a/panda/src/egg2pg/eggRenderState.I b/panda/src/egg2pg/eggRenderState.I index 6f60343f04..46fb150cc2 100644 --- a/panda/src/egg2pg/eggRenderState.I +++ b/panda/src/egg2pg/eggRenderState.I @@ -26,7 +26,7 @@ EggRenderState:: EggRenderState(EggLoader &loader) : _state(RenderState::make_empty()), _hidden(false), - _indexed(false), + _flat_shaded(false), _loader(loader) { } @@ -57,8 +57,8 @@ compare_to(const EggRenderState &other) const { if (_hidden != other._hidden) { return (int)_hidden - (int)other._hidden; } - if (_indexed != other._indexed) { - return (int)_indexed - (int)other._indexed; + if (_flat_shaded != other._flat_shaded) { + return (int)_flat_shaded - (int)other._flat_shaded; } return 0; } diff --git a/panda/src/egg2pg/eggRenderState.cxx b/panda/src/egg2pg/eggRenderState.cxx index 9af080b6ac..641bfae82b 100644 --- a/panda/src/egg2pg/eggRenderState.cxx +++ b/panda/src/egg2pg/eggRenderState.cxx @@ -340,24 +340,11 @@ fill_state(EggPrimitive *egg_prim) { break; } - _indexed = egg_prim->determine_indexed(); - - bool flat_shaded; - - if (!_indexed) { - // If the primitive is not indexed, we're allowed to call - // unify_attributes(), which will modify the vertex pool. - egg_prim->unify_attributes(); - flat_shaded = egg_prim->is_flat_shaded(); - - } else { - // If the primitive *is* indexed, we're not allowed to modify the - // vertex pool, and so we won't have flat shading anyway. - flat_shaded = false; - } + _flat_shaded = + (egg_prim->get_connected_shading() == EggPrimitive::S_per_face); if (use_qpgeom) { - if (flat_shaded) { + if (_flat_shaded) { add_attrib(ShadeModelAttrib::make(ShadeModelAttrib::M_flat)); } else { add_attrib(ShadeModelAttrib::make(ShadeModelAttrib::M_smooth)); diff --git a/panda/src/egg2pg/eggRenderState.h b/panda/src/egg2pg/eggRenderState.h index 7ea1f1d625..256f7adad4 100644 --- a/panda/src/egg2pg/eggRenderState.h +++ b/panda/src/egg2pg/eggRenderState.h @@ -63,7 +63,7 @@ private: public: CPT(RenderState) _state; bool _hidden; - bool _indexed; + bool _flat_shaded; typedef EggLoader::BakeInUVs BakeInUVs; typedef EggLoader::TextureDef TextureDef; diff --git a/panda/src/glstuff/glGeomMunger_src.I b/panda/src/glstuff/glGeomMunger_src.I index 6c53a1621f..24b1bb6bed 100644 --- a/panda/src/glstuff/glGeomMunger_src.I +++ b/panda/src/glstuff/glGeomMunger_src.I @@ -23,5 +23,7 @@ // Description: //////////////////////////////////////////////////////////////////// INLINE CLP(GeomMunger):: -CLP(GeomMunger)() { +CLP(GeomMunger)(GraphicsStateGuardian *gsg, const RenderState *state) : + qpGeomMunger(gsg, state) +{ } diff --git a/panda/src/glstuff/glGeomMunger_src.h b/panda/src/glstuff/glGeomMunger_src.h index f4a7908e30..51506949e3 100644 --- a/panda/src/glstuff/glGeomMunger_src.h +++ b/panda/src/glstuff/glGeomMunger_src.h @@ -18,6 +18,7 @@ #include "pandabase.h" #include "qpgeomMunger.h" +#include "graphicsStateGuardian.h" //////////////////////////////////////////////////////////////////// // Class : GLGeomMunger @@ -27,7 +28,7 @@ //////////////////////////////////////////////////////////////////// class EXPCL_GL CLP(GeomMunger) : public qpGeomMunger { public: - INLINE CLP(GeomMunger)(); + INLINE CLP(GeomMunger)(GraphicsStateGuardian *gsg, const RenderState *state); protected: virtual CPT(qpGeomVertexFormat) munge_format_impl(const qpGeomVertexFormat *orig); diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 998f85e8f3..ef489779ec 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -2058,14 +2058,14 @@ begin_draw_primitives(const qpGeomVertexData *vertex_data) { // Description: Draws a series of disconnected triangles. //////////////////////////////////////////////////////////////////// void CLP(GraphicsStateGuardian):: -draw_triangles(qpGeomTriangles *primitive) { +draw_triangles(const qpGeomTriangles *primitive) { setup_antialias_polygon(); _glDrawRangeElements(GL_TRIANGLES, primitive->get_min_vertex(), primitive->get_max_vertex(), primitive->get_num_vertices(), - GL_UNSIGNED_SHORT, primitive->get_vertices()); + GL_UNSIGNED_SHORT, primitive->get_flat_last_vertices()); report_my_gl_errors(); } @@ -2283,8 +2283,8 @@ static int binary_log_cap(const int x) { // appropriate to this GSG for the indicated state. //////////////////////////////////////////////////////////////////// CPT(qpGeomMunger) CLP(GraphicsStateGuardian):: -get_geom_munger(const RenderState *) { - PT(CLP(GeomMunger)) munger = new CLP(GeomMunger); +get_geom_munger(const RenderState *state) { + PT(CLP(GeomMunger)) munger = new CLP(GeomMunger)(this, state); return qpGeomMunger::register_munger(munger); } diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index 86ecdb1c92..ee7144ab70 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -90,7 +90,7 @@ public: virtual void draw_sphere(GeomSphere *geom, GeomContext *gc); virtual bool begin_draw_primitives(const qpGeomVertexData *vertex_data); - virtual void draw_triangles(qpGeomTriangles *primitive); + virtual void draw_triangles(const qpGeomTriangles *primitive); virtual void end_draw_primitives(); INLINE bool draw_display_list(GeomContext *gc); diff --git a/panda/src/gobj/geom.cxx b/panda/src/gobj/geom.cxx index 534102072f..5f9cdce9bc 100644 --- a/panda/src/gobj/geom.cxx +++ b/panda/src/gobj/geom.cxx @@ -1221,11 +1221,6 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) { CPT(InternalName) tc = DCAST(InternalName, p_list[pi++]); TexCoordDef *def = (*ci); _texcoords_by_name[tc] = *def; - /* - cerr << "InternalName from Geom " << (void *)this - << " complete pointers " << tc << " " << *tc - << " = " << def->_texcoords << " and " << def->_tindex << "\n"; - */ delete def; if (tc == InternalName::get_texcoord()) { _bind[G_TEXCOORD] = G_PER_VERTEX; diff --git a/panda/src/gobj/qpgeom.I b/panda/src/gobj/qpgeom.I index d322ed3aef..ed716ddc24 100644 --- a/panda/src/gobj/qpgeom.I +++ b/panda/src/gobj/qpgeom.I @@ -68,6 +68,7 @@ get_primitive(int i) const { //////////////////////////////////////////////////////////////////// INLINE qpGeomPrimitive *qpGeom:: modify_primitive(int i) { + clear_cache(); CDWriter cdata(_cycler); nassertr(i >= 0 && i < (int)cdata->_primitives.size(), NULL); return cdata->_primitives[i]; @@ -80,10 +81,11 @@ modify_primitive(int i) { // the Geom with the new object. //////////////////////////////////////////////////////////////////// INLINE void qpGeom:: -set_primitive(int i, qpGeomPrimitive *primitive) { +set_primitive(int i, const qpGeomPrimitive *primitive) { + clear_cache(); CDWriter cdata(_cycler); nassertv(i >= 0 && i < (int)cdata->_primitives.size()); - cdata->_primitives[i] = primitive; + cdata->_primitives[i] = (qpGeomPrimitive *)primitive; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/gobj/qpgeom.cxx b/panda/src/gobj/qpgeom.cxx index f617efa918..7d109c1d80 100644 --- a/panda/src/gobj/qpgeom.cxx +++ b/panda/src/gobj/qpgeom.cxx @@ -17,6 +17,8 @@ //////////////////////////////////////////////////////////////////// #include "qpgeom.h" +#include "qpgeomVertexCacheManager.h" +#include "pStatTimer.h" #include "bamReader.h" #include "bamWriter.h" @@ -58,6 +60,7 @@ operator = (const qpGeom ©) { TypedWritableReferenceCount::operator = (copy); BoundedObject::operator = (copy); */ + clear_cache(); Geom::operator = (copy); _cycler = copy._cycler; mark_bound_stale(); @@ -70,6 +73,25 @@ operator = (const qpGeom ©) { //////////////////////////////////////////////////////////////////// qpGeom:: ~qpGeom() { + // When we destruct, we should ensure that all of our cached + // entries, across all pipeline stages, are properly removed from + // the cache manager. + qpGeomVertexCacheManager *cache_mgr = + qpGeomVertexCacheManager::get_global_ptr(); + + int num_stages = _cycler.get_num_stages(); + for (int i = 0; i < num_stages; i++) { + if (_cycler.is_stage_unique(i)) { + CData *cdata = _cycler.write_stage(i); + for (MungedCache::iterator ci = cdata->_munged_cache.begin(); + ci != cdata->_munged_cache.end(); + ++ci) { + cache_mgr->remove_geom(this, (*ci).first); + } + cdata->_munged_cache.clear(); + _cycler.release_write_stage(i, cdata); + } + } } //////////////////////////////////////////////////////////////////// @@ -94,7 +116,7 @@ modify_vertex_data() { // Perform copy-on-write: if the reference count on the vertex data // is greater than 1, assume some other Geom has the same pointer, // so make a copy of it first. - + clear_cache(); CDWriter cdata(_cycler); if (cdata->_data->get_ref_count() > 1) { cdata->_data = new qpGeomVertexData(*cdata->_data); @@ -110,9 +132,10 @@ modify_vertex_data() { // a completely new table. //////////////////////////////////////////////////////////////////// void qpGeom:: -set_vertex_data(PT(qpGeomVertexData) data) { +set_vertex_data(const qpGeomVertexData *data) { + clear_cache(); CDWriter cdata(_cycler); - cdata->_data = data; + cdata->_data = (qpGeomVertexData *)data; mark_bound_stale(); } @@ -125,9 +148,10 @@ set_vertex_data(PT(qpGeomVertexData) data) { // of the indicated type. //////////////////////////////////////////////////////////////////// void qpGeom:: -add_primitive(qpGeomPrimitive *primitive) { +add_primitive(const qpGeomPrimitive *primitive) { + clear_cache(); CDWriter cdata(_cycler); - cdata->_primitives.push_back(primitive); + cdata->_primitives.push_back((qpGeomPrimitive *)primitive); } //////////////////////////////////////////////////////////////////// @@ -137,6 +161,7 @@ add_primitive(qpGeomPrimitive *primitive) { //////////////////////////////////////////////////////////////////// void qpGeom:: remove_primitive(int i) { + clear_cache(); CDWriter cdata(_cycler); nassertv(i >= 0 && i < (int)cdata->_primitives.size()); cdata->_primitives.erase(cdata->_primitives.begin() + i); @@ -152,10 +177,97 @@ remove_primitive(int i) { //////////////////////////////////////////////////////////////////// void qpGeom:: clear_primitives() { + clear_cache(); CDWriter cdata(_cycler); cdata->_primitives.clear(); } +//////////////////////////////////////////////////////////////////// +// Function: qpGeom::get_num_bytes +// Access: Published +// Description: Returns the number of bytes consumed by the geom and +// its primitives (but not including its vertex table). +//////////////////////////////////////////////////////////////////// +int qpGeom:: +get_num_bytes() const { + CDReader cdata(_cycler); + + int num_bytes = sizeof(qpGeom); + Primitives::const_iterator pi; + for (pi = cdata->_primitives.begin(); + pi != cdata->_primitives.end(); + ++pi) { + num_bytes += (*pi)->get_num_bytes(); + } + + return num_bytes; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeom::munge_geom +// Access: Published +// Description: Applies the indicated munger to the geom and its +// data, and returns a (possibly different) geom and +// data, according to the munger's whim. +// +// The assumption is that for a particular geom and a +// particular munger, the result will always be the +// same; so this result may be cached. +//////////////////////////////////////////////////////////////////// +void qpGeom:: +munge_geom(const qpGeomMunger *munger, + CPT(qpGeom) &result, CPT(qpGeomVertexData) &data) const { + // Look up the format in our cache--maybe we've recently applied the + // indicated munger. + { + // Use read() and release_read() instead of CDReader, because the + // call to record_geom() might recursively call back into this + // object, and require a write. + const CData *cdata = _cycler.read(); + MungedCache::const_iterator ci = cdata->_munged_cache.find(munger); + if (ci != cdata->_munged_cache.end()) { + _cycler.release_read(cdata); + // Record a cache hit, so this element will stay in the cache a + // while longer. + qpGeomVertexCacheManager *cache_mgr = + qpGeomVertexCacheManager::get_global_ptr(); + cache_mgr->record_geom(this, munger, + (*ci).second._geom->get_num_bytes() + + (*ci).second._data->get_num_bytes()); + + result = (*ci).second._geom; + data = (*ci).second._data; + return; + } + _cycler.release_read(cdata); + } + + // Ok, invoke the munger. + PStatTimer timer(qpGeomMunger::_munge_pcollector); + + result = this; + data = munger->munge_data(get_vertex_data()); + ((qpGeomMunger *)munger)->munge_geom_impl(result, data); + + if (result.p() != this) { + // Record the new result in the cache. + { + CDWriter cdata(((qpGeom *)this)->_cycler); + MungeResult &mr = cdata->_munged_cache[munger]; + mr._geom = result; + mr._data = data; + } + + // And tell the cache manager about the new entry. (It might + // immediately request a delete from the cache of the thing we just + // added.) + qpGeomVertexCacheManager *cache_mgr = + qpGeomVertexCacheManager::get_global_ptr(); + cache_mgr->record_geom(this, munger, + result->get_num_bytes() + data->get_num_bytes()); + } +} + //////////////////////////////////////////////////////////////////// // Function: qpGeom::output // Access: Published @@ -200,6 +312,28 @@ write(ostream &out, int indent_level) const { } } +//////////////////////////////////////////////////////////////////// +// Function: qpGeom::clear_cache +// Access: Published +// Description: Removes all of the previously-cached results of +// munge_geom(). +//////////////////////////////////////////////////////////////////// +void qpGeom:: +clear_cache() { + // Probably we shouldn't do anything at all here unless we are + // running in pipeline stage 0. + qpGeomVertexCacheManager *cache_mgr = + qpGeomVertexCacheManager::get_global_ptr(); + + CData *cdata = CDWriter(_cycler); + for (MungedCache::iterator ci = cdata->_munged_cache.begin(); + ci != cdata->_munged_cache.end(); + ++ci) { + cache_mgr->remove_geom(this, (*ci).first); + } + cdata->_munged_cache.clear(); +} + //////////////////////////////////////////////////////////////////// // Function: qpGeom::draw // Access: Public @@ -291,6 +425,27 @@ recompute_bound() { return bound; } +//////////////////////////////////////////////////////////////////// +// Function: qpGeom::remove_cache_entry +// Access: Private +// Description: Removes a particular entry from the local cache; it +// has already been removed from the cache manager. +// This is only called from GeomVertexCacheManager. +//////////////////////////////////////////////////////////////////// +void qpGeom:: +remove_cache_entry(const qpGeomMunger *modifier) const { + // We have to operate on stage 0 of the pipeline, since that's where + // the cache really counts. Because of the multistage pipeline, we + // might not actually have a cache entry there (it might have been + // added to stage 1 instead). No big deal if we don't. + CData *cdata = ((qpGeom *)this)->_cycler.write_stage(0); + MungedCache::iterator ci = cdata->_munged_cache.find(modifier); + if (ci != cdata->_munged_cache.end()) { + cdata->_munged_cache.erase(ci); + } + ((qpGeom *)this)->_cycler.release_write_stage(0, cdata); +} + //////////////////////////////////////////////////////////////////// // Function: qpGeom::register_with_read_factory // Access: Public, Static diff --git a/panda/src/gobj/qpgeom.h b/panda/src/gobj/qpgeom.h index e9d28c4f5c..f2baeff15d 100644 --- a/panda/src/gobj/qpgeom.h +++ b/panda/src/gobj/qpgeom.h @@ -28,6 +28,7 @@ #include "pipelineCycler.h" #include "qpgeomVertexData.h" #include "qpgeomPrimitive.h" +#include "qpgeomMunger.h" #include "pointerTo.h" #include "geom.h" @@ -62,19 +63,26 @@ PUBLISHED: INLINE CPT(qpGeomVertexData) get_vertex_data() const; PT(qpGeomVertexData) modify_vertex_data(); - void set_vertex_data(PT(qpGeomVertexData) data); + void set_vertex_data(const qpGeomVertexData *data); INLINE int get_num_primitives() const; INLINE const qpGeomPrimitive *get_primitive(int i) const; INLINE qpGeomPrimitive *modify_primitive(int i); - INLINE void set_primitive(int i, qpGeomPrimitive *primitive); - void add_primitive(qpGeomPrimitive *primitive); + INLINE void set_primitive(int i, const qpGeomPrimitive *primitive); + void add_primitive(const qpGeomPrimitive *primitive); void remove_primitive(int i); void clear_primitives(); + int get_num_bytes() const; + + void munge_geom(const qpGeomMunger *munger, + CPT(qpGeom) &result, CPT(qpGeomVertexData) &data) const; + void output(ostream &out) const; void write(ostream &out, int indent_level = 0) const; + void clear_cache(); + public: void draw(GraphicsStateGuardianBase *gsg, const qpGeomVertexData *vertex_data) const; @@ -82,9 +90,24 @@ public: protected: virtual BoundingVolume *recompute_bound(); +private: + void remove_cache_entry(const qpGeomMunger *modifier) const; + private: typedef pvector Primitives; + // We have to use reference-counting pointers here instead of having + // explicit cleanup in the GeomVertexFormat destructor, because the + // cache needs to be stored in the CycleData, which makes accurate + // cleanup more difficult. We use the GeomVertexCacheManager class + // to avoid cache bloat. + class MungeResult { + public: + CPT(qpGeom) _geom; + CPT(qpGeomVertexData) _data; + }; + typedef pmap MungedCache; + // This is the data that must be cycled between pipeline stages. class EXPCL_PANDA CData : public CycleData { public: @@ -97,6 +120,7 @@ private: PT(qpGeomVertexData) _data; Primitives _primitives; + MungedCache _munged_cache; }; PipelineCycler _cycler; @@ -131,6 +155,8 @@ public: private: static TypeHandle _type_handle; + + friend class qpGeomVertexCacheManager; }; INLINE ostream &operator << (ostream &out, const qpGeom &obj); diff --git a/panda/src/gobj/qpgeomMunger.I b/panda/src/gobj/qpgeomMunger.I index 75fbcf6510..c8931c8c84 100644 --- a/panda/src/gobj/qpgeomMunger.I +++ b/panda/src/gobj/qpgeomMunger.I @@ -71,20 +71,6 @@ compare_to(const qpGeomMunger &other) const { return compare_to_impl(&other); } -//////////////////////////////////////////////////////////////////// -// Function: qpGeomMunger::munge_data -// Access: Public -// Description: Given a source GeomVertexData, converts it if -// necessary to the appropriate data for rendering. -//////////////////////////////////////////////////////////////////// -INLINE CPT(qpGeomVertexData) qpGeomMunger:: -munge_data(const qpGeomVertexData *data) const { - // We cast away the const pointer, because do_munge_data() needs to - // update caches and stuff, but we trust it not to change any - // user-definable parameters. - return ((qpGeomMunger *)this)->do_munge_data(data); -} - //////////////////////////////////////////////////////////////////// // Function: qpGeomMunger::munge_format // Access: Public @@ -99,6 +85,20 @@ munge_format(const qpGeomVertexFormat *format) const { return ((qpGeomMunger *)this)->do_munge_format(format); } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomMunger::munge_data +// Access: Public +// Description: Given a source GeomVertexData, converts it if +// necessary to the appropriate data for rendering. +//////////////////////////////////////////////////////////////////// +INLINE CPT(qpGeomVertexData) qpGeomMunger:: +munge_data(const qpGeomVertexData *data) const { + // We cast away the const pointer, because do_munge_data() needs to + // update caches and stuff, but we trust it not to change any + // user-definable parameters. + return ((qpGeomMunger *)this)->do_munge_data(data); +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomMunger::get_registry // Access: Private diff --git a/panda/src/gobj/qpgeomMunger.cxx b/panda/src/gobj/qpgeomMunger.cxx index c1603c36b1..03ce282344 100644 --- a/panda/src/gobj/qpgeomMunger.cxx +++ b/panda/src/gobj/qpgeomMunger.cxx @@ -32,7 +32,7 @@ PStatCollector qpGeomMunger::_munge_pcollector("Cull:Munge"); // Description: //////////////////////////////////////////////////////////////////// qpGeomMunger:: -qpGeomMunger() : +qpGeomMunger(const GraphicsStateGuardianBase *, const RenderState *) : _is_registered(false) { } @@ -92,28 +92,6 @@ remove_data(const qpGeomVertexData *data) { nassertv(_is_registered); } -//////////////////////////////////////////////////////////////////// -// Function: qpGeomMunger::do_munge_data -// Access: Protected -// Description: The protected implementation of munge_data(). This -// exists just to cast away the const pointer. -//////////////////////////////////////////////////////////////////// -CPT(qpGeomVertexData) qpGeomMunger:: -do_munge_data(const qpGeomVertexData *data) { - nassertr(_is_registered, NULL); - PStatTimer timer(_munge_pcollector); - - CPT(qpGeomVertexFormat) orig_format = data->get_format(); - CPT(qpGeomVertexFormat) new_format = munge_format(orig_format); - - if (new_format == orig_format) { - // Trivial case. - return data; - } - - return data->convert_to(new_format); -} - //////////////////////////////////////////////////////////////////// // Function: qpGeomMunger::do_munge_format // Access: Protected @@ -161,6 +139,28 @@ do_munge_format(const qpGeomVertexFormat *format) { return derived_format; } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomMunger::do_munge_data +// Access: Protected +// Description: The protected implementation of munge_data(). This +// exists just to cast away the const pointer. +//////////////////////////////////////////////////////////////////// +CPT(qpGeomVertexData) qpGeomMunger:: +do_munge_data(const qpGeomVertexData *data) { + nassertr(_is_registered, NULL); + PStatTimer timer(_munge_pcollector); + + CPT(qpGeomVertexFormat) orig_format = data->get_format(); + CPT(qpGeomVertexFormat) new_format = munge_format(orig_format); + + if (new_format == orig_format) { + // Trivial case. + return data; + } + + return data->convert_to(new_format); +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomMunger::munge_format_impl // Access: Protected, Virtual @@ -172,6 +172,16 @@ munge_format_impl(const qpGeomVertexFormat *orig) { return orig; } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomMunger::munge_geom_impl +// Access: Protected, Virtual +// Description: Converts a Geom and/or its data as necessary. +//////////////////////////////////////////////////////////////////// +void qpGeomMunger:: +munge_geom_impl(CPT(qpGeom) &, CPT(qpGeomVertexData) &) { + // The default implementation does nothing. +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomMunger::compare_to_impl // Access: Protected, Virtual diff --git a/panda/src/gobj/qpgeomMunger.h b/panda/src/gobj/qpgeomMunger.h index b28571ff27..ceb6403136 100644 --- a/panda/src/gobj/qpgeomMunger.h +++ b/panda/src/gobj/qpgeomMunger.h @@ -22,11 +22,17 @@ #include "pandabase.h" #include "typedReferenceCount.h" #include "qpgeomVertexFormat.h" +#include "qpgeomVertexData.h" #include "indirectCompareTo.h" #include "pStatCollector.h" +#include "pointerTo.h" #include "pmap.h" #include "pset.h" +class GraphicsStateGuardianBase; +class RenderState; +class qpGeom; + //////////////////////////////////////////////////////////////////// // Class : qpGeomMunger // Description : Objects of this class are used to convert vertex data @@ -52,7 +58,7 @@ //////////////////////////////////////////////////////////////////// class EXPCL_PANDA qpGeomMunger : public TypedReferenceCount { public: - qpGeomMunger(); + qpGeomMunger(const GraphicsStateGuardianBase *gsg, const RenderState *state); virtual ~qpGeomMunger(); INLINE bool is_registered() const; @@ -64,6 +70,8 @@ public: INLINE CPT(qpGeomVertexData) munge_data(const qpGeomVertexData *data) const; void remove_data(const qpGeomVertexData *data); + // Also see Geom::munge_geom() for the primary interface. + public: INLINE int compare_to(const qpGeomMunger &other) const; @@ -72,6 +80,7 @@ protected: CPT(qpGeomVertexData) do_munge_data(const qpGeomVertexData *data); virtual CPT(qpGeomVertexFormat) munge_format_impl(const qpGeomVertexFormat *orig); + virtual void munge_geom_impl(CPT(qpGeom) &geom, CPT(qpGeomVertexData) &data); virtual int compare_to_impl(const qpGeomMunger *other) const; private: @@ -116,6 +125,8 @@ public: private: static TypeHandle _type_handle; + + friend class qpGeom; }; #include "qpgeomMunger.I" diff --git a/panda/src/gobj/qpgeomPrimitive.I b/panda/src/gobj/qpgeomPrimitive.I index e5a984119b..d1ef9076dd 100644 --- a/panda/src/gobj/qpgeomPrimitive.I +++ b/panda/src/gobj/qpgeomPrimitive.I @@ -17,6 +17,37 @@ //////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////// +// Function: qpGeomPrimitive::get_shade_model +// Access: Published +// Description: Returns the ShadeModel hint for this primitive. +// This is intended as a hint to the renderer to tell it +// how the per-vertex colors and normals are applied. +//////////////////////////////////////////////////////////////////// +INLINE qpGeomPrimitive::ShadeModel qpGeomPrimitive:: +get_shade_model() const { + CDReader cdata(_cycler); + return cdata->_shade_model; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomPrimitive::set_shade_model +// Access: Published +// Description: Changes the ShadeModel hint for this primitive. +// This is different from the ShadeModelAttrib that +// might also be applied from the scene graph. This +// does not affect the shade model that is in effect +// when rendering, but rather serves as a hint to the +// renderer to tell it how the per-vertex colors and +// normals on this primitive are applied. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomPrimitive:: +set_shade_model(qpGeomPrimitive::ShadeModel shade_model) { + CDWriter cdata(_cycler); + cdata->_shade_model = shade_model; + cdata->_rotated_vertices.clear(); +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomPrimitive::get_num_vertices // Access: Published @@ -55,6 +86,64 @@ get_vertices() const { return cdata->_vertices; } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomPrimitive::get_flat_first_vertices +// Access: Published +// Description: Returns a const pointer to the vertex index array, +// suitable for traversing for a renderer that expects +// the key vertex of each triangle in a flat-shaded +// model to be the triangle's first vertex (as DirectX +// does). +// +// The result of this is dependent on the value +// specified to set_shade_model(). If the shade model +// indicates flat_last_vertex, this will return the +// vertices rotated to bring them into first_vertex +// compliance; otherwise, it will be the unmodified +// result from get_vertices(). +//////////////////////////////////////////////////////////////////// +INLINE CPTA_ushort qpGeomPrimitive:: +get_flat_first_vertices() const { + CDReader cdata(_cycler); + if (cdata->_shade_model == SM_flat_last_vertex) { + if (cdata->_rotated_vertices == (const ushort *)NULL) { + ((qpGeomPrimitive *)this)->do_rotate(); + } + return cdata->_rotated_vertices; + } else { + return cdata->_vertices; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomPrimitive::get_flat_last_vertices +// Access: Published +// Description: Returns a const pointer to the vertex index array, +// suitable for traversing for a renderer that expects +// the key vertex of each triangle in a flat-shaded +// model to be the triangle's last vertex (as OpenGL +// does). +// +// The result of this is dependent on the value +// specified to set_shade_model(). If the shade model +// indicates flat_first_vertex, this will return the +// vertices rotated to bring them into last_vertex +// compliance; otherwise, it will be the unmodified +// result from get_vertices(). +//////////////////////////////////////////////////////////////////// +INLINE CPTA_ushort qpGeomPrimitive:: +get_flat_last_vertices() const { + CDReader cdata(_cycler); + if (cdata->_shade_model == SM_flat_first_vertex) { + if (cdata->_rotated_vertices == (const ushort *)NULL) { + ((qpGeomPrimitive *)this)->do_rotate(); + } + return cdata->_rotated_vertices; + } else { + return cdata->_vertices; + } +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomPrimitive::get_vertices // Access: Published @@ -103,6 +192,17 @@ get_max_vertex() const { return cdata->_max_vertex; } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomPrimitive::get_primitive_end +// Access: Published +// Description: Returns the element within the _vertices list at which +// the ith primitive ends. +//////////////////////////////////////////////////////////////////// +INLINE int qpGeomPrimitive:: +get_primitive_end(int i) const { + return get_primitive_start(i + 1); +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomPrimitive::CData::Constructor // Access: Public @@ -110,6 +210,7 @@ get_max_vertex() const { //////////////////////////////////////////////////////////////////// INLINE qpGeomPrimitive::CData:: CData() : + _shade_model(SM_smooth), _got_minmax(true), _min_vertex(0), _max_vertex(0) @@ -123,7 +224,9 @@ CData() : //////////////////////////////////////////////////////////////////// INLINE qpGeomPrimitive::CData:: CData(const qpGeomPrimitive::CData ©) : + _shade_model(copy._shade_model), _vertices(copy._vertices), + _rotated_vertices(copy._rotated_vertices), _ends(copy._ends), _got_minmax(copy._got_minmax), _min_vertex(copy._min_vertex), diff --git a/panda/src/gobj/qpgeomPrimitive.cxx b/panda/src/gobj/qpgeomPrimitive.cxx index f6ee135872..94c248f873 100644 --- a/panda/src/gobj/qpgeomPrimitive.cxx +++ b/panda/src/gobj/qpgeomPrimitive.cxx @@ -28,6 +28,8 @@ TypeHandle qpGeomPrimitive::_type_handle; +PStatCollector qpGeomPrimitive::_rotate_pcollector("Cull:Rotate"); + //////////////////////////////////////////////////////////////////// // Function: qpGeomPrimitive::Constructor // Access: Published @@ -67,7 +69,7 @@ qpGeomPrimitive:: if (_cycler.is_stage_unique(i)) { CData *cdata = _cycler.write_stage(i); if (cdata->_decomposed != (qpGeomPrimitive *)NULL) { - cache_mgr->remove_decompose(this); + cache_mgr->remove_primitive(this); cdata->_decomposed = NULL; } _cycler.release_write_stage(i, cdata); @@ -93,6 +95,7 @@ add_vertex(int vertex) { clear_cache(); CDWriter cdata(_cycler); cdata->_vertices.push_back(short_vertex); + cdata->_rotated_vertices.clear(); if (cdata->_got_minmax) { cdata->_min_vertex = min(cdata->_min_vertex, short_vertex); @@ -118,6 +121,7 @@ add_consecutive_vertices(int start, int num_vertices) { for (unsigned short v = short_start; v <= short_end; ++v) { cdata->_vertices.push_back(v); } + cdata->_rotated_vertices.clear(); if (cdata->_got_minmax) { cdata->_min_vertex = min(cdata->_min_vertex, short_start); @@ -171,6 +175,7 @@ clear_vertices() { clear_cache(); CDWriter cdata(_cycler); cdata->_vertices.clear(); + cdata->_rotated_vertices.clear(); cdata->_ends.clear(); } @@ -186,6 +191,7 @@ PTA_ushort qpGeomPrimitive:: modify_vertices() { clear_cache(); CDWriter cdata(_cycler); + cdata->_rotated_vertices.clear(); return cdata->_vertices; } @@ -201,6 +207,7 @@ set_vertices(PTA_ushort vertices) { clear_cache(); CDWriter cdata(_cycler); cdata->_vertices = vertices; + cdata->_rotated_vertices.clear(); } //////////////////////////////////////////////////////////////////// @@ -244,14 +251,14 @@ set_ends(PTA_int ends) { //////////////////////////////////////////////////////////////////// // Function: qpGeomPrimitive::get_num_bytes // Access: Published -// Description: Records the number of bytes consumed by the primitive +// Description: Returns the number of bytes consumed by the primitive // and its index table(s). //////////////////////////////////////////////////////////////////// int qpGeomPrimitive:: get_num_bytes() const { CDReader cdata(_cycler); return cdata->_vertices.size() * sizeof(short) + - cdata->_ends.size() * sizeof(int); + cdata->_ends.size() * sizeof(int) + sizeof(qpGeomPrimitive); } //////////////////////////////////////////////////////////////////// @@ -309,7 +316,7 @@ get_num_primitives() const { // Function: qpGeomPrimitive::get_primitive_start // Access: Published // Description: Returns the element within the _vertices list at which -// the ith primitive ends. +// the ith primitive starts. // // If i is one more than the highest valid primitive // vertex, the return value will be one more than the @@ -382,12 +389,12 @@ get_primitive_num_vertices(int i) const { // primitive without having to write handlers for each // possible kind of primitive type. //////////////////////////////////////////////////////////////////// -PT(qpGeomPrimitive) qpGeomPrimitive:: -decompose() { - PT(qpGeomPrimitive) result; +CPT(qpGeomPrimitive) qpGeomPrimitive:: +decompose() const { + CPT(qpGeomPrimitive) result; { // Use read() and release_read() instead of CDReader, because the - // call to record_decompose() might recursively call back into + // call to record_primitive() might recursively call back into // this object, and require a write. const CData *cdata = _cycler.read(); if (cdata->_decomposed != (qpGeomPrimitive *)NULL) { @@ -397,7 +404,7 @@ decompose() { // while longer. qpGeomVertexCacheManager *cache_mgr = qpGeomVertexCacheManager::get_global_ptr(); - cache_mgr->record_decompose(this, result->get_num_bytes()); + cache_mgr->record_primitive(this, result->get_num_bytes()); return result; } @@ -405,8 +412,8 @@ decompose() { } result = decompose_impl(); - if (result.p() == this) { - // decomposing this primitive has no effect. + if (result.p() == this || result.p() == NULL) { + // decomposing this primitive has no effect or cannot be done. return this; } @@ -416,13 +423,13 @@ decompose() { } // Record the result for the future. - CDWriter cdata(_cycler); + CDWriter cdata(((qpGeomPrimitive *)this)->_cycler); cdata->_decomposed = result; // And add *this* object to the cache manager. qpGeomVertexCacheManager *cache_mgr = qpGeomVertexCacheManager::get_global_ptr(); - cache_mgr->record_decompose(this, result->get_num_bytes()); + cache_mgr->record_primitive(this, result->get_num_bytes()); return result; } @@ -464,7 +471,7 @@ write(ostream &out, int indent_level) const { // Function: qpGeomPrimitive::clear_cache // Access: Published // Description: Removes all of the previously-cached results of -// convert_to(). +// decompose(). //////////////////////////////////////////////////////////////////// void qpGeomPrimitive:: clear_cache() { @@ -475,7 +482,7 @@ clear_cache() { CData *cdata = CDWriter(_cycler); if (cdata->_decomposed != (qpGeomPrimitive *)NULL) { - cache_mgr->remove_decompose(this); + cache_mgr->remove_primitive(this); cdata->_decomposed = NULL; } } @@ -538,7 +545,7 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point, //////////////////////////////////////////////////////////////////// // Function: qpGeomPrimitive::decompose_impl -// Access: Protected +// Access: Protected, Virtual // Description: Decomposes a complex primitive type into a simpler // primitive type, for instance triangle strips to // triangles, and returns a pointer to the new primitive @@ -550,11 +557,51 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point, // primitive without having to write handlers for each // possible kind of primitive type. //////////////////////////////////////////////////////////////////// -PT(qpGeomPrimitive) qpGeomPrimitive:: -decompose_impl() { +CPT(qpGeomPrimitive) qpGeomPrimitive:: +decompose_impl() const { return this; } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomPrimitive::rotate_impl +// Access: Protected, Virtual +// Description: The virtual implementation of do_rotate(). +//////////////////////////////////////////////////////////////////// +CPTA_ushort qpGeomPrimitive:: +rotate_impl() const { + // The default implementation doesn't even try to do anything. + nassertr(false, get_vertices()); + return get_vertices(); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomPrimitive::do_rotate +// Access: Private +// Description: Fills _rotated_vertices by rotating the individual +// primitives to bring each flat-colored vertex to the +// opposite position. If the current shade_model +// indicates flat_vertex_last, this should bring the +// last vertex to the first position; if it indicates +// flat_vertex_first, this should bring the first vertex +// to the last position. This is used internally to +// implement get_flat_first_vertices() and +// get_flat_last_vertices(). +//////////////////////////////////////////////////////////////////// +void qpGeomPrimitive:: +do_rotate() { + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << "Rotating " << get_type() << ": " << (void *)this << "\n"; + } + + PStatTimer timer(_rotate_pcollector); + CDReader cdata(_cycler); + CPTA_ushort rotated_vertices = rotate_impl(); + + CDWriter cdataw(_cycler, cdata); + cdataw->_rotated_vertices = rotated_vertices; +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomPrimitive::remove_cache_entry // Access: Private @@ -570,6 +617,7 @@ remove_cache_entry() const { // added to stage 1 instead). No big deal if we don't. CData *cdata = ((qpGeomPrimitive *)this)->_cycler.write_stage(0); cdata->_decomposed = NULL; + ((qpGeomPrimitive *)this)->_cycler.release_write_stage(0, cdata); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/gobj/qpgeomPrimitive.h b/panda/src/gobj/qpgeomPrimitive.h index 35c4b0f669..5afe5a6f4e 100644 --- a/panda/src/gobj/qpgeomPrimitive.h +++ b/panda/src/gobj/qpgeomPrimitive.h @@ -25,6 +25,7 @@ #include "pointerTo.h" #include "pta_ushort.h" #include "pta_int.h" +#include "pStatCollector.h" #include "cycleData.h" #include "cycleDataReader.h" #include "cycleDataWriter.h" @@ -64,6 +65,18 @@ PUBLISHED: qpGeomPrimitive(const qpGeomPrimitive ©); virtual ~qpGeomPrimitive(); + virtual PT(qpGeomPrimitive) make_copy() const=0; + + enum ShadeModel { + SM_smooth, + SM_uniform, + SM_flat_first_vertex, + SM_flat_last_vertex, + }; + + INLINE ShadeModel get_shade_model() const; + INLINE void set_shade_model(ShadeModel shade_model); + INLINE int get_num_vertices() const; INLINE int get_vertex(int i) const; void add_vertex(int vertex); @@ -72,6 +85,8 @@ PUBLISHED: void clear_vertices(); INLINE CPTA_ushort get_vertices() const; + INLINE CPTA_ushort get_flat_first_vertices() const; + INLINE CPTA_ushort get_flat_last_vertices() const; PTA_ushort modify_vertices(); void set_vertices(PTA_ushort vertices); @@ -88,9 +103,10 @@ PUBLISHED: virtual int get_min_num_vertices_per_primitive() const; int get_num_primitives() const; int get_primitive_start(int i) const; + INLINE int get_primitive_end(int i) const; int get_primitive_num_vertices(int i) const; - PT(qpGeomPrimitive) decompose(); + CPT(qpGeomPrimitive) decompose() const; virtual void output(ostream &out) const; virtual void write(ostream &out, int indent_level) const; @@ -98,18 +114,23 @@ PUBLISHED: void clear_cache(); public: - virtual void draw(GraphicsStateGuardianBase *gsg)=0; + virtual void draw(GraphicsStateGuardianBase *gsg) const=0; virtual void calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point, bool &found_any, const qpGeomVertexData *vertex_data) const; protected: - virtual PT(qpGeomPrimitive) decompose_impl(); + virtual CPT(qpGeomPrimitive) decompose_impl() const; + virtual CPTA_ushort rotate_impl() const; -private: +private: + void do_rotate(); void remove_cache_entry() const; +protected: + static PStatCollector _rotate_pcollector; + private: // This is the data that must be cycled between pipeline stages. class EXPCL_PANDA CData : public CycleData { @@ -121,14 +142,16 @@ private: virtual int complete_pointers(TypedWritable **plist, BamReader *manager); virtual void fillin(DatagramIterator &scan, BamReader *manager); + ShadeModel _shade_model; PTA_ushort _vertices; + CPTA_ushort _rotated_vertices; PTA_int _ends; bool _got_minmax; unsigned short _min_vertex; unsigned short _max_vertex; - PT(qpGeomPrimitive) _decomposed; + CPT(qpGeomPrimitive) _decomposed; }; PipelineCycler _cycler; diff --git a/panda/src/gobj/qpgeomTriangles.cxx b/panda/src/gobj/qpgeomTriangles.cxx index 857b377a1e..0b02a9d6e2 100644 --- a/panda/src/gobj/qpgeomTriangles.cxx +++ b/panda/src/gobj/qpgeomTriangles.cxx @@ -17,6 +17,7 @@ //////////////////////////////////////////////////////////////////// #include "qpgeomTriangles.h" +#include "pStatTimer.h" #include "bamReader.h" #include "bamWriter.h" @@ -51,6 +52,16 @@ qpGeomTriangles:: ~qpGeomTriangles() { } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomTriangles::make_copy +// Access: Published, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +PT(qpGeomPrimitive) qpGeomTriangles:: +make_copy() const { + return new qpGeomTriangles(*this); +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomTriangles::get_num_vertices_per_primitive // Access: Published, Virtual @@ -74,10 +85,58 @@ get_num_vertices_per_primitive() const { // primitive. //////////////////////////////////////////////////////////////////// void qpGeomTriangles:: -draw(GraphicsStateGuardianBase *gsg) { +draw(GraphicsStateGuardianBase *gsg) const { gsg->draw_triangles(this); } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomTriangles::rotate_impl +// Access: Protected, Virtual +// Description: The virtual implementation of do_rotate(). +//////////////////////////////////////////////////////////////////// +CPTA_ushort qpGeomTriangles:: +rotate_impl() const { + // To rotate triangles, we just move one vertex from the front to + // the back, or vice-versa; but we have to know what direction we're + // going. + CPTA_ushort vertices = get_vertices(); + ShadeModel shade_model = get_shade_model(); + + PTA_ushort new_vertices; + new_vertices.reserve(vertices.size()); + + switch (shade_model) { + case SM_flat_first_vertex: + // Move the first vertex to the end. + { + for (int begin = 0; begin < (int)vertices.size(); begin += 3) { + new_vertices.push_back(vertices[begin + 1]); + new_vertices.push_back(vertices[begin + 2]); + new_vertices.push_back(vertices[begin]); + } + } + break; + + case SM_flat_last_vertex: + // Move the last vertex to the front. + { + for (int begin = 0; begin < (int)vertices.size(); begin += 3) { + new_vertices.push_back(vertices[begin + 2]); + new_vertices.push_back(vertices[begin]); + new_vertices.push_back(vertices[begin + 1]); + } + } + break; + + default: + // This shouldn't get called with any other shade model. + nassertr(false, vertices); + } + + nassertr(new_vertices.size() == vertices.size(), vertices); + return new_vertices; +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomTriangles::register_with_read_factory // Access: Public, Static diff --git a/panda/src/gobj/qpgeomTriangles.h b/panda/src/gobj/qpgeomTriangles.h index c1bcb391a3..959e742c57 100644 --- a/panda/src/gobj/qpgeomTriangles.h +++ b/panda/src/gobj/qpgeomTriangles.h @@ -34,10 +34,15 @@ PUBLISHED: qpGeomTriangles(const qpGeomTriangles ©); virtual ~qpGeomTriangles(); + virtual PT(qpGeomPrimitive) make_copy() const; + virtual int get_num_vertices_per_primitive() const; public: - virtual void draw(GraphicsStateGuardianBase *gsg); + virtual void draw(GraphicsStateGuardianBase *gsg) const; + +protected: + virtual CPTA_ushort rotate_impl() const; public: static void register_with_read_factory(); diff --git a/panda/src/gobj/qpgeomTrifans.cxx b/panda/src/gobj/qpgeomTrifans.cxx index 180de49677..586b830bce 100644 --- a/panda/src/gobj/qpgeomTrifans.cxx +++ b/panda/src/gobj/qpgeomTrifans.cxx @@ -52,6 +52,16 @@ qpGeomTrifans:: ~qpGeomTrifans() { } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomTrifans::make_copy +// Access: Published, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +PT(qpGeomPrimitive) qpGeomTrifans:: +make_copy() const { + return new qpGeomTrifans(*this); +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomTrifans::draw // Access: Public, Virtual @@ -59,13 +69,13 @@ qpGeomTrifans:: // primitive. //////////////////////////////////////////////////////////////////// void qpGeomTrifans:: -draw(GraphicsStateGuardianBase *gsg) { +draw(GraphicsStateGuardianBase *gsg) const { gsg->draw_trifans(this); } //////////////////////////////////////////////////////////////////// // Function: qpGeomTrifans::decompose_impl -// Access: Published, Virtual +// Access: Protected, Virtual // Description: Decomposes a complex primitive type into a simpler // primitive type, for instance triangle strips to // triangles, and returns a pointer to the new primitive @@ -77,9 +87,10 @@ draw(GraphicsStateGuardianBase *gsg) { // primitive without having to write handlers for each // possible kind of primitive type. //////////////////////////////////////////////////////////////////// -PT(qpGeomPrimitive) qpGeomTrifans:: -decompose_impl() { +CPT(qpGeomPrimitive) qpGeomTrifans:: +decompose_impl() const { PT(qpGeomTriangles) triangles = new qpGeomTriangles; + triangles->set_shade_model(get_shade_model()); CPTA_ushort vertices = get_vertices(); CPTA_int ends = get_ends(); @@ -109,6 +120,19 @@ decompose_impl() { return triangles.p(); } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomTrifans::rotate_impl +// Access: Protected, Virtual +// Description: The virtual implementation of do_rotate(). +//////////////////////////////////////////////////////////////////// +CPTA_ushort qpGeomTrifans:: +rotate_impl() const { + // Actually, we can't rotate fans without chaging the winding order. + // It's an error to define a flat shade model for a GeomTrifan. + nassertr(false, get_vertices()); + return get_vertices(); +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomTrifans::register_with_read_factory // Access: Public, Static diff --git a/panda/src/gobj/qpgeomTrifans.h b/panda/src/gobj/qpgeomTrifans.h index 187fc14496..d3199d126d 100644 --- a/panda/src/gobj/qpgeomTrifans.h +++ b/panda/src/gobj/qpgeomTrifans.h @@ -34,11 +34,14 @@ PUBLISHED: qpGeomTrifans(const qpGeomTrifans ©); virtual ~qpGeomTrifans(); + virtual PT(qpGeomPrimitive) make_copy() const; + public: - virtual void draw(GraphicsStateGuardianBase *gsg); + virtual void draw(GraphicsStateGuardianBase *gsg) const; protected: - virtual PT(qpGeomPrimitive) decompose_impl(); + virtual CPT(qpGeomPrimitive) decompose_impl() const; + virtual CPTA_ushort rotate_impl() const; public: static void register_with_read_factory(); diff --git a/panda/src/gobj/qpgeomTristrips.cxx b/panda/src/gobj/qpgeomTristrips.cxx index 53a1e757c7..ff9a66145b 100644 --- a/panda/src/gobj/qpgeomTristrips.cxx +++ b/panda/src/gobj/qpgeomTristrips.cxx @@ -18,6 +18,7 @@ #include "qpgeomTristrips.h" #include "qpgeomTriangles.h" +#include "pStatTimer.h" #include "bamReader.h" #include "bamWriter.h" @@ -52,6 +53,16 @@ qpGeomTristrips:: ~qpGeomTristrips() { } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomTristrips::make_copy +// Access: Published, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +PT(qpGeomPrimitive) qpGeomTristrips:: +make_copy() const { + return new qpGeomTristrips(*this); +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomTristrips::draw // Access: Public, Virtual @@ -59,13 +70,13 @@ qpGeomTristrips:: // primitive. //////////////////////////////////////////////////////////////////// void qpGeomTristrips:: -draw(GraphicsStateGuardianBase *gsg) { +draw(GraphicsStateGuardianBase *gsg) const { gsg->draw_tristrips(this); } //////////////////////////////////////////////////////////////////// // Function: qpGeomTristrips::decompose_impl -// Access: Published, Virtual +// Access: Protected, Virtual // Description: Decomposes a complex primitive type into a simpler // primitive type, for instance triangle strips to // triangles, and returns a pointer to the new primitive @@ -77,49 +88,137 @@ draw(GraphicsStateGuardianBase *gsg) { // primitive without having to write handlers for each // possible kind of primitive type. //////////////////////////////////////////////////////////////////// -PT(qpGeomPrimitive) qpGeomTristrips:: -decompose_impl() { +CPT(qpGeomPrimitive) qpGeomTristrips:: +decompose_impl() const { PT(qpGeomTriangles) triangles = new qpGeomTriangles; + triangles->set_shade_model(get_shade_model()); CPTA_ushort vertices = get_vertices(); CPTA_int ends = get_ends(); - int vi = 0; - int li = 0; - while (li < (int)ends.size()) { - int end = ends[li]; - nassertr(vi + 2 <= end, triangles.p()); - nassertr(vi < (int)vertices.size(), this); - int v0 = vertices[vi]; - ++vi; - nassertr(vi < (int)vertices.size(), this); - int v1 = vertices[vi]; - ++vi; - bool reversed = false; - while (vi < end) { - if (reversed) { - triangles->add_vertex(v1); - triangles->add_vertex(v0); - reversed = false; - } else { - triangles->add_vertex(v0); - triangles->add_vertex(v1); - reversed = true; - } - triangles->add_vertex(vertices[vi]); - v0 = v1; + // We need a slightly different algorithm for SM_flat_first_vertex + // than for SM_flat_last_vertex, to preserve the key vertex in the + // right place. The remaining shade models can either either + // algorithm. + if (get_shade_model() == SM_flat_first_vertex) { + // Preserve the first vertex of each component triangle as the + // first vertex of each generated triangle. + int vi = 0; + int li = 0; + while (li < (int)ends.size()) { + int end = ends[li]; + nassertr(vi + 2 <= end, triangles.p()); nassertr(vi < (int)vertices.size(), this); - v1 = vertices[vi]; - triangles->close_primitive(); + int v0 = vertices[vi]; ++vi; + nassertr(vi < (int)vertices.size(), this); + int v1 = vertices[vi]; + ++vi; + bool reversed = false; + while (vi < end) { + triangles->add_vertex(v0); + if (reversed) { + triangles->add_vertex(vertices[vi]); + triangles->add_vertex(v1); + reversed = false; + } else { + triangles->add_vertex(v1); + triangles->add_vertex(vertices[vi]); + reversed = true; + } + v0 = v1; + nassertr(vi < (int)vertices.size(), this); + v1 = vertices[vi]; + triangles->close_primitive(); + ++vi; + } + ++li; } - ++li; + nassertr(vi == (int)vertices.size(), triangles.p()); + + } else { + // Preserve the last vertex of each component triangle as the + // last vertex of each generated triangle. + int vi = 0; + int li = 0; + while (li < (int)ends.size()) { + int end = ends[li]; + nassertr(vi + 2 <= end, triangles.p()); + nassertr(vi < (int)vertices.size(), this); + int v0 = vertices[vi]; + ++vi; + nassertr(vi < (int)vertices.size(), this); + int v1 = vertices[vi]; + ++vi; + bool reversed = false; + while (vi < end) { + if (reversed) { + triangles->add_vertex(v1); + triangles->add_vertex(v0); + reversed = false; + } else { + triangles->add_vertex(v0); + triangles->add_vertex(v1); + reversed = true; + } + triangles->add_vertex(vertices[vi]); + v0 = v1; + nassertr(vi < (int)vertices.size(), this); + v1 = vertices[vi]; + triangles->close_primitive(); + ++vi; + } + ++li; + } + nassertr(vi == (int)vertices.size(), triangles.p()); } - nassertr(vi == (int)vertices.size(), triangles.p()); - return triangles.p(); } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomTristrips::rotate_impl +// Access: Protected, Virtual +// Description: The virtual implementation of do_rotate(). +//////////////////////////////////////////////////////////////////// +CPTA_ushort qpGeomTristrips:: +rotate_impl() const { + // To rotate a triangle strip with an even number of vertices, we + // just reverse the vertices. But if we have an odd number of + // vertices, that doesn't work--in fact, nothing works (without also + // changing the winding order), so we don't allow an odd number of + // vertices in a flat-shaded tristrip. + CPTA_ushort vertices = get_vertices(); + CPTA_int ends = get_ends(); + PTA_ushort new_vertices; + new_vertices.reserve(vertices.size()); + + bool any_odd = false; + + int begin = 0; + CPTA_int::const_iterator ei; + for (ei = ends.begin(); ei != ends.end(); ++ei) { + int end = (*ei); + int num_vertices = end - begin; + + if ((num_vertices & 1) == 0) { + for (int vi = end - 1; vi >= begin; --vi) { + new_vertices.push_back(vertices[vi]); + } + } else { + any_odd = true; + } + + begin = end; + } + nassertr(new_vertices.size() == vertices.size(), vertices); + + // If this assertion is triggered, there was a triangle strip with + // an odd number of vertices and either SM_flat_first_vertex or + // SM_flat_last_vertex specified--which is not allowed. + nassertr(!any_odd, new_vertices); + return new_vertices; +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomTristrips::register_with_read_factory // Access: Public, Static diff --git a/panda/src/gobj/qpgeomTristrips.h b/panda/src/gobj/qpgeomTristrips.h index bb788026ec..c6f11e8451 100644 --- a/panda/src/gobj/qpgeomTristrips.h +++ b/panda/src/gobj/qpgeomTristrips.h @@ -34,11 +34,14 @@ PUBLISHED: qpGeomTristrips(const qpGeomTristrips ©); virtual ~qpGeomTristrips(); + virtual PT(qpGeomPrimitive) make_copy() const; + public: - virtual void draw(GraphicsStateGuardianBase *gsg); + virtual void draw(GraphicsStateGuardianBase *gsg) const; protected: - virtual PT(qpGeomPrimitive) decompose_impl(); + virtual CPT(qpGeomPrimitive) decompose_impl() const; + virtual CPTA_ushort rotate_impl() const; public: static void register_with_read_factory(); diff --git a/panda/src/gobj/qpgeomVertexCacheManager.I b/panda/src/gobj/qpgeomVertexCacheManager.I index bacc34a164..08e5d80c6d 100644 --- a/panda/src/gobj/qpgeomVertexCacheManager.I +++ b/panda/src/gobj/qpgeomVertexCacheManager.I @@ -69,18 +69,12 @@ get_total_size() const { //////////////////////////////////////////////////////////////////// INLINE void qpGeomVertexCacheManager:: record_munger(const qpGeomMunger *munger) { - Entry entry; - entry._munger = munger; - entry._primitive = NULL; - entry._source = NULL; - entry._format = NULL; - entry._result_size = 100; // Make up a nominal number. - - record_entry(entry); + // Make up a nominal result_size for a munger. + record_entry(Entry(munger, 100)); } //////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexCacheManager::record_decompose +// Function: qpGeomVertexCacheManager::record_primitive // Access: Private // Description: Records a new entry in the cache, or marks a cache // hit for a previous entry in the cache. This should @@ -88,34 +82,23 @@ record_munger(const qpGeomMunger *munger) { // // The cache manager will not hold a reference on the // GeomPrimitive pointer; if it destructs, it should -// call remove_decompose() to remove itself from the cache. +// call remove_primitive() to remove itself from the cache. //////////////////////////////////////////////////////////////////// INLINE void qpGeomVertexCacheManager:: -record_decompose(const qpGeomPrimitive *primitive, int result_size) { - Entry entry; - entry._primitive = primitive; - entry._source = NULL; - entry._format = NULL; - entry._result_size = result_size; - - record_entry(entry); +record_primitive(const qpGeomPrimitive *primitive, int result_size) { + record_entry(Entry(primitive, result_size)); } //////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexCacheManager::remove_decompose +// Function: qpGeomVertexCacheManager::remove_primitive // Access: Private // Description: Removes an entry from the cache, if it is there. // Quietly ignores it if it is not. This should only be // called by GeomPrimitive. //////////////////////////////////////////////////////////////////// INLINE void qpGeomVertexCacheManager:: -remove_decompose(const qpGeomPrimitive *primitive) { - Entry entry; - entry._primitive = primitive; - entry._source = NULL; - entry._format = NULL; - - remove_entry(entry); +remove_primitive(const qpGeomPrimitive *primitive) { + remove_entry(Entry(primitive, 0)); } //////////////////////////////////////////////////////////////////// @@ -130,15 +113,9 @@ remove_decompose(const qpGeomPrimitive *primitive) { // call remove_data() to remove itself from the cache. //////////////////////////////////////////////////////////////////// INLINE void qpGeomVertexCacheManager:: -record_data(const qpGeomVertexData *source, const qpGeomVertexFormat *format, +record_data(const qpGeomVertexData *source, const qpGeomVertexFormat *modifier, int result_size) { - Entry entry; - entry._primitive = NULL; - entry._source = source; - entry._format = format; - entry._result_size = result_size; - - record_entry(entry); + record_entry(Entry(source, modifier, result_size)); } //////////////////////////////////////////////////////////////////// @@ -150,13 +127,139 @@ record_data(const qpGeomVertexData *source, const qpGeomVertexFormat *format, //////////////////////////////////////////////////////////////////// INLINE void qpGeomVertexCacheManager:: remove_data(const qpGeomVertexData *source, - const qpGeomVertexFormat *format) { - Entry entry; - entry._primitive = NULL; - entry._source = source; - entry._format = format; + const qpGeomVertexFormat *modifier) { + remove_entry(Entry(source, modifier, 0)); +} - remove_entry(entry); +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexCacheManager::record_geom +// Access: Private +// Description: Records a new entry in the cache, or marks a cache +// hit for a previous entry in the cache. This should +// only be called by Geom. +// +// The cache manager will not hold a reference on the +// Geom pointer; if it destructs, it should +// call remove_geom() to remove itself from the cache. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexCacheManager:: +record_geom(const qpGeom *source, const qpGeomMunger *modifier, + int result_size) { + record_entry(Entry(source, modifier, result_size)); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexCacheManager::remove_geom +// Access: Private +// Description: Removes an entry from the cache, if it is there. +// Quietly ignores it if it is not. This should only be +// called by Geom. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexCacheManager:: +remove_geom(const qpGeom *source, const qpGeomMunger *modifier) { + remove_entry(Entry(source, modifier, 0)); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexCacheManager::Entry::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE qpGeomVertexCacheManager::Entry:: +Entry(const qpGeomMunger *munger, int result_size) : + _cache_type(CT_munger), + _result_size(result_size) +{ + _u._munger = munger; + _u._munger->ref(); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexCacheManager::Entry::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE qpGeomVertexCacheManager::Entry:: +Entry(const qpGeomPrimitive *primitive, int result_size) : + _cache_type(CT_primitive), + _result_size(result_size) +{ + _u._primitive = primitive; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexCacheManager::Entry::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE qpGeomVertexCacheManager::Entry:: +Entry(const qpGeomVertexData *source, const qpGeomVertexFormat *modifier, + int result_size) : + _cache_type(CT_data), + _result_size(result_size) +{ + _u._data._source = source; + _u._data._modifier = modifier; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexCacheManager::Entry::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE qpGeomVertexCacheManager::Entry:: +Entry(const qpGeom *source, const qpGeomMunger *modifier, int result_size) : + _cache_type(CT_geom), + _result_size(result_size) +{ + _u._geom._source = source; + _u._geom._modifier = modifier; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexCacheManager::Entry::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE qpGeomVertexCacheManager::Entry:: +Entry(const qpGeomVertexCacheManager::Entry ©) : + _cache_type(copy._cache_type), + _result_size(copy._result_size) +{ + _u = copy._u; + if (_cache_type == CT_munger) { + _u._munger->ref(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexCacheManager::Entry::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexCacheManager::Entry:: +operator = (const qpGeomVertexCacheManager::Entry ©) { + if (_cache_type == CT_munger) { + unref_delete(_u._munger); + } + _cache_type = copy._cache_type; + _result_size = copy._result_size; + _u = copy._u; + if (_cache_type == CT_munger) { + _u._munger->ref(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexCacheManager::Entry::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE qpGeomVertexCacheManager::Entry:: +~Entry() { + if (_cache_type == CT_munger) { + unref_delete(_u._munger); + } } //////////////////////////////////////////////////////////////////// @@ -166,14 +269,32 @@ remove_data(const qpGeomVertexData *source, //////////////////////////////////////////////////////////////////// INLINE bool qpGeomVertexCacheManager::Entry:: operator < (const qpGeomVertexCacheManager::Entry &other) const { - if (_munger != other._munger) { - return _munger < other._munger; + switch (_cache_type) { + case CT_munger: + return _u._munger < other._u._munger; + + case CT_primitive: + return _u._primitive < other._u._primitive; + + case CT_data: + if (_u._data._source != other._u._data._source) { + return _u._data._source < other._u._data._source; + } + return _u._data._modifier < other._u._data._modifier; + + case CT_geom: + if (_u._geom._source != other._u._geom._source) { + return _u._geom._source < other._u._geom._source; + } + return _u._geom._modifier < other._u._geom._modifier; } - if (_primitive != other._primitive) { - return _primitive < other._primitive; - } - if (_source != other._source) { - return _source < other._source; - } - return _format < other._format; + + return 0; } + +INLINE ostream & +operator << (ostream &out, const qpGeomVertexCacheManager::Entry &entry) { + entry.output(out); + return out; +} + diff --git a/panda/src/gobj/qpgeomVertexCacheManager.cxx b/panda/src/gobj/qpgeomVertexCacheManager.cxx index b48fb6a0c8..158104323c 100644 --- a/panda/src/gobj/qpgeomVertexCacheManager.cxx +++ b/panda/src/gobj/qpgeomVertexCacheManager.cxx @@ -62,13 +62,6 @@ get_global_ptr() { //////////////////////////////////////////////////////////////////// void qpGeomVertexCacheManager:: record_entry(const qpGeomVertexCacheManager::Entry &entry) { - if (gobj_cat.is_debug()) { - gobj_cat.debug() - << "record_entry(" << entry._munger << ", " - << entry._primitive << ", " << entry._source << ", " - << entry._format << ", " << entry._result_size << ")\n"; - } - MutexHolder holder(_lock); EntriesIndex::iterator ii = _entries_index.find(&entry); if (ii != _entries_index.end()) { @@ -77,6 +70,17 @@ record_entry(const qpGeomVertexCacheManager::Entry &entry) { _total_size -= (*ei)._result_size; _entries_index.erase(ii); _entries.erase(ei); + + if (gobj_cat.is_spam()) { + gobj_cat.spam() + << "refreshing cache entry: " << entry << "\n"; + } + } else { + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << "recording cache entry: " << entry << ", total_size = " + << _total_size + entry._result_size << "\n"; + } } // Add the new entry at the end of the list, so it will be the last @@ -97,30 +101,41 @@ record_entry(const qpGeomVertexCacheManager::Entry &entry) { // is set to 0. int max_size = get_max_size(); while (_total_size > max_size) { - if (gobj_cat.is_debug()) { - gobj_cat.debug() - << "total_size = " << _total_size << ", max_size = " - << max_size << ", flushing\n"; - } nassertv(!_entries.empty()); ei = _entries.begin(); entry_pointer = &(*ei); + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << "cache total_size = " << _total_size << ", max_size = " + << max_size << ", removing " << *entry_pointer << "\n"; + } + ii = _entries_index.find(entry_pointer); nassertv(ii != _entries_index.end()); - if (entry_pointer->_source != (qpGeomVertexData *)NULL) { - entry_pointer->_source->remove_cache_entry(entry_pointer->_format); + switch (entry_pointer->_cache_type) { + case CT_data: + entry_pointer->_u._data._source->remove_cache_entry + (entry_pointer->_u._data._modifier); + break; + + case CT_primitive: + entry_pointer->_u._primitive->remove_cache_entry(); + break; + + case CT_geom: + entry_pointer->_u._geom._source->remove_cache_entry + (entry_pointer->_u._geom._modifier); + break; + + default: + break; } _total_size -= entry_pointer->_result_size; _entries_index.erase(ii); _entries.erase(ei); } - if (gobj_cat.is_debug()) { - gobj_cat.debug() - << "total_size = " << _total_size << ", max_size = " - << max_size << ", done\n"; - } } //////////////////////////////////////////////////////////////////// @@ -133,8 +148,7 @@ void qpGeomVertexCacheManager:: remove_entry(const qpGeomVertexCacheManager::Entry &entry) { if (gobj_cat.is_debug()) { gobj_cat.debug() - << "remove_entry(" << entry._munger << ", " - << entry._source << ", " << entry._format << ")\n"; + << "remove_entry(" << entry << ")\n"; } MutexHolder holder(_lock); @@ -146,3 +160,35 @@ remove_entry(const qpGeomVertexCacheManager::Entry &entry) { _entries.erase(ei); } } + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexCacheManager::Entry::output +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void qpGeomVertexCacheManager::Entry:: +output(ostream &out) const { + out << "[ "; + switch (_cache_type) { + case CT_munger: + out << "munger " << (void *)_u._munger << ":" + << _u._munger->get_ref_count(); + break; + + case CT_primitive: + out << "primitive " << (void *)_u._primitive; + break; + + case CT_data: + out << "data " << (void *)_u._data._source << ", " + << (void *)_u._data._modifier; + break; + + case CT_geom: + out << "geom " << (void *)_u._geom._source << ", " + << (void *)_u._geom._modifier; + break; + } + + out << ", result_size = " << _result_size << " ]"; +} diff --git a/panda/src/gobj/qpgeomVertexCacheManager.h b/panda/src/gobj/qpgeomVertexCacheManager.h index 8b29113fb8..4ed64d30d1 100644 --- a/panda/src/gobj/qpgeomVertexCacheManager.h +++ b/panda/src/gobj/qpgeomVertexCacheManager.h @@ -28,6 +28,7 @@ #include "plist.h" #include "pmap.h" +class qpGeom; class qpGeomPrimitive; class qpGeomVertexData; class qpGeomVertexFormat; @@ -44,6 +45,12 @@ class qpGeomVertexFormat; // GeomVertexData source objects. This allows the cache // data to propagate through the multiprocess pipeline. // +// This structure actually caches any of a number of +// different types of pointers, and mixes them all up in +// the same LRU cache list. Some of them (such as +// GeomMunger) are reference-counted here in the cache; +// most are not. +// // This is part of the experimental Geom rewrite. //////////////////////////////////////////////////////////////////// class EXPCL_PANDA qpGeomVertexCacheManager { @@ -51,6 +58,9 @@ protected: qpGeomVertexCacheManager(); ~qpGeomVertexCacheManager(); +public: + class Entry; + PUBLISHED: INLINE void set_max_size(int max_size) const; INLINE int get_max_size() const; @@ -61,16 +71,18 @@ PUBLISHED: private: INLINE void record_munger(const qpGeomMunger *munger); - INLINE void record_decompose(const qpGeomPrimitive *primitive, + INLINE void record_primitive(const qpGeomPrimitive *primitive, int result_size); - INLINE void remove_decompose(const qpGeomPrimitive *primitive); + INLINE void remove_primitive(const qpGeomPrimitive *primitive); INLINE void record_data(const qpGeomVertexData *source, - const qpGeomVertexFormat *format, + const qpGeomVertexFormat *modifier, int result_size); INLINE void remove_data(const qpGeomVertexData *source, - const qpGeomVertexFormat *format); - - class Entry; + const qpGeomVertexFormat *modifier); + INLINE void record_geom(const qpGeom *source, + const qpGeomMunger *modifier, + int result_size); + INLINE void remove_geom(const qpGeom *geom, const qpGeomMunger *modifier); void record_entry(const Entry &entry); void remove_entry(const Entry &entry); @@ -81,17 +93,52 @@ private: int _total_size; - class Entry { - public: - INLINE bool operator < (const Entry &other) const; - - CPT(qpGeomMunger) _munger; - const qpGeomPrimitive *_primitive; - const qpGeomVertexData *_source; - const qpGeomVertexFormat *_format; - int _result_size; + enum CacheType { + CT_munger, + CT_primitive, + CT_data, + CT_geom, }; +public: + // This class is public only so we can declare the global ostream + // output operator. It doesn't need to be visible outside this + // class. It contains a single cache entry, which might actually be + // any of a handful of different pointer types. The enumerated type + // declared above, and the union declared below, serve to implement + // this C-style polymorphism. + class Entry { + public: + INLINE Entry(const qpGeomMunger *munger, int result_size); + INLINE Entry(const qpGeomPrimitive *primitive, int result_size); + INLINE Entry(const qpGeomVertexData *source, + const qpGeomVertexFormat *modifier, int result_size); + INLINE Entry(const qpGeom *source, const qpGeomMunger *modifier, + int result_size); + INLINE Entry(const Entry ©); + INLINE void operator = (const Entry ©); + INLINE ~Entry(); + INLINE bool operator < (const Entry &other) const; + + void output(ostream &out) const; + + CacheType _cache_type; + int _result_size; + union { + const qpGeomMunger *_munger; + const qpGeomPrimitive *_primitive; + struct { + const qpGeomVertexData *_source; + const qpGeomVertexFormat *_modifier; + } _data; + struct { + const qpGeom *_source; + const qpGeomMunger *_modifier; + } _geom; + } _u; + }; + +private: // This list keeps the cache entries in least-recently-used order: // the items at the head of the list are ready to be flushed. typedef plist Entries; @@ -106,8 +153,11 @@ private: friend class qpGeomMunger; friend class qpGeomPrimitive; friend class qpGeomVertexData; + friend class qpGeom; }; +INLINE ostream &operator << (ostream &out, const qpGeomVertexCacheManager::Entry &entry); + #include "qpgeomVertexCacheManager.I" #endif diff --git a/panda/src/gobj/qpgeomVertexData.cxx b/panda/src/gobj/qpgeomVertexData.cxx index ed6df1bb3c..849374c770 100644 --- a/panda/src/gobj/qpgeomVertexData.cxx +++ b/panda/src/gobj/qpgeomVertexData.cxx @@ -247,7 +247,7 @@ int qpGeomVertexData:: get_num_bytes() const { CDReader cdata(_cycler); - int num_bytes = 0; + int num_bytes = sizeof(qpGeomVertexData); Arrays::const_iterator ai; for (ai = cdata->_arrays.begin(); ai != cdata->_arrays.end(); ++ai) { @@ -738,13 +738,13 @@ unpack_argb(float data[4], unsigned int packed_argb) { // This is only called from GeomVertexCacheManager. //////////////////////////////////////////////////////////////////// void qpGeomVertexData:: -remove_cache_entry(const qpGeomVertexFormat *format) const { +remove_cache_entry(const qpGeomVertexFormat *modifier) const { // We have to operate on stage 0 of the pipeline, since that's where // the cache really counts. Because of the multistage pipeline, we // might not actually have a cache entry there (it might have been // added to stage 1 instead). No big deal if we don't. CData *cdata = ((qpGeomVertexData *)this)->_cycler.write_stage(0); - ConvertedCache::iterator ci = cdata->_converted_cache.find(format); + ConvertedCache::iterator ci = cdata->_converted_cache.find(modifier); if (ci != cdata->_converted_cache.end()) { cdata->_converted_cache.erase(ci); } diff --git a/panda/src/gobj/qpgeomVertexData.h b/panda/src/gobj/qpgeomVertexData.h index 5936a9c954..131474f876 100644 --- a/panda/src/gobj/qpgeomVertexData.h +++ b/panda/src/gobj/qpgeomVertexData.h @@ -105,7 +105,7 @@ public: static void unpack_argb(float data[4], unsigned int packed_argb); private: - void remove_cache_entry(const qpGeomVertexFormat *format) const; + void remove_cache_entry(const qpGeomVertexFormat *modifier) const; private: CPT(qpGeomVertexFormat) _format; diff --git a/panda/src/gsgbase/graphicsStateGuardianBase.h b/panda/src/gsgbase/graphicsStateGuardianBase.h index b47a02257d..35dc30f72a 100644 --- a/panda/src/gsgbase/graphicsStateGuardianBase.h +++ b/panda/src/gsgbase/graphicsStateGuardianBase.h @@ -174,9 +174,9 @@ public: virtual void draw_sphere(GeomSphere *geom, GeomContext *gc)=0; virtual bool begin_draw_primitives(const qpGeomVertexData *vertex_data)=0; - virtual void draw_triangles(qpGeomTriangles *primitive)=0; - virtual void draw_tristrips(qpGeomTristrips *primitive)=0; - virtual void draw_trifans(qpGeomTrifans *primitive)=0; + virtual void draw_triangles(const qpGeomTriangles *primitive)=0; + virtual void draw_tristrips(const qpGeomTristrips *primitive)=0; + virtual void draw_trifans(const qpGeomTrifans *primitive)=0; virtual void end_draw_primitives()=0; virtual void framebuffer_copy_to_texture diff --git a/panda/src/pgraph/cullResult.cxx b/panda/src/pgraph/cullResult.cxx index d5dd122f36..44d2436704 100644 --- a/panda/src/pgraph/cullResult.cxx +++ b/panda/src/pgraph/cullResult.cxx @@ -128,7 +128,7 @@ add_object(CullableObject *object) { get_dual_transparent_state_decals() : get_dual_transparent_state(); transparent_part->_state = state->compose(transparent_state); - transparent_part->munge_vertices(_gsg); + transparent_part->munge_geom(_gsg); CullBin *bin = get_bin(transparent_part->_state->get_bin_index()); nassertv(bin != (CullBin *)NULL); bin->add_object(transparent_part); @@ -155,7 +155,7 @@ add_object(CullableObject *object) { // Munge vertices as needed for the GSG's requirements, and the // object's current state. - object->munge_vertices(_gsg); + object->munge_geom(_gsg); CullBin *bin = get_bin(object->_state->get_bin_index()); nassertv(bin != (CullBin *)NULL); diff --git a/panda/src/pgraph/cullableObject.I b/panda/src/pgraph/cullableObject.I index 3eb8cb1c61..8abdacc9bd 100644 --- a/panda/src/pgraph/cullableObject.I +++ b/panda/src/pgraph/cullableObject.I @@ -104,23 +104,15 @@ has_decals() const { } //////////////////////////////////////////////////////////////////// -// Function: CullableObject::munge_vertices +// Function: CullableObject::munge_geom // Access: Public // Description: Gets a GeomMunger from the GSG to transform the -// vertices to meet the GSG's vertex requirements for -// the current state. +// geom and/or its vertices to meet the GSG's vertex +// requirements for the current state. //////////////////////////////////////////////////////////////////// INLINE void CullableObject:: -munge_vertices(GraphicsStateGuardianBase *gsg) { - // Temporary test until the experimental Geom rewrite becomes the - // actual Geom implementation. - if (_geom == (Geom *)NULL || _geom->is_exact_type(qpGeom::get_class_type())) { - CPT(qpGeomMunger) munger = gsg->get_geom_munger(_state); - _munged_data = munger->munge_data(((const qpGeom *)_geom.p())->get_vertex_data()); - if (_next != (CullableObject *)NULL) { - _next->munge_vertices(gsg); - } - } +munge_geom(GraphicsStateGuardianBase *gsg) { + munge_geom(gsg->get_geom_munger(_state)); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/pgraph/cullableObject.cxx b/panda/src/pgraph/cullableObject.cxx index 7761df1f81..49f890154f 100644 --- a/panda/src/pgraph/cullableObject.cxx +++ b/panda/src/pgraph/cullableObject.cxx @@ -23,6 +23,28 @@ CullableObject *CullableObject::_deleted_chain = (CullableObject *)NULL; int CullableObject::_num_ever_allocated = 0; TypeHandle CullableObject::_type_handle; +//////////////////////////////////////////////////////////////////// +// Function: CullableObject::munge_geom +// Access: Public +// Description: Uses the indicated GeomMunger to transform the geom +// and/or its vertices. +//////////////////////////////////////////////////////////////////// +void CullableObject:: +munge_geom(const qpGeomMunger *munger) { + if (_geom != (Geom *)NULL) { + // Temporary test and dcast until the experimental Geom rewrite + // becomes the actual Geom rewrite. + if (_geom->is_exact_type(qpGeom::get_class_type())) { + CPT(qpGeom) qpgeom = DCAST(qpGeom, _geom); + qpgeom->munge_geom(munger, qpgeom, _munged_data); + _geom = qpgeom; + } + } + if (_next != (CullableObject *)NULL) { + _next->munge_geom(munger); + } +} + //////////////////////////////////////////////////////////////////// // Function: CullableObject::Destructor // Access: Public diff --git a/panda/src/pgraph/cullableObject.h b/panda/src/pgraph/cullableObject.h index b2b02f8a12..eedd56db29 100644 --- a/panda/src/pgraph/cullableObject.h +++ b/panda/src/pgraph/cullableObject.h @@ -54,7 +54,8 @@ public: INLINE bool has_decals() const; - INLINE void munge_vertices(GraphicsStateGuardianBase *gsg); + INLINE void munge_geom(GraphicsStateGuardianBase *gsg); + void munge_geom(const qpGeomMunger *munger); INLINE void draw(GraphicsStateGuardianBase *gsg); public: diff --git a/panda/src/pgraph/drawCullHandler.cxx b/panda/src/pgraph/drawCullHandler.cxx index 1d923afb1a..724ccb35a4 100644 --- a/panda/src/pgraph/drawCullHandler.cxx +++ b/panda/src/pgraph/drawCullHandler.cxx @@ -35,7 +35,7 @@ void DrawCullHandler:: record_object(CullableObject *object) { // Munge vertices as needed for the GSG's requirements, and the // object's current state. - object->munge_vertices(_gsg); + object->munge_geom(_gsg); // And draw the object, then dispense with it. draw(object, _gsg); diff --git a/panda/src/pgraph/renderState.I b/panda/src/pgraph/renderState.I index 919e7581e9..da2f70de30 100644 --- a/panda/src/pgraph/renderState.I +++ b/panda/src/pgraph/renderState.I @@ -154,6 +154,24 @@ get_bin_index() const { return _bin_index; } +//////////////////////////////////////////////////////////////////// +// Function: RenderState::is_flat_shaded +// Access: Published +// Description: Returns true if this RenderState specifies a flat +// shading model (that is, per-vertex color applies to +// entire triangles), or false if it specifies a smooth +// shading model or does not specify. +//////////////////////////////////////////////////////////////////// +INLINE bool RenderState:: +is_flat_shaded() const { + if ((_flags & F_checked_flat_shaded) == 0) { + // We pretend this function is const, even though it transparently + // modifies the internal flat_shaded cache. + ((RenderState *)this)->determine_flat_shaded(); + } + return ((_flags & F_flat_shaded) != 0); +} + //////////////////////////////////////////////////////////////////// // Function: RenderState::set_destructing // Access: Private diff --git a/panda/src/pgraph/renderState.cxx b/panda/src/pgraph/renderState.cxx index e2728ff90c..ad3144ea4b 100644 --- a/panda/src/pgraph/renderState.cxx +++ b/panda/src/pgraph/renderState.cxx @@ -22,6 +22,7 @@ #include "cullBinManager.h" #include "fogAttrib.h" #include "transparencyAttrib.h" +#include "shadeModelAttrib.h" #include "pStatTimer.h" #include "config_pgraph.h" #include "bamReader.h" @@ -1511,6 +1512,26 @@ determine_transparency() { _flags |= F_checked_transparency; } +//////////////////////////////////////////////////////////////////// +// Function: RenderState::determine_flat_shaded +// Access: Private +// Description: This is the private implementation of +// is_flat_shaded(). +//////////////////////////////////////////////////////////////////// +void RenderState:: +determine_flat_shaded() { + const RenderAttrib *attrib = + get_attrib(ShadeModelAttrib::get_class_type()); + if (attrib != (const RenderAttrib *)NULL) { + const ShadeModelAttrib *sma = DCAST(ShadeModelAttrib, attrib); + if (sma->get_mode() == ShadeModelAttrib::M_flat) { + _flags |= F_flat_shaded; + } + } + + _flags |= F_checked_flat_shaded; +} + //////////////////////////////////////////////////////////////////// // Function: RenderState::register_with_read_factory // Access: Public, Static diff --git a/panda/src/pgraph/renderState.h b/panda/src/pgraph/renderState.h index 46cd1d12b6..1dc274fc1f 100644 --- a/panda/src/pgraph/renderState.h +++ b/panda/src/pgraph/renderState.h @@ -114,6 +114,7 @@ PUBLISHED: INLINE const CullBinAttrib *get_bin() const; INLINE const TransparencyAttrib *get_transparency() const; INLINE int get_bin_index() const; + INLINE bool is_flat_shaded() const; public: CPT(RenderState) issue_delta_modify(const RenderState *other, @@ -150,6 +151,7 @@ private: void determine_fog(); void determine_bin(); void determine_transparency(); + void determine_flat_shaded(); INLINE void set_destructing(); INLINE bool is_destructing() const; @@ -229,6 +231,8 @@ private: F_checked_fog = 0x0002, F_checked_bin = 0x0004, F_checked_transparency = 0x0008, + F_checked_flat_shaded = 0x0010, + F_flat_shaded = 0x0020, F_is_destructing = 0x8000, }; unsigned short _flags; diff --git a/panda/src/pstatclient/pStatProperties.cxx b/panda/src/pstatclient/pStatProperties.cxx index 5973616ded..ea05b89a34 100644 --- a/panda/src/pstatclient/pStatProperties.cxx +++ b/panda/src/pstatclient/pStatProperties.cxx @@ -126,6 +126,7 @@ static TimeCollectorProperties time_properties[] = { { 1, "Cull:Show fps", { 0.5, 0.8, 1.0 } }, { 1, "Cull:Bins", { 0.3, 0.6, 0.3 } }, { 1, "Cull:Munge", { 0.3, 0.3, 0.9 } }, + { 1, "Cull:Rotate", { 0.9, 0.8, 0.5 } }, { 1, "Draw", { 1.0, 0.0, 0.0 }, 1.0 / 30.0 }, { 1, "Draw:Make current", { 0.4, 0.2, 0.6 } }, { 1, "Draw:Copy texture", { 0.2, 0.6, 0.4 } }, diff --git a/pandatool/src/egg-qtess/eggQtess.cxx b/pandatool/src/egg-qtess/eggQtess.cxx index 10053cd396..b0b1a17c85 100644 --- a/pandatool/src/egg-qtess/eggQtess.cxx +++ b/pandatool/src/egg-qtess/eggQtess.cxx @@ -215,7 +215,7 @@ run() { // vertices. _surfaces.clear(); - _data->remove_unused_vertices(); + _data->remove_unused_vertices(true); write_egg_file(); } } diff --git a/pandatool/src/eggbase/eggMultiBase.cxx b/pandatool/src/eggbase/eggMultiBase.cxx index 0602359c2e..0b403e0778 100644 --- a/pandatool/src/eggbase/eggMultiBase.cxx +++ b/pandatool/src/eggbase/eggMultiBase.cxx @@ -84,7 +84,7 @@ post_process_egg_files() { nout << "Stripping normals.\n"; for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) { (*ei)->strip_normals(); - (*ei)->remove_unused_vertices(); + (*ei)->remove_unused_vertices(true); } break; @@ -92,7 +92,7 @@ post_process_egg_files() { nout << "Recomputing polygon normals.\n"; for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) { (*ei)->recompute_polygon_normals(); - (*ei)->remove_unused_vertices(); + (*ei)->remove_unused_vertices(true); } break; @@ -100,7 +100,7 @@ post_process_egg_files() { nout << "Recomputing vertex normals.\n"; for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) { (*ei)->recompute_vertex_normals(_normals_threshold); - (*ei)->remove_unused_vertices(); + (*ei)->remove_unused_vertices(true); } break; diff --git a/pandatool/src/eggbase/eggWriter.cxx b/pandatool/src/eggbase/eggWriter.cxx index 137a845ef9..8e43bff5f5 100644 --- a/pandatool/src/eggbase/eggWriter.cxx +++ b/pandatool/src/eggbase/eggWriter.cxx @@ -142,19 +142,19 @@ post_process_egg_file() { case NM_strip: nout << "Stripping normals.\n"; _data->strip_normals(); - _data->remove_unused_vertices(); + _data->remove_unused_vertices(true); break; case NM_polygon: nout << "Recomputing polygon normals.\n"; _data->recompute_polygon_normals(); - _data->remove_unused_vertices(); + _data->remove_unused_vertices(true); break; case NM_vertex: nout << "Recomputing vertex normals.\n"; _data->recompute_vertex_normals(_normals_threshold); - _data->remove_unused_vertices(); + _data->remove_unused_vertices(true); break; case NM_preserve: diff --git a/pandatool/src/eggprogs/eggCrop.cxx b/pandatool/src/eggprogs/eggCrop.cxx index ae6a7c367a..cb4241f7cc 100644 --- a/pandatool/src/eggprogs/eggCrop.cxx +++ b/pandatool/src/eggprogs/eggCrop.cxx @@ -76,7 +76,7 @@ run() { int num_removed = strip_prims(_data); nout << "Removed " << num_removed << " primitives.\n"; - _data->remove_unused_vertices(); + _data->remove_unused_vertices(true); write_egg_file(); } diff --git a/pandatool/src/eggprogs/eggToC.cxx b/pandatool/src/eggprogs/eggToC.cxx index 7bd19d5877..4f761362e4 100644 --- a/pandatool/src/eggprogs/eggToC.cxx +++ b/pandatool/src/eggprogs/eggToC.cxx @@ -95,7 +95,7 @@ EggToC() : void EggToC:: run() { nout << "Removing invalid primitives.\n"; - int num_removed = _data->remove_invalid_primitives(); + int num_removed = _data->remove_invalid_primitives(true); nout << " (" << num_removed << " removed.)\n"; if (_triangulate_polygons) { @@ -106,7 +106,7 @@ run() { _data->apply_texmats(); _data->flatten_transforms(); - _data->remove_unused_vertices(); + _data->remove_unused_vertices(true); // Collect all the polygons together into polysets. EggPolysetMaker pmaker; diff --git a/pandatool/src/eggprogs/eggTrans.cxx b/pandatool/src/eggprogs/eggTrans.cxx index 6c6571dfc0..56d9964595 100644 --- a/pandatool/src/eggprogs/eggTrans.cxx +++ b/pandatool/src/eggprogs/eggTrans.cxx @@ -89,9 +89,9 @@ void EggTrans:: run() { if (_remove_invalid_primitives) { nout << "Removing invalid primitives.\n"; - int num_removed = _data->remove_invalid_primitives(); + int num_removed = _data->remove_invalid_primitives(true); nout << " (" << num_removed << " removed.)\n"; - _data->remove_unused_vertices(); + _data->remove_unused_vertices(true); } if (_triangulate_polygons) { @@ -108,7 +108,7 @@ run() { if (_apply_texmats) { nout << "Applying texture matrices.\n"; _data->apply_texmats(); - _data->remove_unused_vertices(); + _data->remove_unused_vertices(true); } if (_collapse_equivalent_textures) { @@ -120,7 +120,7 @@ run() { if (_flatten_transforms) { nout << "Flattening transforms.\n"; _data->flatten_transforms(); - _data->remove_unused_vertices(); + _data->remove_unused_vertices(true); } if (_standardize_names) { diff --git a/pandatool/src/lwoegg/lwoToEggConverter.cxx b/pandatool/src/lwoegg/lwoToEggConverter.cxx index 056c10ea0b..9f798388a4 100644 --- a/pandatool/src/lwoegg/lwoToEggConverter.cxx +++ b/pandatool/src/lwoegg/lwoToEggConverter.cxx @@ -164,7 +164,7 @@ convert_lwo(const LwoHeader *lwo_header) { make_egg(); connect_egg(); - _egg_data->remove_unused_vertices(); + _egg_data->remove_unused_vertices(true); cleanup(); return !had_error(); diff --git a/pandatool/src/xfileegg/xFileMaker.cxx b/pandatool/src/xfileegg/xFileMaker.cxx index 830902f94f..7003678b8b 100644 --- a/pandatool/src/xfileegg/xFileMaker.cxx +++ b/pandatool/src/xfileegg/xFileMaker.cxx @@ -198,7 +198,7 @@ add_bin(EggBin *egg_bin, XFileNode *x_parent) { bool XFileMaker:: add_polyset(EggBin *egg_bin, XFileNode *x_parent) { // Make sure that all our polygons are reasonable. - egg_bin->remove_invalid_primitives(); + egg_bin->remove_invalid_primitives(true); XFileMesh *mesh = get_mesh(x_parent);