support flat-shading properly in new geom

This commit is contained in:
David Rose 2005-03-17 01:16:28 +00:00
parent e58313c806
commit d72e2da5c5
69 changed files with 2069 additions and 729 deletions

View File

@ -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);
}

View File

@ -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);

View File

@ -23,5 +23,7 @@
// Description:
////////////////////////////////////////////////////////////////////
INLINE DXGeomMunger8::
DXGeomMunger8() {
DXGeomMunger8(GraphicsStateGuardian *gsg, const RenderState *state) :
qpGeomMunger(gsg, state)
{
}

View File

@ -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;
}

View File

@ -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() {

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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()) {

View File

@ -37,6 +37,8 @@ PUBLISHED:
INLINE EggCompositePrimitive &operator = (const EggCompositePrimitive &copy);
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:

View File

@ -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);
}
}
}

View File

@ -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;

View File

@ -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 &copy) :
_bface(copy._bface)
{
copy_vertices(copy);
_connected_shading = S_unknown;
}
////////////////////////////////////////////////////////////////////
@ -56,6 +58,7 @@ operator = (const EggPrimitive &copy) {
_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

View File

@ -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);
}
}
}
}

View File

@ -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 &copy);
INLINE EggPrimitive &operator = (const EggPrimitive &copy);
@ -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:

View File

@ -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

View File

@ -35,6 +35,9 @@ PUBLISHED:
INLINE EggTriangleStrip(const EggTriangleStrip &copy);
INLINE EggTriangleStrip &operator = (const EggTriangleStrip &copy);
virtual void apply_last_attribute();
virtual void apply_first_attribute();
virtual void write(ostream &out, int indent_level) const;
protected:

View File

@ -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;
}

View File

@ -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

View File

@ -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<Primitives::iterator, bool> 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.

View File

@ -57,6 +57,7 @@ class CollisionPlane;
class CollisionPolygon;
class PortalNode;
class PolylightNode;
class EggRenderState;
///////////////////////////////////////////////////////////////////
// Class : EggLoader
@ -99,7 +100,15 @@ private:
typedef pmap<CPT(InternalName), const EggTexture *> BakeInUVs;
// This is used by make_primitive().
typedef pmap<TypeHandle, PT(qpGeomPrimitive) > Primitives;
class PrimitiveUnifier {
public:
INLINE PrimitiveUnifier(const qpGeomPrimitive *prim);
INLINE bool operator < (const PrimitiveUnifier &other) const;
TypeHandle _type;
qpGeomPrimitive::ShadeModel _shade_model;
};
typedef pmap<PrimitiveUnifier, PT(qpGeomPrimitive) > 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);

View File

@ -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;
}

View File

@ -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));

View File

@ -63,7 +63,7 @@ private:
public:
CPT(RenderState) _state;
bool _hidden;
bool _indexed;
bool _flat_shaded;
typedef EggLoader::BakeInUVs BakeInUVs;
typedef EggLoader::TextureDef TextureDef;

View File

@ -23,5 +23,7 @@
// Description:
////////////////////////////////////////////////////////////////////
INLINE CLP(GeomMunger)::
CLP(GeomMunger)() {
CLP(GeomMunger)(GraphicsStateGuardian *gsg, const RenderState *state) :
qpGeomMunger(gsg, state)
{
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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;

View File

@ -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;
}
////////////////////////////////////////////////////////////////////

View File

@ -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 &copy) {
TypedWritableReferenceCount::operator = (copy);
BoundedObject::operator = (copy);
*/
clear_cache();
Geom::operator = (copy);
_cycler = copy._cycler;
mark_bound_stale();
@ -70,6 +73,25 @@ operator = (const qpGeom &copy) {
////////////////////////////////////////////////////////////////////
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

View File

@ -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<PT(qpGeomPrimitive) > 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<CPT(qpGeomMunger), MungeResult> 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<CData> _cycler;
@ -131,6 +155,8 @@ public:
private:
static TypeHandle _type_handle;
friend class qpGeomVertexCacheManager;
};
INLINE ostream &operator << (ostream &out, const qpGeom &obj);

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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 &copy) :
_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),

View File

@ -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);
}
////////////////////////////////////////////////////////////////////

View File

@ -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 &copy);
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<CData> _cycler;

View File

@ -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

View File

@ -34,10 +34,15 @@ PUBLISHED:
qpGeomTriangles(const qpGeomTriangles &copy);
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();

View File

@ -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

View File

@ -34,11 +34,14 @@ PUBLISHED:
qpGeomTrifans(const qpGeomTrifans &copy);
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();

View File

@ -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

View File

@ -34,11 +34,14 @@ PUBLISHED:
qpGeomTristrips(const qpGeomTristrips &copy);
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();

View File

@ -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 &copy) :
_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 &copy) {
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;
}

View File

@ -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 << " ]";
}

View File

@ -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 &copy);
INLINE void operator = (const Entry &copy);
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<Entry> 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

View File

@ -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);
}

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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));
}
////////////////////////////////////////////////////////////////////

View File

@ -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

View File

@ -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:

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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 } },

View File

@ -215,7 +215,7 @@ run() {
// vertices.
_surfaces.clear();
_data->remove_unused_vertices();
_data->remove_unused_vertices(true);
write_egg_file();
}
}

View File

@ -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;

View File

@ -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:

View File

@ -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();
}

View File

@ -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;

View File

@ -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) {

View File

@ -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();

View File

@ -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);