diff --git a/panda/src/char/jointVertexTransform.I b/panda/src/char/jointVertexTransform.I index 73e4ffc29c..2560ae6056 100644 --- a/panda/src/char/jointVertexTransform.I +++ b/panda/src/char/jointVertexTransform.I @@ -27,3 +27,18 @@ INLINE const CharacterJoint *JointVertexTransform:: get_joint() const { return _joint; } + +//////////////////////////////////////////////////////////////////// +// Function: JointVertexTransform::check_matrix +// Access: Private +// Description: Recomputes _matrix if it needs it. +//////////////////////////////////////////////////////////////////// +INLINE void JointVertexTransform:: +check_matrix() const { + if (_matrix_stale) { + ((JointVertexTransform *)this)->_matrix = + _joint->_initial_net_transform_inverse * + _joint->_net_transform; + ((JointVertexTransform *)this)->_matrix_stale = false; + } +} diff --git a/panda/src/char/jointVertexTransform.cxx b/panda/src/char/jointVertexTransform.cxx index 038edf1053..e1968460db 100644 --- a/panda/src/char/jointVertexTransform.cxx +++ b/panda/src/char/jointVertexTransform.cxx @@ -70,16 +70,59 @@ JointVertexTransform:: //////////////////////////////////////////////////////////////////// void JointVertexTransform:: get_matrix(LMatrix4f &matrix) const { - if (_matrix_stale) { - ((JointVertexTransform *)this)->_matrix = - _joint->_initial_net_transform_inverse * - _joint->_net_transform; - ((JointVertexTransform *)this)->_matrix_stale = false; - } - + check_matrix(); matrix = _matrix; } +//////////////////////////////////////////////////////////////////// +// Function: JointVertexTransform::mult_matrix +// Access: Published, Virtual +// Description: Premultiplies this transform's matrix with the +// indicated previous matrix, so that the result is the +// net composition of the given transform with this +// transform. The result is stored in the parameter +// "result", which should not be the same matrix as +// previous. +//////////////////////////////////////////////////////////////////// +void JointVertexTransform:: +mult_matrix(LMatrix4f &result, const LMatrix4f &previous) const { + check_matrix(); + result.multiply(_matrix, previous); +} + +//////////////////////////////////////////////////////////////////// +// Function: JointVertexTransform::accumulate_matrix +// Access: Published, Virtual +// Description: Adds the value of this transform's matrix, modified +// by the indicated weight, into the indicated +// accumulation matrix. This is used to compute the +// result of several blended transforms. +//////////////////////////////////////////////////////////////////// +void JointVertexTransform:: +accumulate_matrix(LMatrix4f &accum, float weight) const { + check_matrix(); + + accum._m.m._00 += _matrix._m.m._00 * weight; + accum._m.m._01 += _matrix._m.m._01 * weight; + accum._m.m._02 += _matrix._m.m._02 * weight; + accum._m.m._03 += _matrix._m.m._03 * weight; + + accum._m.m._10 += _matrix._m.m._10 * weight; + accum._m.m._11 += _matrix._m.m._11 * weight; + accum._m.m._12 += _matrix._m.m._12 * weight; + accum._m.m._13 += _matrix._m.m._13 * weight; + + accum._m.m._20 += _matrix._m.m._20 * weight; + accum._m.m._21 += _matrix._m.m._21 * weight; + accum._m.m._22 += _matrix._m.m._22 * weight; + accum._m.m._23 += _matrix._m.m._23 * weight; + + accum._m.m._30 += _matrix._m.m._30 * weight; + accum._m.m._31 += _matrix._m.m._31 * weight; + accum._m.m._32 += _matrix._m.m._32 * weight; + accum._m.m._33 += _matrix._m.m._33 * weight; +} + //////////////////////////////////////////////////////////////////// // Function: JointVertexTransform::output // Access: Published, Virtual diff --git a/panda/src/char/jointVertexTransform.h b/panda/src/char/jointVertexTransform.h index 988e053607..26f5c8f8b4 100644 --- a/panda/src/char/jointVertexTransform.h +++ b/panda/src/char/jointVertexTransform.h @@ -51,10 +51,14 @@ PUBLISHED: INLINE const CharacterJoint *get_joint() const; virtual void get_matrix(LMatrix4f &matrix) const; + virtual void mult_matrix(LMatrix4f &result, const LMatrix4f &previous) const; + virtual void accumulate_matrix(LMatrix4f &accum, float weight) const; virtual void output(ostream &out) const; private: + INLINE void check_matrix() const; + PT(CharacterJoint) _joint; LMatrix4f _matrix; diff --git a/panda/src/display/Sources.pp b/panda/src/display/Sources.pp index 554db7e077..2b685b2b2b 100644 --- a/panda/src/display/Sources.pp +++ b/panda/src/display/Sources.pp @@ -11,7 +11,7 @@ #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx #define SOURCES \ - colorMunger.I colorMunger.h \ + standardMunger.I standardMunger.h \ config_display.h \ drawableRegion.I drawableRegion.h \ displayRegion.I displayRegion.h \ @@ -35,7 +35,7 @@ lensStack.I lensStack.h #define INCLUDED_SOURCES \ - colorMunger.cxx \ + standardMunger.cxx \ config_display.cxx \ drawableRegion.cxx \ displayRegion.cxx \ @@ -53,7 +53,7 @@ windowProperties.cxx #define INSTALL_HEADERS \ - colorMunger.I colorMunger.h \ + standardMunger.I standardMunger.h \ config_display.h \ drawableRegion.I drawableRegion.h \ displayRegion.I displayRegion.h displayRegionStack.I \ diff --git a/panda/src/display/config_display.cxx b/panda/src/display/config_display.cxx index 3d172c83d9..b66ac2154e 100644 --- a/panda/src/display/config_display.cxx +++ b/panda/src/display/config_display.cxx @@ -18,7 +18,7 @@ #include "config_display.h" -#include "colorMunger.h" +#include "standardMunger.h" #include "graphicsStateGuardian.h" #include "graphicsPipe.h" #include "graphicsOutput.h" @@ -248,7 +248,7 @@ init_libdisplay() { } initialized = true; - ColorMunger::init_type(); + StandardMunger::init_type(); GraphicsStateGuardian::init_type(); GraphicsPipe::init_type(); GraphicsOutput::init_type(); diff --git a/panda/src/display/display_composite1.cxx b/panda/src/display/display_composite1.cxx index df4ffd42a0..afbd8a6bd1 100644 --- a/panda/src/display/display_composite1.cxx +++ b/panda/src/display/display_composite1.cxx @@ -1,4 +1,4 @@ -#include "colorMunger.cxx" +#include "standardMunger.cxx" #include "drawableRegion.cxx" #include "displayRegion.cxx" #include "graphicsEngine.cxx" diff --git a/panda/src/display/graphicsStateGuardian.I b/panda/src/display/graphicsStateGuardian.I index 7ba0b4fbc1..02d3883d50 100644 --- a/panda/src/display/graphicsStateGuardian.I +++ b/panda/src/display/graphicsStateGuardian.I @@ -201,6 +201,44 @@ get_max_cube_map_dimension() const { return _max_cube_map_dimension; } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::get_max_vertex_transforms +// Access: Published +// Description: Returns the maximum number of transform matrices that +// may be simultaneously used to transform any one +// vertex by the graphics hardware. If this number is +// 0, then the hardware (or the graphics backend) +// doesn't support soft-skinned vertices (in which case +// Panda will animate the vertices in software). +// +// The value returned may not be meaningful until after +// the graphics context has been fully created (e.g. the +// window has been opened). +//////////////////////////////////////////////////////////////////// +INLINE int GraphicsStateGuardian:: +get_max_vertex_transforms() const { + return _max_vertex_transforms; +} + +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::get_max_vertex_transform_indices +// Access: Published +// Description: Returns the maximum number of transforms there may be +// in a single TransformPalette for this graphics +// hardware. If this number is 0 (but +// get_max_transforms() is nonzero), then the graphics +// hardware (or API) doesn't support indexed transforms, +// but can support direct transform references. +// +// The value returned may not be meaningful until after +// the graphics context has been fully created (e.g. the +// window has been opened). +//////////////////////////////////////////////////////////////////// +INLINE int GraphicsStateGuardian:: +get_max_vertex_transform_indices() const { + return _max_vertex_transform_indices; +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::get_copy_texture_inverted // Access: Published diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index ffcbcf1931..2d1162464b 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -104,6 +104,10 @@ GraphicsStateGuardian(const FrameBufferProperties &properties, _max_3d_texture_dimension = 0; _max_cube_map_dimension = 0; + // Assume no vertex blending capability. + _max_vertex_transforms = 0; + _max_vertex_transform_indices = 0; + // Initially, we set this to false; a GSG that knows it has this // property should set it to true. _copy_texture_inverted = false; diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index f035166a6d..d9295afeb5 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -88,6 +88,9 @@ PUBLISHED: INLINE int get_max_3d_texture_dimension() const; INLINE int get_max_cube_map_dimension() const; + INLINE int get_max_vertex_transforms() const; + INLINE int get_max_vertex_transform_indices() const; + INLINE bool get_copy_texture_inverted() const; virtual bool get_supports_multisample() const; INLINE bool get_supports_generate_mipmap() const; @@ -314,6 +317,9 @@ protected: int _max_3d_texture_dimension; int _max_cube_map_dimension; + int _max_vertex_transforms; + int _max_vertex_transform_indices; + bool _copy_texture_inverted; bool _supports_multisample; bool _supports_generate_mipmap; diff --git a/panda/src/display/colorMunger.I b/panda/src/display/standardMunger.I similarity index 95% rename from panda/src/display/colorMunger.I rename to panda/src/display/standardMunger.I index 4568a07c26..54853325eb 100644 --- a/panda/src/display/colorMunger.I +++ b/panda/src/display/standardMunger.I @@ -1,4 +1,4 @@ -// Filename: colorMunger.I +// Filename: standardMunger.I // Created by: drose (21Mar05) // //////////////////////////////////////////////////////////////////// diff --git a/panda/src/display/colorMunger.cxx b/panda/src/display/standardMunger.cxx similarity index 55% rename from panda/src/display/colorMunger.cxx rename to panda/src/display/standardMunger.cxx index 9a08da748e..3668879c25 100644 --- a/panda/src/display/colorMunger.cxx +++ b/panda/src/display/standardMunger.cxx @@ -1,4 +1,4 @@ -// Filename: colorMunger.cxx +// Filename: standardMunger.cxx // Created by: drose (21Mar05) // //////////////////////////////////////////////////////////////////// @@ -16,47 +16,53 @@ // //////////////////////////////////////////////////////////////////// -#include "colorMunger.h" +#include "standardMunger.h" #include "renderState.h" +#include "graphicsStateGuardian.h" #include "dcast.h" +#include "config_gobj.h" -TypeHandle ColorMunger::_type_handle; +TypeHandle StandardMunger::_type_handle; //////////////////////////////////////////////////////////////////// -// Function: ColorMunger::Constructor +// Function: StandardMunger::Constructor // Access: Public -// Description: +// Description: The StandardMunger constructor accepts additional +// parameters that specify the GSG's preferred color +// format (since we might be munging the color anyway, +// we might as well convert it as we munge). //////////////////////////////////////////////////////////////////// -ColorMunger:: -ColorMunger(const GraphicsStateGuardianBase *gsg, const RenderState *state, - int num_components, - qpGeomVertexDataType::NumericType numeric_type, - qpGeomVertexDataType::Contents contents) : +StandardMunger:: +StandardMunger(const GraphicsStateGuardianBase *gsg, const RenderState *state, + int num_components, + qpGeomVertexDataType::NumericType numeric_type, + qpGeomVertexDataType::Contents contents) : qpGeomMunger(gsg, state), _num_components(num_components), _numeric_type(numeric_type), _contents(contents) { + _gsg = DCAST(GraphicsStateGuardian, gsg); _color = state->get_color(); _color_scale = state->get_color_scale(); } //////////////////////////////////////////////////////////////////// -// Function: ColorMunger::Destructor +// Function: StandardMunger::Destructor // Access: Public, Virtual // Description: //////////////////////////////////////////////////////////////////// -ColorMunger:: -~ColorMunger() { +StandardMunger:: +~StandardMunger() { } //////////////////////////////////////////////////////////////////// -// Function: ColorMunger::munge_data_impl +// Function: StandardMunger::munge_data_impl // Access: Protected, Virtual // Description: Given a source GeomVertexData, converts it as // necessary for rendering. //////////////////////////////////////////////////////////////////// -CPT(qpGeomVertexData) ColorMunger:: +CPT(qpGeomVertexData) StandardMunger:: munge_data_impl(const qpGeomVertexData *data) { CPT(qpGeomVertexData) new_data = data; @@ -81,20 +87,62 @@ munge_data_impl(const qpGeomVertexData *data) { _contents); } - return qpGeomMunger::munge_data_impl(new_data); + qpGeomVertexAnimationSpec animation = data->get_format()->get_animation(); + if (hardware_animated_vertices && + animation.get_animation_type() == qpGeomVertexAnimationSpec::AT_panda && + data->get_slider_table() == (SliderTable *)NULL) { + // Maybe we can animate the vertices with hardware. + const TransformBlendPalette *palette = data->get_transform_blend_palette(); + if (palette != (TransformBlendPalette *)NULL && + palette->get_max_simultaneous_transforms() <= + _gsg->get_max_vertex_transforms()) { + if (palette->get_num_transforms() <= + _gsg->get_max_vertex_transform_indices()) { + + if (palette->get_num_transforms() == palette->get_max_simultaneous_transforms()) { + // We can support an indexed palette, but since that won't + // save us any per-vertex blends, go ahead and do a plain + // old nonindexed palette instead. + animation.set_hardware(palette->get_num_transforms(), false); + + } else { + // We can support an indexed palette, and that means we can + // reduce the number of blends we have to specify for each + // vertex. + animation.set_hardware(palette->get_max_simultaneous_transforms(), true); + } + + } else if (palette->get_num_transforms() <= + _gsg->get_max_vertex_transforms()) { + // We can't support an indexed palette, but we have few enough + // transforms that we can do a nonindexed palette. + animation.set_hardware(palette->get_num_transforms(), false); + } + } + } + + CPT(qpGeomVertexFormat) orig_format = data->get_format(); + CPT(qpGeomVertexFormat) new_format = munge_format(orig_format, animation); + + if (new_format == orig_format) { + // Trivial case. + return data; + } + + return data->convert_to(new_format); } //////////////////////////////////////////////////////////////////// -// Function: ColorMunger::compare_to_impl +// Function: StandardMunger::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 ColorMunger:: +int StandardMunger:: compare_to_impl(const qpGeomMunger *other) const { - const ColorMunger *om = DCAST(ColorMunger, other); + const StandardMunger *om = DCAST(StandardMunger, other); if (_color != om->_color) { return _color < om->_color ? -1 : 1; } @@ -106,16 +154,16 @@ compare_to_impl(const qpGeomMunger *other) const { } //////////////////////////////////////////////////////////////////// -// Function: ColorMunger::geom_compare_to_impl +// Function: StandardMunger::geom_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 ColorMunger:: +int StandardMunger:: geom_compare_to_impl(const qpGeomMunger *other) const { - const ColorMunger *om = DCAST(ColorMunger, other); + const StandardMunger *om = DCAST(StandardMunger, other); if (_color != om->_color) { return _color < om->_color ? -1 : 1; } diff --git a/panda/src/display/colorMunger.h b/panda/src/display/standardMunger.h similarity index 67% rename from panda/src/display/colorMunger.h rename to panda/src/display/standardMunger.h index 0639d4ffc8..cba58fce42 100644 --- a/panda/src/display/colorMunger.h +++ b/panda/src/display/standardMunger.h @@ -1,4 +1,4 @@ -// Filename: colorMunger.h +// Filename: standardMunger.h // Created by: drose (21Mar05) // //////////////////////////////////////////////////////////////////// @@ -16,29 +16,32 @@ // //////////////////////////////////////////////////////////////////// -#ifndef COLORMUNGER_H -#define COLORMUNGER_H +#ifndef STANDARDMUNGER_H +#define STANDARDMUNGER_H #include "pandabase.h" #include "qpgeomMunger.h" +#include "graphicsStateGuardian.h" #include "colorAttrib.h" #include "colorScaleAttrib.h" #include "pointerTo.h" //////////////////////////////////////////////////////////////////// -// Class : ColorMunger -// Description : Applies ColorAttrib and ColorScaleAttrib by munging -// the vertex data. +// Class : StandardMunger +// Description : Performs some generic munging that is appropriate for +// all GSG types; for instance, applies ColorAttrib and +// ColorScaleAttrib to the vertices, and checks for +// hardware-accelerated animation capabilities. // // This is part of the experimental Geom rewrite. //////////////////////////////////////////////////////////////////// -class EXPCL_PANDA ColorMunger : public qpGeomMunger { +class EXPCL_PANDA StandardMunger : public qpGeomMunger { public: - ColorMunger(const GraphicsStateGuardianBase *gsg, const RenderState *state, - int num_components, - qpGeomVertexDataType::NumericType numeric_type, - qpGeomVertexDataType::Contents contents); - virtual ~ColorMunger(); + StandardMunger(const GraphicsStateGuardianBase *gsg, const RenderState *state, + int num_components, + qpGeomVertexDataType::NumericType numeric_type, + qpGeomVertexDataType::Contents contents); + virtual ~StandardMunger(); protected: virtual CPT(qpGeomVertexData) munge_data_impl(const qpGeomVertexData *data); @@ -49,6 +52,7 @@ private: int _num_components; qpGeomVertexDataType::NumericType _numeric_type; qpGeomVertexDataType::Contents _contents; + CPT(GraphicsStateGuardian) _gsg; CPT(ColorAttrib) _color; CPT(ColorScaleAttrib) _color_scale; @@ -58,7 +62,7 @@ public: } static void init_type() { qpGeomMunger::init_type(); - register_type(_type_handle, "ColorMunger", + register_type(_type_handle, "StandardMunger", qpGeomMunger::get_class_type()); } virtual TypeHandle get_type() const { @@ -70,7 +74,7 @@ private: static TypeHandle _type_handle; }; -#include "colorMunger.I" +#include "standardMunger.I" #endif diff --git a/panda/src/dxgsg8/dxGeomMunger8.I b/panda/src/dxgsg8/dxGeomMunger8.I index ab8300638d..60d9eb17e6 100644 --- a/panda/src/dxgsg8/dxGeomMunger8.I +++ b/panda/src/dxgsg8/dxGeomMunger8.I @@ -24,7 +24,7 @@ //////////////////////////////////////////////////////////////////// INLINE DXGeomMunger8:: DXGeomMunger8(GraphicsStateGuardian *gsg, const RenderState *state) : - ColorMunger(gsg, state, 1, qpGeomVertexDataType::NT_packed_8888, + StandardMunger(gsg, state, 1, qpGeomVertexDataType::NT_packed_8888, qpGeomVertexDataType::C_argb) { } @@ -38,5 +38,5 @@ DXGeomMunger8(GraphicsStateGuardian *gsg, const RenderState *state) : //////////////////////////////////////////////////////////////////// INLINE void *DXGeomMunger8:: operator new(size_t size) { - return do_operator_new(size, _deleted_chain); + return do_operator_new(size, &_deleted_chain); } diff --git a/panda/src/dxgsg8/dxGeomMunger8.cxx b/panda/src/dxgsg8/dxGeomMunger8.cxx index b5af94f843..6a0a3b1239 100644 --- a/panda/src/dxgsg8/dxGeomMunger8.cxx +++ b/panda/src/dxgsg8/dxGeomMunger8.cxx @@ -17,6 +17,9 @@ //////////////////////////////////////////////////////////////////// #include "dxGeomMunger8.h" +#include "qpgeomVertexReader.h" +#include "qpgeomVertexWriter.h" +#include "config_dxgsg8.h" qpGeomMunger *DXGeomMunger8::_deleted_chain = NULL; TypeHandle DXGeomMunger8::_type_handle; @@ -28,11 +31,19 @@ TypeHandle DXGeomMunger8::_type_handle; // necessary to the appropriate format for rendering. //////////////////////////////////////////////////////////////////// CPT(qpGeomVertexFormat) DXGeomMunger8:: -munge_format_impl(const qpGeomVertexFormat *orig) { +munge_format_impl(const qpGeomVertexFormat *orig, + const qpGeomVertexAnimationSpec &animation) { + if (dxgsg8_cat.is_debug()) { + if (animation.get_animation_type() != qpGeomVertexAnimationSpec::AT_none) { + dxgsg8_cat.debug() + << "preparing animation type " << animation << "\n"; + } + } // We have to build a completely new format that includes only the // appropriate components, in the appropriate order, in just one // array. PT(qpGeomVertexFormat) new_format = new qpGeomVertexFormat(*orig); + new_format->set_animation(animation); PT(qpGeomVertexArrayFormat) new_array_format = new qpGeomVertexArrayFormat; const qpGeomVertexDataType *vertex_type = @@ -54,6 +65,23 @@ munge_format_impl(const qpGeomVertexFormat *orig) { return orig; } + if (animation.get_animation_type() == qpGeomVertexAnimationSpec::AT_hardware && + animation.get_num_transforms() > 0) { + // If we want hardware animation, we need to reserve space for the + // blend weights. + new_array_format->add_data_type + (InternalName::get_transform_weight(), animation.get_num_transforms() - 1, + qpGeomVertexDataType::NT_float32, qpGeomVertexDataType::C_other); + + if (animation.get_indexed_transforms()) { + // Also, if we'll be indexing into the transfom palette, reserve + // space for the index. + new_array_format->add_data_type + (InternalName::get_transform_index(), 1, + qpGeomVertexDataType::NT_packed_8888, qpGeomVertexDataType::C_index); + } + } + if (normal_type != (const qpGeomVertexDataType *)NULL) { new_array_format->add_data_type (InternalName::get_normal(), 3, qpGeomVertexDataType::NT_float32, @@ -89,27 +117,3 @@ munge_format_impl(const qpGeomVertexFormat *orig) { new_format->insert_array(0, 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 ColorMunger::compare_to_impl(other); -} diff --git a/panda/src/dxgsg8/dxGeomMunger8.h b/panda/src/dxgsg8/dxGeomMunger8.h index d1cee0c7de..c1ccef30bd 100644 --- a/panda/src/dxgsg8/dxGeomMunger8.h +++ b/panda/src/dxgsg8/dxGeomMunger8.h @@ -20,7 +20,7 @@ #define DXGEOMMUNGER8_H #include "pandabase.h" -#include "colorMunger.h" +#include "standardMunger.h" #include "graphicsStateGuardian.h" //////////////////////////////////////////////////////////////////// @@ -31,14 +31,13 @@ // and that all relevant components are packed into a // single array, in the correct order. //////////////////////////////////////////////////////////////////// -class EXPCL_PANDADX DXGeomMunger8 : public ColorMunger { +class EXPCL_PANDADX DXGeomMunger8 : public StandardMunger { public: 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; + virtual CPT(qpGeomVertexFormat) munge_format_impl(const qpGeomVertexFormat *orig, + const qpGeomVertexAnimationSpec &animation); public: INLINE void *operator new(size_t size); @@ -51,9 +50,9 @@ public: return _type_handle; } static void init_type() { - ColorMunger::init_type(); + StandardMunger::init_type(); register_type(_type_handle, "DXGeomMunger8", - ColorMunger::get_class_type()); + StandardMunger::get_class_type()); } virtual TypeHandle get_type() const { return get_class_type(); diff --git a/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx b/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx index 95d66bca16..734aa5fd77 100644 --- a/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx +++ b/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx @@ -196,6 +196,7 @@ DXGraphicsStateGuardian8(const FrameBufferProperties &properties) : _pD3DDevice = NULL; _bDXisReady = false; + _transform_stale = false; _overlay_windows_supported = false; _pFvfBufBasePtr = NULL; @@ -287,10 +288,16 @@ free_d3d_device(void) { //////////////////////////////////////////////////////////////////// void DXGraphicsStateGuardian8:: free_nondx_resources() { - // this must not release any objects associated with D3D/DX! - // those should be released in free_dxgsg_objects instead + // this must not release any objects associated with D3D/DX! + // those should be released in free_dxgsg_objects instead + if (_index_buf != NULL) { SAFE_DELETE_ARRAY(_index_buf); + _index_buf = NULL; + } + if (_pFvfBufBasePtr != NULL) { SAFE_DELETE_ARRAY(_pFvfBufBasePtr); + _pFvfBufBasePtr = NULL; + } } //////////////////////////////////////////////////////////////////// @@ -310,6 +317,34 @@ dx_init(void) { assert(_pScrn->pD3D8!=NULL); assert(_pD3DDevice!=NULL); + D3DCAPS8 d3dCaps; + _pD3DDevice->GetDeviceCaps(&d3dCaps); + + if (dxgsg8_cat.is_debug()) { + dxgsg8_cat.debug() + << "\nHwTransformAndLight = " << ((d3dCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) != 0) + << "\nMaxTextureWidth = " << d3dCaps.MaxTextureWidth + << "\nMaxTextureHeight = " << d3dCaps.MaxTextureHeight + << "\nMaxVolumeExtent = " << d3dCaps.MaxVolumeExtent + << "\nMaxTextureAspectRatio = " << d3dCaps.MaxTextureAspectRatio + << "\nTexCoordCount = " << (d3dCaps.FVFCaps & D3DFVFCAPS_TEXCOORDCOUNTMASK) + << "\nMaxTextureBlendStages = " << d3dCaps.MaxTextureBlendStages + << "\nMaxSimultaneousTextures = " << d3dCaps.MaxSimultaneousTextures + << "\nMaxActiveLights = " << d3dCaps.MaxActiveLights + << "\nMaxUserClipPlanes = " << d3dCaps.MaxUserClipPlanes + << "\nMaxVertexBlendMatrices = " << d3dCaps.MaxVertexBlendMatrices + << "\nMaxVertexBlendMatrixIndex = " << d3dCaps.MaxVertexBlendMatrixIndex + << "\nMaxPointSize = " << d3dCaps.MaxPointSize + << "\nMaxPrimitiveCount = " << d3dCaps.MaxPrimitiveCount + << "\nMaxVertexIndex = " << d3dCaps.MaxVertexIndex + << "\nMaxStreams = " << d3dCaps.MaxStreams + << "\nMaxStreamStride = " << d3dCaps.MaxStreamStride + << "\n"; + } + + _max_vertex_transforms = d3dCaps.MaxVertexBlendMatrices; + _max_vertex_transform_indices = d3dCaps.MaxVertexBlendMatrixIndex; + ZeroMemory(&_lmodel_ambient,sizeof(Colorf)); _pD3DDevice->SetRenderState(D3DRS_AMBIENT, 0x0); @@ -2616,14 +2651,64 @@ begin_draw_primitives(const qpGeom *geom, const qpGeomMunger *munger, const qpGeomVertexFormat *format = _vertex_data->get_format(); - // The munger should have put the "vertex" data at the beginning of - // the first array. + // The munger should have put the FVF data in the first array. const qpGeomVertexArrayData *data = _vertex_data->get_array(0); VertexBufferContext *vbc = ((qpGeomVertexArrayData *)data)->prepare_now(get_prepared_objects(), this); nassertr(vbc != (VertexBufferContext *)NULL, false); apply_vertex_buffer(vbc); + const qpGeomVertexAnimationSpec &animation = + vertex_data->get_format()->get_animation(); + if (animation.get_animation_type() == qpGeomVertexAnimationSpec::AT_hardware) { + // Set up vertex blending. + switch (animation.get_num_transforms()) { + case 1: + _pD3DDevice->SetRenderState(D3DRS_VERTEXBLEND, D3DVBF_0WEIGHTS); + break; + case 2: + _pD3DDevice->SetRenderState(D3DRS_VERTEXBLEND, D3DVBF_1WEIGHTS); + break; + case 3: + _pD3DDevice->SetRenderState(D3DRS_VERTEXBLEND, D3DVBF_2WEIGHTS); + break; + case 4: + _pD3DDevice->SetRenderState(D3DRS_VERTEXBLEND, D3DVBF_3WEIGHTS); + break; + } + + if (animation.get_indexed_transforms()) { + // Set up indexed vertex blending. + _pD3DDevice->SetRenderState(D3DRS_INDEXEDVERTEXBLENDENABLE, TRUE); + } else { + _pD3DDevice->SetRenderState(D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE); + } + + const TransformPalette *palette = vertex_data->get_transform_palette(); + if (palette != (TransformPalette *)NULL) { + for (int i = 0; i < palette->get_num_transforms(); i++) { + LMatrix4f mat; + palette->get_transform(i)->mult_matrix(mat, _transform->get_mat()); + const D3DMATRIX *d3d_mat = (const D3DMATRIX *)mat.get_data(); + _pD3DDevice->SetTransform(D3DTS_WORLDMATRIX(i), d3d_mat); + } + + // Setting the palette transforms steps on the world matrix, so + // we have to set a flag to reload the world matrix later. + _transform_stale = true; + } + + } else { + // We're not using vertex blending. + _pD3DDevice->SetRenderState(D3DRS_VERTEXBLEND, D3DVBF_DISABLE); + + if (_transform_stale) { + const D3DMATRIX *d3d_mat = (const D3DMATRIX *)_transform->get_mat().get_data(); + _pD3DDevice->SetTransform(D3DTS_WORLD, d3d_mat); + } + } + + return true; } @@ -2938,16 +3023,18 @@ release_texture(TextureContext *tc) { //////////////////////////////////////////////////////////////////// VertexBufferContext *DXGraphicsStateGuardian8:: prepare_vertex_buffer(qpGeomVertexArrayData *data) { - if (dxgsg8_cat.is_debug()) { - dxgsg8_cat.debug() - << "prepare_vertex_buffer(" << (void *)data << ")\n"; - } - DXVertexBufferContext8 *dvbc = new DXVertexBufferContext8(data); if (vertex_buffers) { dvbc->create_vbuffer(*_pScrn); dvbc->mark_loaded(); + + if (dxgsg8_cat.is_debug()) { + dxgsg8_cat.debug() + << "creating vertex buffer " << dvbc->_vbuffer << ": " + << data->get_num_vertices() << " vertices " + << *data->get_array_format() << "\n"; + } } return dvbc; @@ -2967,10 +3054,6 @@ apply_vertex_buffer(VertexBufferContext *vbc) { add_to_vertex_buffer_record(dvbc); if (dvbc->was_modified()) { - if (dxgsg8_cat.is_debug()) { - dxgsg8_cat.debug() - << "apply_vertex_buffer(" << (void *)vbc->get_data() << ")\n"; - } if (dvbc->changed_size()) { // Here we have to destroy the old vertex buffer and create a // new one. @@ -3005,11 +3088,6 @@ apply_vertex_buffer(VertexBufferContext *vbc) { //////////////////////////////////////////////////////////////////// void DXGraphicsStateGuardian8:: release_vertex_buffer(VertexBufferContext *vbc) { - if (dxgsg8_cat.is_debug()) { - dxgsg8_cat.debug() - << "release_vertex_buffer(" << (void *)vbc->get_data() << ")\n"; - } - DXVertexBufferContext8 *dvbc = DCAST(DXVertexBufferContext8, vbc); delete dvbc; } @@ -3029,16 +3107,16 @@ release_vertex_buffer(VertexBufferContext *vbc) { //////////////////////////////////////////////////////////////////// IndexBufferContext *DXGraphicsStateGuardian8:: prepare_index_buffer(qpGeomPrimitive *data) { - if (dxgsg8_cat.is_debug()) { - dxgsg8_cat.debug() - << "prepare_index_buffer(" << (void *)data << ")\n"; - } - DXIndexBufferContext8 *dibc = new DXIndexBufferContext8(data); dibc->create_ibuffer(*_pScrn); dibc->mark_loaded(); + if (dxgsg8_cat.is_debug()) { + dxgsg8_cat.debug() + << "creating index buffer " << dibc->_ibuffer << "\n"; + } + return dibc; } @@ -3056,10 +3134,6 @@ apply_index_buffer(IndexBufferContext *ibc) { add_to_index_buffer_record(dibc); if (dibc->was_modified()) { - if (dxgsg8_cat.is_debug()) { - dxgsg8_cat.debug() - << "apply_index_buffer(" << (void *)ibc->get_data() << ")\n"; - } if (dibc->changed_size()) { // Here we have to destroy the old index buffer and create a // new one. @@ -3092,11 +3166,6 @@ apply_index_buffer(IndexBufferContext *ibc) { //////////////////////////////////////////////////////////////////// void DXGraphicsStateGuardian8:: release_index_buffer(IndexBufferContext *ibc) { - if (dxgsg8_cat.is_debug()) { - dxgsg8_cat.debug() - << "release_index_buffer(" << (void *)ibc->get_data() << ")\n"; - } - DXIndexBufferContext8 *dibc = DCAST(DXIndexBufferContext8, ibc); delete dibc; } @@ -3419,9 +3488,9 @@ void DXGraphicsStateGuardian8:: issue_transform(const TransformState *transform) { DO_PSTATS_STUFF(_transform_state_pcollector.add_level(1)); - // if we're using ONLY vertex shaders, could get avoid calling SetTrans - D3DMATRIX *pMat = (D3DMATRIX*)transform->get_mat().get_data(); - _pD3DDevice->SetTransform(D3DTS_WORLD,pMat); + const D3DMATRIX *d3d_mat = (const D3DMATRIX *)transform->get_mat().get_data(); + _pD3DDevice->SetTransform(D3DTS_WORLD, d3d_mat); + _transform_stale = false; _transform = transform; if (_auto_rescale_normal) { diff --git a/panda/src/dxgsg8/dxGraphicsStateGuardian8.h b/panda/src/dxgsg8/dxGraphicsStateGuardian8.h index b91c8ac17a..c812fba5cf 100644 --- a/panda/src/dxgsg8/dxGraphicsStateGuardian8.h +++ b/panda/src/dxgsg8/dxGraphicsStateGuardian8.h @@ -185,8 +185,7 @@ protected: HRESULT _last_testcooplevel_result; DXTextureContext8 *_pCurTexContext; - bool _bTransformIssued; // decaling needs to tell when a transform has been issued - D3DMATRIX _SavedTransform; + bool _transform_stale; RenderBuffer::Type _cur_read_pixel_buffer; // source for copy_pixel_buffer operation bool _auto_rescale_normal; diff --git a/panda/src/dxgsg8/dxIndexBufferContext8.cxx b/panda/src/dxgsg8/dxIndexBufferContext8.cxx index f3d896fa7f..c10ca78579 100644 --- a/panda/src/dxgsg8/dxIndexBufferContext8.cxx +++ b/panda/src/dxgsg8/dxIndexBufferContext8.cxx @@ -43,6 +43,11 @@ DXIndexBufferContext8(qpGeomPrimitive *data) : DXIndexBufferContext8:: ~DXIndexBufferContext8() { if (_ibuffer != NULL) { + if (dxgsg8_cat.is_debug()) { + dxgsg8_cat.debug() + << "deleting index buffer " << _ibuffer << "\n"; + } + RELEASE(_ibuffer, dxgsg8, "index buffer", RELEASE_ONCE); _ibuffer = NULL; } @@ -84,6 +89,12 @@ upload_data() { nassertv(_ibuffer != NULL); int data_size = get_data()->get_data_size_bytes(); + + if (dxgsg8_cat.is_debug()) { + dxgsg8_cat.debug() + << "copying " << data_size + << " bytes into index buffer " << _ibuffer << "\n"; + } BYTE *local_pointer; HRESULT hr = _ibuffer->Lock(0, data_size, &local_pointer, 0); diff --git a/panda/src/dxgsg8/dxVertexBufferContext8.cxx b/panda/src/dxgsg8/dxVertexBufferContext8.cxx index 495a1b2241..16f0943f2c 100644 --- a/panda/src/dxgsg8/dxVertexBufferContext8.cxx +++ b/panda/src/dxgsg8/dxVertexBufferContext8.cxx @@ -47,9 +47,52 @@ DXVertexBufferContext8(qpGeomVertexArrayData *data) : if (n < num_data_types && array_format->get_data_type(n)->get_name() == InternalName::get_vertex()) { - _fvf |= D3DFVF_XYZ; ++n; + + int num_blend_values = 0; + + if (n < num_data_types && + array_format->get_data_type(n)->get_name() == InternalName::get_transform_weight()) { + // We have hardware vertex animation. + num_blend_values = array_format->get_data_type(n)->get_num_values(); + ++n; + + if (n < num_data_types && + array_format->get_data_type(n)->get_name() == InternalName::get_transform_index()) { + // Furthermore, it's indexed vertex animation. + _fvf |= D3DFVF_LASTBETA_UBYTE4; + ++num_blend_values; + ++n; + } + } + + switch (num_blend_values) { + case 0: + _fvf |= D3DFVF_XYZ; + break; + + case 1: + _fvf |= D3DFVF_XYZB1; + break; + + case 2: + _fvf |= D3DFVF_XYZB2; + break; + + case 3: + _fvf |= D3DFVF_XYZB3; + break; + + case 4: + _fvf |= D3DFVF_XYZB4; + break; + + case 5: + _fvf |= D3DFVF_XYZB5; + break; + } } + if (n < num_data_types && array_format->get_data_type(n)->get_name() == InternalName::get_normal()) { _fvf |= D3DFVF_NORMAL; @@ -95,6 +138,11 @@ DXVertexBufferContext8(qpGeomVertexArrayData *data) : DXVertexBufferContext8:: ~DXVertexBufferContext8() { if (_vbuffer != NULL) { + if (dxgsg8_cat.is_debug()) { + dxgsg8_cat.debug() + << "deleting vertex buffer " << _vbuffer << "\n"; + } + RELEASE(_vbuffer, dxgsg8, "vertex buffer", RELEASE_ONCE); _vbuffer = NULL; } @@ -136,6 +184,12 @@ upload_data() { nassertv(_vbuffer != NULL); int data_size = get_data()->get_data_size_bytes(); + + if (dxgsg8_cat.is_debug()) { + dxgsg8_cat.debug() + << "copying " << data_size + << " bytes into vertex buffer " << _vbuffer << "\n"; + } BYTE *local_pointer; HRESULT hr = _vbuffer->Lock(0, data_size, &local_pointer, 0); diff --git a/panda/src/egg2pg/eggLoader.cxx b/panda/src/egg2pg/eggLoader.cxx index 87e4cf5663..e9addf9e02 100644 --- a/panda/src/egg2pg/eggLoader.cxx +++ b/panda/src/egg2pg/eggLoader.cxx @@ -1960,6 +1960,13 @@ make_vertex_data(const EggRenderState *render_state, // maybe a SliderTable, and additional columns in the vertex data: // one that indexes into the blend palette per vertex, and also // one for each different type of morph delta. + + // Tell the format that we're setting it up for Panda-based + // animation. + qpGeomVertexAnimationSpec animation; + animation.set_panda(); + temp_format->set_animation(animation); + blend_palette = new TransformBlendPalette; PT(qpGeomVertexArrayFormat) anim_array_format = new qpGeomVertexArrayFormat; diff --git a/panda/src/glstuff/glGeomMunger_src.I b/panda/src/glstuff/glGeomMunger_src.I index 139a074da5..8c59c93027 100644 --- a/panda/src/glstuff/glGeomMunger_src.I +++ b/panda/src/glstuff/glGeomMunger_src.I @@ -24,7 +24,7 @@ //////////////////////////////////////////////////////////////////// INLINE CLP(GeomMunger):: CLP(GeomMunger)(GraphicsStateGuardian *gsg, const RenderState *state) : - ColorMunger(gsg, state, 4, qpGeomVertexDataType::NT_uint8, + StandardMunger(gsg, state, 4, qpGeomVertexDataType::NT_uint8, qpGeomVertexDataType::C_rgba), _gsg(gsg), _texture(state->get_texture()), @@ -52,5 +52,5 @@ get_gsg() const { //////////////////////////////////////////////////////////////////// INLINE void *CLP(GeomMunger):: operator new(size_t size) { - return do_operator_new(size, _deleted_chain); + return do_operator_new(size, &_deleted_chain); } diff --git a/panda/src/glstuff/glGeomMunger_src.cxx b/panda/src/glstuff/glGeomMunger_src.cxx index b627832640..d4173c320e 100644 --- a/panda/src/glstuff/glGeomMunger_src.cxx +++ b/panda/src/glstuff/glGeomMunger_src.cxx @@ -44,33 +44,33 @@ CLP(GeomMunger):: // necessary to the appropriate format for rendering. //////////////////////////////////////////////////////////////////// CPT(qpGeomVertexFormat) CLP(GeomMunger):: -munge_format_impl(const qpGeomVertexFormat *orig) { - CPT(qpGeomVertexFormat) format = orig; +munge_format_impl(const qpGeomVertexFormat *orig, + const qpGeomVertexAnimationSpec &animation) { + PT(qpGeomVertexFormat) new_format = new qpGeomVertexFormat(*orig); + new_format->set_animation(animation); const qpGeomVertexDataType *color_type = - format->get_data_type(InternalName::get_color()); + orig->get_data_type(InternalName::get_color()); if (color_type != (qpGeomVertexDataType *)NULL && (color_type->get_numeric_type() == qpGeomVertexDataType::NT_packed_8888 || color_type->get_contents() != qpGeomVertexDataType::C_rgba)) { // We need to convert the color format; OpenGL doesn't support the // byte order of DirectX's packed ARGB format. - int color_array = format->get_array_with(InternalName::get_color()); + int color_array = orig->get_array_with(InternalName::get_color()); - PT(qpGeomVertexFormat) new_format = new qpGeomVertexFormat(*format); PT(qpGeomVertexArrayFormat) new_array_format = new_format->modify_array(color_array); // Replace the existing color format with the new format. new_array_format->add_data_type (InternalName::get_color(), 4, qpGeomVertexDataType::NT_uint8, qpGeomVertexDataType::C_rgba, color_type->get_start()); - - format = qpGeomVertexFormat::register_format(new_format); } /* if (true) { // Split out the interleaved array into n parallel arrays. - PT(qpGeomVertexFormat) new_format = new qpGeomVertexFormat; + CPT(qpGeomVertexFormat) format = new_format; + new_format = new qpGeomVertexFormat; for (int i = 0; i < format->get_num_data_types(); i++) { const qpGeomVertexDataType *data_type = format->get_data_type(i); PT(qpGeomVertexArrayFormat) new_array_format = new qpGeomVertexArrayFormat; @@ -78,11 +78,10 @@ munge_format_impl(const qpGeomVertexFormat *orig) { data_type->get_numeric_type()); new_format->add_array(new_array_format); } - format = qpGeomVertexFormat::register_format(new_format); } */ - return format; + return qpGeomVertexFormat::register_format(new_format); } //////////////////////////////////////////////////////////////////// @@ -103,7 +102,7 @@ compare_to_impl(const qpGeomMunger *other) const { return _tex_gen < om->_tex_gen ? -1 : 1; } - return ColorMunger::compare_to_impl(other); + return StandardMunger::compare_to_impl(other); } //////////////////////////////////////////////////////////////////// @@ -119,5 +118,5 @@ geom_compare_to_impl(const qpGeomMunger *other) const { // We don't consider _texture and _tex_gen for this purpose; they // affect only whether the GL display list should be regenerated or // not, and don't require reconverting the vertices. - return ColorMunger::geom_compare_to_impl(other); + return StandardMunger::geom_compare_to_impl(other); } diff --git a/panda/src/glstuff/glGeomMunger_src.h b/panda/src/glstuff/glGeomMunger_src.h index 60bc76c304..3073aacfa2 100644 --- a/panda/src/glstuff/glGeomMunger_src.h +++ b/panda/src/glstuff/glGeomMunger_src.h @@ -17,7 +17,7 @@ //////////////////////////////////////////////////////////////////// #include "pandabase.h" -#include "colorMunger.h" +#include "standardMunger.h" #include "graphicsStateGuardian.h" #include "textureAttrib.h" #include "texGenAttrib.h" @@ -31,7 +31,7 @@ class CLP(GeomContext); // for OpenGL rendering. In particular, it makes sure // colors aren't stored in DirectX's packed_argb format. //////////////////////////////////////////////////////////////////// -class EXPCL_GL CLP(GeomMunger) : public ColorMunger { +class EXPCL_GL CLP(GeomMunger) : public StandardMunger { public: INLINE CLP(GeomMunger)(GraphicsStateGuardian *gsg, const RenderState *state); virtual ~CLP(GeomMunger)(); @@ -39,7 +39,8 @@ public: INLINE GraphicsStateGuardian *get_gsg() const; protected: - virtual CPT(qpGeomVertexFormat) munge_format_impl(const qpGeomVertexFormat *orig); + virtual CPT(qpGeomVertexFormat) munge_format_impl(const qpGeomVertexFormat *orig, + const qpGeomVertexAnimationSpec &animation); virtual int compare_to_impl(const qpGeomMunger *other) const; virtual int geom_compare_to_impl(const qpGeomMunger *other) const; @@ -61,9 +62,9 @@ public: return _type_handle; } static void init_type() { - ColorMunger::init_type(); + StandardMunger::init_type(); register_type(_type_handle, CLASSPREFIX_QUOTED "GeomMunger", - ColorMunger::get_class_type()); + StandardMunger::get_class_type()); } virtual TypeHandle get_type() const { return get_class_type(); diff --git a/panda/src/gobj/Sources.pp b/panda/src/gobj/Sources.pp index ebbdf0b57a..1ac7e466c1 100644 --- a/panda/src/gobj/Sources.pp +++ b/panda/src/gobj/Sources.pp @@ -32,6 +32,7 @@ qpgeomVertexArrayFormat.h qpgeomVertexArrayFormat.I \ qpgeomCacheEntry.h qpgeomCacheEntry.I \ qpgeomCacheManager.h qpgeomCacheManager.I \ + qpgeomVertexAnimationSpec.h qpgeomVertexAnimationSpec.I \ qpgeomVertexData.h qpgeomVertexData.I \ qpgeomVertexDataType.h qpgeomVertexDataType.I \ qpgeomVertexFormat.h qpgeomVertexFormat.I \ @@ -82,6 +83,7 @@ qpgeomVertexArrayFormat.cxx \ qpgeomCacheEntry.cxx \ qpgeomCacheManager.cxx \ + qpgeomVertexAnimationSpec.cxx \ qpgeomVertexData.cxx \ qpgeomVertexDataType.cxx \ qpgeomVertexFormat.cxx \ @@ -131,6 +133,7 @@ qpgeomVertexArrayFormat.h qpgeomVertexArrayFormat.I \ qpgeomCacheEntry.h qpgeomCacheEntry.I \ qpgeomCacheManager.h qpgeomCacheManager.I \ + qpgeomVertexAnimationSpec.h qpgeomVertexAnimationSpec.I \ qpgeomVertexData.h qpgeomVertexData.I \ qpgeomVertexDataType.h qpgeomVertexDataType.I \ qpgeomVertexFormat.h qpgeomVertexFormat.I \ diff --git a/panda/src/gobj/config_gobj.cxx b/panda/src/gobj/config_gobj.cxx index 25876a4027..52ec9460ea 100644 --- a/panda/src/gobj/config_gobj.cxx +++ b/panda/src/gobj/config_gobj.cxx @@ -113,9 +113,21 @@ ConfigVariableBool display_lists "rendering static geometry. On some systems, this can result " "in a performance improvement over vertex buffers alone; on " "other systems (particularly low-end systems) it makes little to " - "no difference. This has no effect on DirectX rendering. If " - "vertex-buffers is also enabled, then OpenGL buffer objects " - "will also be created for dynamic geometry.")); + "no difference. On some systems, using display lists can actually " + "reduce performance. This has no effect on DirectX rendering or " + "on dynamic geometry (e.g. soft-skinned animation).")); + +ConfigVariableBool hardware_animated_vertices +("hardware-animated-vertices", false, + PRC_DESC("Set this true to allow the transforming of soft-skinned " + "animated vertices via hardware, if supported, or false always " + "to perform the vertex animation via software within Panda. " + "If you have a card that supports this, and your scene does " + "not contain too many vertices already, this can provide a " + "performance boost by offloading some work from your CPU onto " + "your graphics card. It may also help by reducing the bandwidth " + "necessary on your computer's bus. However, in some cases it " + "may actually reduce performance.")); ConfigVariableBool use_qpgeom ("use-qpgeom", false, diff --git a/panda/src/gobj/config_gobj.h b/panda/src/gobj/config_gobj.h index 8c3b1edc63..b581d79bad 100644 --- a/panda/src/gobj/config_gobj.h +++ b/panda/src/gobj/config_gobj.h @@ -53,6 +53,7 @@ extern EXPCL_PANDA ConfigVariableBool keep_geom_ram; extern EXPCL_PANDA ConfigVariableBool retained_mode; extern EXPCL_PANDA ConfigVariableBool vertex_buffers; extern EXPCL_PANDA ConfigVariableBool display_lists; +extern EXPCL_PANDA ConfigVariableBool hardware_animated_vertices; extern EXPCL_PANDA ConfigVariableBool use_qpgeom; diff --git a/panda/src/gobj/gobj_composite1.cxx b/panda/src/gobj/gobj_composite1.cxx index f33fa04dbc..07a403a699 100644 --- a/panda/src/gobj/gobj_composite1.cxx +++ b/panda/src/gobj/gobj_composite1.cxx @@ -24,6 +24,7 @@ #include "qpgeomVertexArrayFormat.cxx" #include "qpgeomCacheEntry.cxx" #include "qpgeomCacheManager.cxx" +#include "qpgeomVertexAnimationSpec.cxx" #include "qpgeomVertexData.cxx" #include "qpgeomVertexDataType.cxx" #include "qpgeomVertexFormat.cxx" diff --git a/panda/src/gobj/internalName.I b/panda/src/gobj/internalName.I index b84cdca1ea..36bea1f7ff 100644 --- a/panda/src/gobj/internalName.I +++ b/panda/src/gobj/internalName.I @@ -191,6 +191,32 @@ get_transform_blend() { return _transform_blend; } +//////////////////////////////////////////////////////////////////// +// Function: InternalName::get_transform_weight +// Access: Published, Static +// Description: Returns the standard InternalName "transform_weight". +//////////////////////////////////////////////////////////////////// +INLINE PT(InternalName) InternalName:: +get_transform_weight() { + if (_transform_weight == (InternalName *)NULL) { + _transform_weight = InternalName::make("transform_weight"); + } + return _transform_weight; +} + +//////////////////////////////////////////////////////////////////// +// Function: InternalName::get_transform_index +// Access: Published, Static +// Description: Returns the standard InternalName "transform_index". +//////////////////////////////////////////////////////////////////// +INLINE PT(InternalName) InternalName:: +get_transform_index() { + if (_transform_index == (InternalName *)NULL) { + _transform_index = InternalName::make("transform_index"); + } + return _transform_index; +} + //////////////////////////////////////////////////////////////////// // Function: InternalName::get_morph // Access: Published, Static diff --git a/panda/src/gobj/internalName.cxx b/panda/src/gobj/internalName.cxx index d907483bea..0a84853bdd 100644 --- a/panda/src/gobj/internalName.cxx +++ b/panda/src/gobj/internalName.cxx @@ -33,6 +33,8 @@ PT(InternalName) InternalName::_binormal; PT(InternalName) InternalName::_texcoord; PT(InternalName) InternalName::_color; PT(InternalName) InternalName::_transform_blend; +PT(InternalName) InternalName::_transform_weight; +PT(InternalName) InternalName::_transform_index; TypeHandle InternalName::_type_handle; TypeHandle InternalName::_texcoord_type_handle; diff --git a/panda/src/gobj/internalName.h b/panda/src/gobj/internalName.h index 31539a1345..2ee49b0664 100644 --- a/panda/src/gobj/internalName.h +++ b/panda/src/gobj/internalName.h @@ -72,6 +72,8 @@ PUBLISHED: INLINE static PT(InternalName) get_texcoord_name(const string &name); INLINE static PT(InternalName) get_color(); INLINE static PT(InternalName) get_transform_blend(); + INLINE static PT(InternalName) get_transform_weight(); + INLINE static PT(InternalName) get_transform_index(); INLINE static PT(InternalName) get_morph(InternalName *data_type, const string &slider); private: @@ -91,6 +93,8 @@ private: static PT(InternalName) _texcoord; static PT(InternalName) _color; static PT(InternalName) _transform_blend; + static PT(InternalName) _transform_weight; + static PT(InternalName) _transform_index; public: // Datagram stuff diff --git a/panda/src/gobj/qpgeomMunger.I b/panda/src/gobj/qpgeomMunger.I index 70b27f635d..e4fc4d8b5f 100644 --- a/panda/src/gobj/qpgeomMunger.I +++ b/panda/src/gobj/qpgeomMunger.I @@ -19,7 +19,7 @@ //////////////////////////////////////////////////////////////////// // Function: qpGeomMunger::is_registered -// Access: Published +// Access: Public // Description: Returns true if this munger has been registered, // false if it has not. It may not be used for a Geom // until it has been registered, but once registered, it @@ -32,7 +32,7 @@ is_registered() const { //////////////////////////////////////////////////////////////////// // Function: qpGeomMunger::register_munger -// Access: Published, Static +// Access: Public, Static // Description: Adds the indicated munger to the registry, if there // is not an equivalent munger already there; in either // case, returns the pointer to the equivalent munger @@ -100,13 +100,19 @@ geom_compare_to(const qpGeomMunger &other) const { // Access: Public // Description: Given a source GeomVertexFormat, converts it if // necessary to the appropriate format for rendering. +// +// If the GeomVertexAnimationSpec so indicates, then the +// format will be chosen to convert CPU-based animation +// tables to HW-based animation tables, reserving space +// for the specified number of transforms per vertex. //////////////////////////////////////////////////////////////////// INLINE CPT(qpGeomVertexFormat) qpGeomMunger:: -munge_format(const qpGeomVertexFormat *format) const { +munge_format(const qpGeomVertexFormat *format, + const qpGeomVertexAnimationSpec &animation) const { // We cast away the const pointer, because do_munge_format() needs // to update caches and stuff, but we trust it not to change any // user-definable parameters. - return ((qpGeomMunger *)this)->do_munge_format(format); + return ((qpGeomMunger *)this)->do_munge_format(format, animation); } //////////////////////////////////////////////////////////////////// @@ -123,6 +129,22 @@ munge_data(const qpGeomVertexData *data) const { return ((qpGeomMunger *)this)->munge_data_impl(data); } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomMunger::operator new +// Access: Public +// Description: This implementation of operator new isn't really +// supposed to be called, but it's provided in case +// someone screws up and doesn't define a more +// appropriate operator new (that passes up a valid +// _deleted_chain) for a derived class. Having this +// operator new that passes NULL for _deleted_chain will +// at least allow us to detect the error. +//////////////////////////////////////////////////////////////////// +INLINE void *qpGeomMunger:: +operator new(size_t size) { + return do_operator_new(size, NULL); +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomMunger::operator delete // Access: Public @@ -133,7 +155,14 @@ munge_data(const qpGeomVertexData *data) const { INLINE void qpGeomMunger:: operator delete(void *ptr) { qpGeomMunger *obj = (qpGeomMunger *)ptr; - nassertv(obj->_deleted_chain != (qpGeomMunger **)NULL); + +#ifndef NDEBUG + nassertd(obj->_deleted_chain != (qpGeomMunger **)NULL) { + delete obj; + return; + } +#endif + nassertv(obj->_next == (qpGeomMunger *)NULL); obj->_next = (*obj->_deleted_chain); (*obj->_deleted_chain) = obj; @@ -153,15 +182,24 @@ operator delete(void *ptr) { // there when it is deleted. //////////////////////////////////////////////////////////////////// INLINE void *qpGeomMunger:: -do_operator_new(size_t size, qpGeomMunger *&deleted_chain) { +do_operator_new(size_t size, qpGeomMunger **deleted_chain) { qpGeomMunger *obj; - if (deleted_chain != (qpGeomMunger *)NULL) { - obj = deleted_chain; - deleted_chain = deleted_chain->_next; + +#ifndef NDEBUG + nassertd(deleted_chain != (qpGeomMunger **)NULL) { + obj = (qpGeomMunger *)::operator new(size); + obj->_deleted_chain = NULL; + return obj; + } +#endif + + if ((*deleted_chain) != (qpGeomMunger *)NULL) { + obj = (*deleted_chain); + (*deleted_chain) = (*deleted_chain)->_next; } else { obj = (qpGeomMunger *)::operator new(size); } - obj->_deleted_chain = &deleted_chain; + obj->_deleted_chain = deleted_chain; return obj; } diff --git a/panda/src/gobj/qpgeomMunger.cxx b/panda/src/gobj/qpgeomMunger.cxx index 908f983528..5e171df0e1 100644 --- a/panda/src/gobj/qpgeomMunger.cxx +++ b/panda/src/gobj/qpgeomMunger.cxx @@ -76,36 +76,7 @@ qpGeomMunger:: if (is_registered()) { get_registry()->unregister_munger(this); } - nassertv(_formats.empty()); -} - -//////////////////////////////////////////////////////////////////// -// Function: qpGeomMunger::remove_format -// Access: Public -// Description: Removes a prepared GeomVertexFormat from the cache. -//////////////////////////////////////////////////////////////////// -void qpGeomMunger:: -remove_format(const qpGeomVertexFormat *format) { - // If this assertion is triggered, maybe we accidentally deleted a - // GeomVertexFormat while we were in the process of unregistering, - // causing a recursive re-entry. - nassertv(_is_registered); - - Formats::iterator fi; - fi = _formats.find(format); - nassertv(fi != _formats.end()); - - // We can't save this in a CPT, because it might be the same pointer - // as format, which might be in the middle of its destructor. - // Putting it in a CPT would bump up its reference count again, and - // end up calling the destructor twice. - const qpGeomVertexFormat *derived_format = (*fi).second; - _formats.erase(fi); - - // We need to unref the derived format, if we reffed it earlier. - if (derived_format != format) { - unref_delete(derived_format); - } + nassertv(_formats_by_animation.empty()); } //////////////////////////////////////////////////////////////////// @@ -128,43 +99,29 @@ remove_data(const qpGeomVertexData *data) { // exists just to cast away the const pointer. //////////////////////////////////////////////////////////////////// CPT(qpGeomVertexFormat) qpGeomMunger:: -do_munge_format(const qpGeomVertexFormat *format) { +do_munge_format(const qpGeomVertexFormat *format, + const qpGeomVertexAnimationSpec &animation) { nassertr(_is_registered, NULL); nassertr(format->is_registered(), NULL); + Formats &formats = _formats_by_animation[animation]; + Formats::iterator fi; - fi = _formats.find(format); - if (fi != _formats.end()) { + fi = formats.find(format); + if (fi != formats.end()) { // This format was previously munged, so the answer will be the // same. return (*fi).second; } // We have to munge this format for the first time. - CPT(qpGeomVertexFormat) derived_format = munge_format_impl(format); + CPT(qpGeomVertexFormat) derived_format = munge_format_impl(format, animation); nassertr(derived_format->is_registered(), NULL); // Store the answer in the map, so we can quickly get it next time. - bool inserted = _formats.insert(Formats::value_type(format, derived_format)).second; + bool inserted = formats.insert(Formats::value_type(format, derived_format)).second; nassertr(inserted, NULL); - // Tell the source format that we have its pointer. - { - qpGeomVertexFormat *f = (qpGeomVertexFormat *)format; - MutexHolder holder(f->_cache_lock); - nassertr(f->is_registered(), NULL); - inserted = f->_mungers.insert(this).second; - nassertr(inserted, NULL); - } - - // We also want to keep a reference count on the derived format, but - // only if it is actually a different pointer from the original - // format--otherwise it would cause a self-referential reference - // count. - if (derived_format != format) { - derived_format->ref(); - } - return derived_format; } @@ -175,7 +132,7 @@ do_munge_format(const qpGeomVertexFormat *format) { // necessary to the appropriate format for rendering. //////////////////////////////////////////////////////////////////// CPT(qpGeomVertexFormat) qpGeomMunger:: -munge_format_impl(const qpGeomVertexFormat *orig) { +munge_format_impl(const qpGeomVertexFormat *orig, const qpGeomVertexAnimationSpec &) { return orig; } @@ -190,7 +147,8 @@ munge_data_impl(const qpGeomVertexData *data) { nassertr(_is_registered, NULL); CPT(qpGeomVertexFormat) orig_format = data->get_format(); - CPT(qpGeomVertexFormat) new_format = munge_format(orig_format); + CPT(qpGeomVertexFormat) new_format = + munge_format(orig_format, orig_format->get_animation()); if (new_format == orig_format) { // Trivial case. @@ -208,7 +166,7 @@ munge_data_impl(const qpGeomVertexData *data) { void qpGeomMunger:: munge_geom_impl(CPT(qpGeom) &, CPT(qpGeomVertexData) &) { // The default implementation does nothing (the work has already - // been done in munge_format_impl). + // been done in munge_format_impl() and munge_data_impl()). } //////////////////////////////////////////////////////////////////// @@ -261,7 +219,7 @@ do_register() { << "GeomMunger::do_register(): " << (void *)this << "\n"; } nassertv(!_is_registered); - nassertv(_formats.empty()); + nassertv(_formats_by_animation.empty()); // Tell the cache manager to hang on to this new GeomMunger, so we // don't waste our time re-registering the same GeomMunger over and @@ -288,22 +246,7 @@ do_unregister() { _is_registered = false; // Unregistering means we should blow away the cache. - Formats::iterator fi; - for (fi = _formats.begin(); fi != _formats.end(); ++fi) { - qpGeomVertexFormat *format = (qpGeomVertexFormat *)(*fi).first; - CPT(qpGeomVertexFormat) derived_format = (*fi).second; - - { - MutexHolder holder(format->_cache_lock); - size_t num_erased = format->_mungers.erase(this); - nassertv(num_erased == 1); - } - - if (derived_format != format) { - derived_format->unref(); - } - } - _formats.clear(); + _formats_by_animation.clear(); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/gobj/qpgeomMunger.h b/panda/src/gobj/qpgeomMunger.h index d240774289..e72bf66f0b 100644 --- a/panda/src/gobj/qpgeomMunger.h +++ b/panda/src/gobj/qpgeomMunger.h @@ -21,6 +21,7 @@ #include "pandabase.h" #include "typedReferenceCount.h" +#include "qpgeomVertexAnimationSpec.h" #include "qpgeomVertexFormat.h" #include "qpgeomVertexData.h" #include "qpgeomCacheEntry.h" @@ -67,8 +68,8 @@ public: INLINE bool is_registered() const; INLINE static CPT(qpGeomMunger) register_munger(qpGeomMunger *munger); - INLINE CPT(qpGeomVertexFormat) munge_format(const qpGeomVertexFormat *format) const; - void remove_format(const qpGeomVertexFormat *format); + INLINE CPT(qpGeomVertexFormat) munge_format(const qpGeomVertexFormat *format, + const qpGeomVertexAnimationSpec &animation) const; INLINE CPT(qpGeomVertexData) munge_data(const qpGeomVertexData *data) const; void remove_data(const qpGeomVertexData *data); @@ -80,9 +81,11 @@ public: INLINE int geom_compare_to(const qpGeomMunger &other) const; protected: - CPT(qpGeomVertexFormat) do_munge_format(const qpGeomVertexFormat *format); + CPT(qpGeomVertexFormat) do_munge_format(const qpGeomVertexFormat *format, + const qpGeomVertexAnimationSpec &animation); - virtual CPT(qpGeomVertexFormat) munge_format_impl(const qpGeomVertexFormat *orig); + virtual CPT(qpGeomVertexFormat) munge_format_impl(const qpGeomVertexFormat *orig, + const qpGeomVertexAnimationSpec &animation); virtual CPT(qpGeomVertexData) munge_data_impl(const qpGeomVertexData *data); virtual void munge_geom_impl(CPT(qpGeom) &geom, CPT(qpGeomVertexData) &data); virtual int compare_to_impl(const qpGeomMunger *other) const; @@ -93,10 +96,11 @@ public: // and delete using their own deleted_chain. This is the base class // implementation, which requires a pointer to deleted_chain be // stored on each instance. + INLINE void *operator new(size_t size); INLINE void operator delete(void *ptr); protected: - INLINE static void *do_operator_new(size_t size, qpGeomMunger *&_deleted_chain); + INLINE static void *do_operator_new(size_t size, qpGeomMunger **_deleted_chain); private: qpGeomMunger **_deleted_chain; @@ -120,8 +124,9 @@ private: PT(qpGeomMunger) _munger; }; - typedef pmap Formats; - Formats _formats; + typedef pmap Formats; + typedef pmap FormatsByAnimation; + FormatsByAnimation _formats_by_animation; bool _is_registered; typedef pset > Mungers; diff --git a/panda/src/gobj/qpgeomVertexAnimationSpec.I b/panda/src/gobj/qpgeomVertexAnimationSpec.I new file mode 100644 index 0000000000..ed216ea409 --- /dev/null +++ b/panda/src/gobj/qpgeomVertexAnimationSpec.I @@ -0,0 +1,198 @@ +// Filename: qpgeomVertexAnimationSpec.I +// Created by: drose (29Mar05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexAnimationSpec::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE qpGeomVertexAnimationSpec:: +qpGeomVertexAnimationSpec() : + _animation_type(AT_none), + _num_transforms(0), + _indexed_transforms(0) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexAnimationSpec::Copy Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE qpGeomVertexAnimationSpec:: +qpGeomVertexAnimationSpec(const qpGeomVertexAnimationSpec &other) : + _animation_type(other._animation_type), + _num_transforms(other._num_transforms), + _indexed_transforms(other._indexed_transforms) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexAnimationSpec::Copy Assignment Operator +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexAnimationSpec:: +operator = (const qpGeomVertexAnimationSpec &other) { + _animation_type = other._animation_type; + _num_transforms = other._num_transforms; + _indexed_transforms = other._indexed_transforms; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexAnimationSpec::get_animation_type +// Access: Published +// Description: Returns the type of animation represented by this +// spec. +//////////////////////////////////////////////////////////////////// +INLINE qpGeomVertexAnimationSpec::AnimationType qpGeomVertexAnimationSpec:: +get_animation_type() const { + return _animation_type; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexAnimationSpec::get_num_transforms +// Access: Published +// Description: This is only meaningful for animation_type +// AT_hardware. It specifies the maximum number of +// transforms that might be simultaneously applied to +// any one vertex by the data in this format. +//////////////////////////////////////////////////////////////////// +INLINE int qpGeomVertexAnimationSpec:: +get_num_transforms() const { + return _num_transforms; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexAnimationSpec::get_indexed_transforms +// Access: Published +// Description: This is only meaningful for animation_type +// AT_hardware. If true, it indicates that the format +// uses indexed animation palettes. It is false if each +// vertex will reference the first _num_transforms +// palette entries only. +//////////////////////////////////////////////////////////////////// +INLINE bool qpGeomVertexAnimationSpec:: +get_indexed_transforms() const { + return _indexed_transforms; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexAnimationSpec::set_none +// Access: Published +// Description: Specifies that no vertex animation is represented by +// this spec. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexAnimationSpec:: +set_none() { + _animation_type = AT_none; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexAnimationSpec::set_panda +// Access: Published +// Description: Specifies that vertex animation is to be performed by +// Panda. This is the most general setting and can +// handle any kind of vertex animation represented. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexAnimationSpec:: +set_panda() { + _animation_type = AT_panda; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexAnimationSpec::set_hardware +// Access: Published +// Description: Specifies that vertex animation is to be performed by +// the graphics hardware (or at least by the graphics +// backend API, which is actually still free to animate +// the vertices on the CPU). +// +// This is only legal if the graphics hardware can +// support the specified limits on number of transforms +// and/or indexed transforms. Also, no current graphics +// API's support morphing. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexAnimationSpec:: +set_hardware(int num_transforms, bool indexed_transforms) { + _animation_type = AT_hardware; + _num_transforms = num_transforms; + _indexed_transforms = indexed_transforms; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexAnimationSpec::operator < +// Access: Public +// Description: Provides an arbitrary ordering between different +// animation specs. +//////////////////////////////////////////////////////////////////// +INLINE bool qpGeomVertexAnimationSpec:: +operator < (const qpGeomVertexAnimationSpec &other) const { + return (compare_to(other) < 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexAnimationSpec::operator == +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool qpGeomVertexAnimationSpec:: +operator == (const qpGeomVertexAnimationSpec &other) const { + return (compare_to(other) == 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexAnimationSpec::operator != +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool qpGeomVertexAnimationSpec:: +operator != (const qpGeomVertexAnimationSpec &other) const { + return (compare_to(other) != 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexAnimationSpec::compare_to +// Access: Public +// Description: Provides an arbitrary ordering between different +// animation specs. +//////////////////////////////////////////////////////////////////// +INLINE int qpGeomVertexAnimationSpec:: +compare_to(const qpGeomVertexAnimationSpec &other) const { + if (_animation_type != other._animation_type) { + return (int)_animation_type - (int)other._animation_type; + } + + if (_animation_type == AT_hardware) { + if (_num_transforms != other._num_transforms) { + return _num_transforms - other._num_transforms; + } + if (_indexed_transforms != other._indexed_transforms) { + return (int)_indexed_transforms - (int)other._indexed_transforms; + } + } + + return 0; +} + +INLINE ostream & +operator << (ostream &out, const qpGeomVertexAnimationSpec &animation) { + animation.output(out); + return out; +} diff --git a/panda/src/gobj/qpgeomVertexAnimationSpec.cxx b/panda/src/gobj/qpgeomVertexAnimationSpec.cxx new file mode 100644 index 0000000000..13f251a7b3 --- /dev/null +++ b/panda/src/gobj/qpgeomVertexAnimationSpec.cxx @@ -0,0 +1,42 @@ +// Filename: qpgeomVertexAnimationSpec.cxx +// Created by: drose (29Mar05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "qpgeomVertexAnimationSpec.h" + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexAnimationSpec::output +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +void qpGeomVertexAnimationSpec:: +output(ostream &out) const { + switch (_animation_type) { + case AT_none: + out << "none"; + break; + + case AT_panda: + out << "panda"; + break; + + case AT_hardware: + out << "hardware(" << _num_transforms << ", " + << _indexed_transforms << ")"; + break; + } +} diff --git a/panda/src/gobj/qpgeomVertexAnimationSpec.h b/panda/src/gobj/qpgeomVertexAnimationSpec.h new file mode 100644 index 0000000000..f374036e0e --- /dev/null +++ b/panda/src/gobj/qpgeomVertexAnimationSpec.h @@ -0,0 +1,83 @@ +// Filename: qpgeomVertexAnimationSpec.h +// Created by: drose (29Mar05) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#ifndef qpGEOMVERTEXANIMATIONSPEC_H +#define qpGEOMVERTEXANIMATIONSPEC_H + +#include "pandabase.h" + +//////////////////////////////////////////////////////////////////// +// Class : qpGeomVertexAnimationSpec +// Description : This object describes how the vertex animation, if +// any, represented in a GeomVertexData is encoded. +// +// Vertex animation includes soft-skinned skeleton +// animation and morphs (blend shapes), and might be +// performed on the CPU by Panda, or passed down to the +// graphics backed to be performed on the hardware +// (depending on the hardware's advertised +// capabilities). +// +// Changing this setting doesn't by itself change the +// way the animation is actually performed; this just +// specifies how the vertices are set up to be animated. +// +// This is part of the experimental Geom rewrite. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA qpGeomVertexAnimationSpec { +PUBLISHED: + INLINE qpGeomVertexAnimationSpec(); + INLINE qpGeomVertexAnimationSpec(const qpGeomVertexAnimationSpec &other); + INLINE void operator = (const qpGeomVertexAnimationSpec &other); + + enum AnimationType { + AT_none, // No vertex animation. + AT_panda, // Vertex animation calculated on the CPU by Panda. + AT_hardware, // Hardware-accelerated animation on the graphics card. + }; + + INLINE AnimationType get_animation_type() const; + + INLINE int get_num_transforms() const; + INLINE bool get_indexed_transforms() const; + + INLINE void set_none(); + INLINE void set_panda(); + INLINE void set_hardware(int num_transforms, bool indexed_transforms); + + void output(ostream &out) const; + +public: + INLINE bool operator < (const qpGeomVertexAnimationSpec &other) const; + INLINE bool operator == (const qpGeomVertexAnimationSpec &other) const; + INLINE bool operator != (const qpGeomVertexAnimationSpec &other) const; + INLINE int compare_to(const qpGeomVertexAnimationSpec &other) const; + +private: + AnimationType _animation_type; + + int _num_transforms; + bool _indexed_transforms; +}; + +INLINE ostream & +operator << (ostream &out, const qpGeomVertexAnimationSpec &animation); + +#include "qpgeomVertexAnimationSpec.I" + +#endif diff --git a/panda/src/gobj/qpgeomVertexData.I b/panda/src/gobj/qpgeomVertexData.I index f2df2fe8a9..e9b9947616 100644 --- a/panda/src/gobj/qpgeomVertexData.I +++ b/panda/src/gobj/qpgeomVertexData.I @@ -118,6 +118,40 @@ get_array(int i) const { return cdata->_arrays[i]; } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexData::get_transform_palette +// Access: Published +// Description: Returns a const pointer to the TransformPalette +// assigned to this data. Vertices within the table +// will index into this palette to indicate their +// dynamic skinning information; this table is used when +// the vertex animation is to be performed by the +// graphics hardware (but also see +// get_transform_blend_palette()). +// +// This will return NULL if the vertex data does not +// have a TransformPalette assigned (which implies the +// vertices will not be animated by the graphics +// hardware). +//////////////////////////////////////////////////////////////////// +INLINE const TransformPalette *qpGeomVertexData:: +get_transform_palette() const { + CDReader cdata(_cycler); + return cdata->_transform_palette; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexData::clear_transform_palette +// Access: Published +// Description: Sets the TransformPalette pointer to NULL, +// removing the palette from the vertex data. This +// disables hardware-driven vertex animation. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexData:: +clear_transform_palette() { + set_transform_palette(NULL); +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomVertexData::get_transform_blend_palette // Access: Published @@ -288,6 +322,24 @@ unpack_8888_d(PN_uint32 data) { return data & 0xff; } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexData::add_transform +// Access: Private, Static +// Description: Adds the indicated transform to the palette, if it is +// not already there, and returns its index number. +//////////////////////////////////////////////////////////////////// +INLINE int qpGeomVertexData:: +add_transform(TransformPalette *palette, const VertexTransform *transform, + TransformMap &already_added) { + pair result = already_added.insert(TransformMap::value_type(transform, palette->get_num_transforms())); + + if (result.second) { + palette->add_transform(transform); + } + + return (*(result.first)).second; +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomVertexData::CData::Constructor // Access: Public diff --git a/panda/src/gobj/qpgeomVertexData.cxx b/panda/src/gobj/qpgeomVertexData.cxx index 7d5919ed3c..f5d2fc4c05 100644 --- a/panda/src/gobj/qpgeomVertexData.cxx +++ b/panda/src/gobj/qpgeomVertexData.cxx @@ -201,6 +201,47 @@ set_array(int i, const qpGeomVertexArrayData *array) { cdata->_animated_vertices_modified = UpdateSeq(); } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexData::modify_transform_palette +// Access: Published +// Description: Returns a modifiable pointer to the current +// TransformPalette on this vertex data, if any, or +// NULL if there is not a TransformPalette. See +// get_transform_palette(). +//////////////////////////////////////////////////////////////////// +TransformPalette *qpGeomVertexData:: +modify_transform_palette() { + // Perform copy-on-write: if the reference count on the palette is + // greater than 1, assume some other GeomVertexData has the same + // pointer, so make a copy of it first. + CDWriter cdata(_cycler); + + if (cdata->_transform_palette->get_ref_count() > 1) { + cdata->_transform_palette = new TransformPalette(*cdata->_transform_palette); + } + cdata->_modified = qpGeom::get_next_modified(); + cdata->_animated_vertices_modified = UpdateSeq(); + + return cdata->_transform_palette; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexData::set_transform_palette +// Access: Published +// Description: Replaces the TransformPalette on this vertex +// data with the indicated palette. The length of this +// palette should be consistent with the maximum palette +// index assigned to the vertices under the +// "transform_index" name. +//////////////////////////////////////////////////////////////////// +void qpGeomVertexData:: +set_transform_palette(const TransformPalette *palette) { + CDWriter cdata(_cycler); + cdata->_transform_palette = (TransformPalette *)palette; + cdata->_modified = qpGeom::get_next_modified(); + cdata->_animated_vertices_modified = UpdateSeq(); +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomVertexData::modify_transform_blend_palette // Access: Published @@ -442,6 +483,65 @@ copy_from(const qpGeomVertexData &source, bool keep_data_objects) { } } } + + // Also convert the animation tables as necessary. + const qpGeomVertexAnimationSpec &source_animation = source_format->get_animation(); + const qpGeomVertexAnimationSpec &dest_animation = dest_format->get_animation(); + if (source_animation != dest_animation) { + if (dest_animation.get_animation_type() == qpGeomVertexAnimationSpec::AT_hardware) { + // Convert Panda-style animation tables to hardware-style + // animation tables. + CPT(TransformBlendPalette) blend_palette = source.get_transform_blend_palette(); + if (blend_palette != (TransformBlendPalette *)NULL) { + PT(TransformPalette) transform_palette = new TransformPalette; + TransformMap already_added; + + if (dest_animation.get_indexed_transforms()) { + // Build an indexed transform array. This is easier; this + // means we can put the blends in any order. + qpGeomVertexWriter weight(this, InternalName::get_transform_weight()); + qpGeomVertexWriter index(this, InternalName::get_transform_index()); + qpGeomVertexReader from(&source, InternalName::get_transform_blend()); + + while (!from.is_at_end()) { + const TransformBlend &blend = blend_palette->get_blend(from.get_data1i()); + LVecBase4f weights = LVecBase4f::zero(); + int indices[4] = {0, 0, 0, 0}; + nassertv(blend.get_num_transforms() <= 4); + + for (int i = 0; i < blend.get_num_transforms(); i++) { + weights[i] = blend.get_weight(i); + indices[i] = add_transform(transform_palette, blend.get_transform(i), + already_added); + } + weight.set_data4f(weights); + index.set_data4i(indices[3], indices[2], indices[1], indices[0]); + } + } else { + // Build a nonindexed transform array. This means we have to + // use the same n transforms, in the same order, for each vertex. + qpGeomVertexWriter weight(this, InternalName::get_transform_weight()); + qpGeomVertexReader from(&source, InternalName::get_transform_blend()); + + while (!from.is_at_end()) { + const TransformBlend &blend = blend_palette->get_blend(from.get_data1i()); + LVecBase4f weights = LVecBase4f::zero(); + + for (int i = 0; i < blend.get_num_transforms(); i++) { + int index = add_transform(transform_palette, blend.get_transform(i), + already_added); + nassertv(index <= 4); + weights[index] = blend.get_weight(i); + } + weight.set_data4f(weights); + } + } + + clear_transform_blend_palette(); + set_transform_palette(transform_palette); + } + } + } } //////////////////////////////////////////////////////////////////// @@ -725,6 +825,11 @@ CPT(qpGeomVertexData) qpGeomVertexData:: do_animate_vertices(bool from_app) const { CDReader cdata(_cycler); + if (_format->get_animation().get_animation_type() != + qpGeomVertexAnimationSpec::AT_panda) { + return this; + } + UpdateSeq modified; if (cdata->_transform_blend_palette != (TransformBlendPalette *)NULL) { if (cdata->_slider_table != (SliderTable *)NULL) { diff --git a/panda/src/gobj/qpgeomVertexData.h b/panda/src/gobj/qpgeomVertexData.h index 2d0609e05c..63167a0752 100644 --- a/panda/src/gobj/qpgeomVertexData.h +++ b/panda/src/gobj/qpgeomVertexData.h @@ -25,6 +25,7 @@ #include "qpgeomVertexDataType.h" #include "qpgeomVertexArrayData.h" #include "qpgeomUsageHint.h" +#include "transformPalette.h" #include "transformBlendPalette.h" #include "sliderTable.h" #include "internalName.h" @@ -87,6 +88,11 @@ PUBLISHED: qpGeomVertexArrayData *modify_array(int i); void set_array(int i, const qpGeomVertexArrayData *array); + INLINE const TransformPalette *get_transform_palette() const; + TransformPalette *modify_transform_palette(); + void set_transform_palette(const TransformPalette *palette); + INLINE void clear_transform_palette(); + INLINE const TransformBlendPalette *get_transform_blend_palette() const; TransformBlendPalette *modify_transform_blend_palette(); void set_transform_blend_palette(const TransformBlendPalette *palette); @@ -154,6 +160,11 @@ private: uint8_rgba_to_packed_argb(unsigned char *to, int to_stride, const unsigned char *from, int from_stride, int num_records); + + typedef pmap TransformMap; + INLINE static int + add_transform(TransformPalette *palette, const VertexTransform *transform, + TransformMap &already_added); private: string _name; @@ -173,6 +184,7 @@ private: virtual void fillin(DatagramIterator &scan, BamReader *manager); Arrays _arrays; + PT(TransformPalette) _transform_palette; PT(TransformBlendPalette) _transform_blend_palette; PT(SliderTable) _slider_table; PT(qpGeomVertexData) _animated_vertices; diff --git a/panda/src/gobj/qpgeomVertexFormat.I b/panda/src/gobj/qpgeomVertexFormat.I index 08f6affb64..6b21aa3e00 100644 --- a/panda/src/gobj/qpgeomVertexFormat.I +++ b/panda/src/gobj/qpgeomVertexFormat.I @@ -67,6 +67,34 @@ register_format(qpGeomVertexArrayFormat *format) { return get_registry()->register_format(format); } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexFormat::get_animation +// Access: Published +// Description: Returns the GeomVertexAnimationSpec that indicates +// how this format's vertices are set up for animation. +//////////////////////////////////////////////////////////////////// +INLINE const qpGeomVertexAnimationSpec &qpGeomVertexFormat:: +get_animation() const { + return _animation; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexFormat::set_animation +// Access: Published +// Description: Resets the GeomVertexAnimationSpec that indicates +// how this format's vertices are set up for animation. +// You should also, of course, change the columns in the +// tables accordingly. +// +// This may not be called once the format has been +// registered. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexFormat:: +set_animation(const qpGeomVertexAnimationSpec &animation) { + nassertv(!_is_registered); + _animation = animation; +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomVertexFormat::get_num_arrays // Access: Published diff --git a/panda/src/gobj/qpgeomVertexFormat.cxx b/panda/src/gobj/qpgeomVertexFormat.cxx index 31af78611c..86aabaa9ec 100644 --- a/panda/src/gobj/qpgeomVertexFormat.cxx +++ b/panda/src/gobj/qpgeomVertexFormat.cxx @@ -58,6 +58,7 @@ qpGeomVertexFormat(qpGeomVertexArrayFormat *array_format) : qpGeomVertexFormat:: qpGeomVertexFormat(const qpGeomVertexFormat ©) : _is_registered(false), + _animation(copy._animation), _arrays(copy._arrays) { } @@ -71,6 +72,7 @@ void qpGeomVertexFormat:: operator = (const qpGeomVertexFormat ©) { nassertv(!_is_registered); + _animation = copy._animation; _arrays = copy._arrays; } @@ -84,8 +86,6 @@ qpGeomVertexFormat:: if (is_registered()) { get_registry()->unregister_format(this); } - - nassertv(_mungers.empty()); } //////////////////////////////////////////////////////////////////// @@ -379,6 +379,10 @@ output(ostream &out) const { ++ai; } } + + if (_animation.get_animation_type() != qpGeomVertexAnimationSpec::AT_none) { + out << ", anim " << _animation; + } } //////////////////////////////////////////////////////////////////// @@ -446,6 +450,11 @@ get_array_info(const InternalName *name, int &array_index, //////////////////////////////////////////////////////////////////// int qpGeomVertexFormat:: compare_to(const qpGeomVertexFormat &other) const { + int compare = _animation.compare_to(other._animation); + if (compare != 0) { + return compare; + } + if (_arrays.size() != other._arrays.size()) { return (int)_arrays.size() - (int)other._arrays.size(); } @@ -481,7 +490,6 @@ void qpGeomVertexFormat:: do_register() { nassertv(!_is_registered); nassertv(_data_types_by_name.empty()); - nassertv(_mungers.empty()); for (int array = 0; array < (int)_arrays.size(); ++array) { const qpGeomVertexArrayFormat *array_format = _arrays[array]; @@ -556,13 +564,6 @@ do_unregister() { _is_registered = false; _data_types_by_name.clear(); - - MutexHolder holder(_cache_lock); - Mungers::iterator mi; - for (mi = _mungers.begin(); mi != _mungers.end(); ++mi) { - (*mi)->remove_format(this); - } - _mungers.clear(); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/gobj/qpgeomVertexFormat.h b/panda/src/gobj/qpgeomVertexFormat.h index 7b6e9fb7ed..a3e5ae883f 100644 --- a/panda/src/gobj/qpgeomVertexFormat.h +++ b/panda/src/gobj/qpgeomVertexFormat.h @@ -21,6 +21,7 @@ #include "pandabase.h" #include "typedWritableReferenceCount.h" +#include "qpgeomVertexAnimationSpec.h" #include "qpgeomVertexArrayFormat.h" #include "internalName.h" #include "luse.h" @@ -66,6 +67,9 @@ PUBLISHED: INLINE static CPT(qpGeomVertexFormat) register_format(qpGeomVertexFormat *format); INLINE static CPT(qpGeomVertexFormat) register_format(qpGeomVertexArrayFormat *format); + INLINE const qpGeomVertexAnimationSpec &get_animation() const; + INLINE void set_animation(const qpGeomVertexAnimationSpec &animation); + INLINE int get_num_arrays() const; INLINE const qpGeomVertexArrayFormat *get_array(int array) const; qpGeomVertexArrayFormat *modify_array(int array); @@ -140,6 +144,8 @@ private: bool _is_registered; + qpGeomVertexAnimationSpec _animation; + typedef pvector< PT(qpGeomVertexArrayFormat) > Arrays; Arrays _arrays; @@ -161,12 +167,6 @@ private: typedef pvector Morphs; Morphs _morphs; - // This set keeps track of things that need to be told when we - // destruct, and it is protected by the mutex. - Mutex _cache_lock; - typedef pset Mungers; - Mungers _mungers; - // This is the global registry of all currently-in-use formats. typedef pset > Formats; class EXPCL_PANDA Registry { diff --git a/panda/src/gobj/qpgeomVertexReader.I b/panda/src/gobj/qpgeomVertexReader.I index 8800cbc3cd..2176023cdd 100644 --- a/panda/src/gobj/qpgeomVertexReader.I +++ b/panda/src/gobj/qpgeomVertexReader.I @@ -320,6 +320,45 @@ get_data1i() { return _reader->get_data1i(inc_pointer()); } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexReader::get_data2i +// Access: Published +// Description: Returns the data associated with the read vertex, +// expressed as a 2-component value, and advances the +// read vertex. +//////////////////////////////////////////////////////////////////// +INLINE const int *qpGeomVertexReader:: +get_data2i() { + nassertr(_data_type != (qpGeomVertexDataType *)NULL, 0); + return _reader->get_data2i(inc_pointer()); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexReader::get_data3i +// Access: Published +// Description: Returns the data associated with the read vertex, +// expressed as a 3-component value, and advances the +// read vertex. +//////////////////////////////////////////////////////////////////// +INLINE const int *qpGeomVertexReader:: +get_data3i() { + nassertr(_data_type != (qpGeomVertexDataType *)NULL, 0); + return _reader->get_data3i(inc_pointer()); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexReader::get_data4i +// Access: Published +// Description: Returns the data associated with the read vertex, +// expressed as a 4-component value, and advances the +// read vertex. +//////////////////////////////////////////////////////////////////// +INLINE const int *qpGeomVertexReader:: +get_data4i() { + nassertr(_data_type != (qpGeomVertexDataType *)NULL, 0); + return _reader->get_data4i(inc_pointer()); +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomVertexReader::set_pointer // Access: Private diff --git a/panda/src/gobj/qpgeomVertexReader.cxx b/panda/src/gobj/qpgeomVertexReader.cxx index a0ed258e90..09137769ce 100644 --- a/panda/src/gobj/qpgeomVertexReader.cxx +++ b/panda/src/gobj/qpgeomVertexReader.cxx @@ -423,6 +423,215 @@ get_data1i(const unsigned char *pointer) { return 0; } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexReader::Reader::get_data2i +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +const int *qpGeomVertexReader::Reader:: +get_data2i(const unsigned char *pointer) { + switch (_data_type->get_num_values()) { + case 1: + _i[0] = get_data1i(pointer); + _i[1] = 0; + return _i; + + default: + switch (_data_type->get_numeric_type()) { + case qpGeomVertexDataType::NT_uint8: + _i[0] = pointer[0]; + _i[1] = pointer[1]; + return _i; + + case qpGeomVertexDataType::NT_uint16: + { + const PN_uint16 *pi = (const PN_uint16 *)pointer; + _i[0] = pi[0]; + _i[1] = pi[1]; + } + return _i; + + case qpGeomVertexDataType::NT_packed_8888: + { + PN_uint32 dword = *(const PN_uint32 *)pointer; + if (_data_type->get_contents() == qpGeomVertexDataType::C_argb) { + _i[0] = qpGeomVertexData::unpack_8888_b(dword); + _i[1] = qpGeomVertexData::unpack_8888_c(dword); + } else { + _i[0] = qpGeomVertexData::unpack_8888_a(dword); + _i[1] = qpGeomVertexData::unpack_8888_b(dword); + } + } + return _i; + + case qpGeomVertexDataType::NT_float32: + { + const PN_float32 *pi = (const PN_float32 *)pointer; + _i[0] = (int)pi[0]; + _i[1] = (int)pi[1]; + } + return _i; + } + } + + return _i; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexReader::Reader::get_data3i +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +const int *qpGeomVertexReader::Reader:: +get_data3i(const unsigned char *pointer) { + switch (_data_type->get_num_values()) { + case 1: + _i[0] = get_data1i(pointer); + _i[1] = 0; + _i[2] = 0; + return _i; + + case 2: + { + const int *i = get_data2i(pointer); + _i[0] = i[0]; + _i[1] = i[1]; + _i[2] = 0; + } + return _i; + + default: + switch (_data_type->get_numeric_type()) { + case qpGeomVertexDataType::NT_uint8: + _i[0] = pointer[0]; + _i[1] = pointer[1]; + _i[2] = pointer[2]; + return _i; + + case qpGeomVertexDataType::NT_uint16: + { + const PN_uint16 *pi = (const PN_uint16 *)pointer; + _i[0] = pi[0]; + _i[1] = pi[1]; + _i[2] = pi[2]; + } + return _i; + + case qpGeomVertexDataType::NT_packed_8888: + { + PN_uint32 dword = *(const PN_uint32 *)pointer; + if (_data_type->get_contents() == qpGeomVertexDataType::C_argb) { + _i[0] = qpGeomVertexData::unpack_8888_b(dword); + _i[1] = qpGeomVertexData::unpack_8888_c(dword); + _i[2] = qpGeomVertexData::unpack_8888_d(dword); + } else { + _i[0] = qpGeomVertexData::unpack_8888_a(dword); + _i[1] = qpGeomVertexData::unpack_8888_b(dword); + _i[2] = qpGeomVertexData::unpack_8888_c(dword); + } + } + return _i; + + case qpGeomVertexDataType::NT_float32: + { + const PN_float32 *pi = (const PN_float32 *)pointer; + _i[0] = (int)pi[0]; + _i[1] = (int)pi[1]; + _i[2] = (int)pi[2]; + } + return _i; + } + } + + return _i; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexReader::Reader::get_data4i +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +const int *qpGeomVertexReader::Reader:: +get_data4i(const unsigned char *pointer) { + switch (_data_type->get_num_values()) { + case 1: + _i[0] = get_data1i(pointer); + _i[1] = 0; + _i[2] = 0; + _i[3] = 0; + return _i; + + case 2: + { + const int *i = get_data2i(pointer); + _i[0] = i[0]; + _i[1] = i[1]; + _i[2] = 0; + _i[3] = 0; + } + return _i; + + case 3: + { + const int *i = get_data3i(pointer); + _i[0] = i[0]; + _i[1] = i[1]; + _i[2] = i[2]; + _i[3] = 0; + } + return _i; + + default: + switch (_data_type->get_numeric_type()) { + case qpGeomVertexDataType::NT_uint8: + _i[0] = pointer[0]; + _i[1] = pointer[1]; + _i[2] = pointer[2]; + _i[3] = pointer[3]; + return _i; + + case qpGeomVertexDataType::NT_uint16: + { + const PN_uint16 *pi = (const PN_uint16 *)pointer; + _i[0] = pi[0]; + _i[1] = pi[1]; + _i[2] = pi[2]; + _i[3] = pi[3]; + } + return _i; + + case qpGeomVertexDataType::NT_packed_8888: + { + PN_uint32 dword = *(const PN_uint32 *)pointer; + if (_data_type->get_contents() == qpGeomVertexDataType::C_argb) { + _i[0] = qpGeomVertexData::unpack_8888_b(dword); + _i[1] = qpGeomVertexData::unpack_8888_c(dword); + _i[2] = qpGeomVertexData::unpack_8888_d(dword); + _i[3] = qpGeomVertexData::unpack_8888_a(dword); + } else { + _i[0] = qpGeomVertexData::unpack_8888_a(dword); + _i[1] = qpGeomVertexData::unpack_8888_b(dword); + _i[2] = qpGeomVertexData::unpack_8888_c(dword); + _i[3] = qpGeomVertexData::unpack_8888_d(dword); + } + } + return _i; + + case qpGeomVertexDataType::NT_float32: + { + const PN_float32 *pi = (const PN_float32 *)pointer; + _i[0] = (int)pi[0]; + _i[1] = (int)pi[1]; + _i[2] = (int)pi[2]; + _i[3] = (int)pi[3]; + } + return _i; + } + } + + return _i; +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomVertexReader::Reader_point::get_data1f // Access: Public, Virtual diff --git a/panda/src/gobj/qpgeomVertexReader.h b/panda/src/gobj/qpgeomVertexReader.h index 0d4c0c6a0d..eef9656008 100644 --- a/panda/src/gobj/qpgeomVertexReader.h +++ b/panda/src/gobj/qpgeomVertexReader.h @@ -91,6 +91,9 @@ PUBLISHED: INLINE const LVecBase4f &get_data4f(); INLINE int get_data1i(); + INLINE const int *get_data2i(); + INLINE const int *get_data3i(); + INLINE const int *get_data4i(); private: class Reader; @@ -124,6 +127,9 @@ private: virtual const LVecBase3f &get_data3f(const unsigned char *pointer); virtual const LVecBase4f &get_data4f(const unsigned char *pointer); virtual int get_data1i(const unsigned char *pointer); + virtual const int *get_data2i(const unsigned char *pointer); + virtual const int *get_data3i(const unsigned char *pointer); + virtual const int *get_data4i(const unsigned char *pointer); INLINE float maybe_scale_color(unsigned int value); INLINE void maybe_scale_color(unsigned int a, unsigned int b); @@ -136,6 +142,7 @@ private: LVecBase2f _v2; LVecBase3f _v3; LVecBase4f _v4; + int _i[4]; }; // This is a specialization on the generic Reader that handles diff --git a/panda/src/gobj/qpgeomVertexWriter.I b/panda/src/gobj/qpgeomVertexWriter.I index 77433cc05f..70e2fd1ddb 100644 --- a/panda/src/gobj/qpgeomVertexWriter.I +++ b/panda/src/gobj/qpgeomVertexWriter.I @@ -372,6 +372,93 @@ set_data1i(int data) { _writer->set_data1i(inc_pointer(), data); } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexWriter::set_data2i +// Access: Published +// Description: Sets the write vertex to a particular 2-component +// value, and advances the write vertex. +// +// It is an error for the write vertex to advance past +// the end of data. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexWriter:: +set_data2i(int a, int b) { + nassertv(_data_type != (qpGeomVertexDataType *)NULL); + _writer->set_data2i(inc_pointer(), a, b); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexWriter::set_data2i +// Access: Published +// Description: Sets the write vertex to a particular 2-component +// value, and advances the write vertex. +// +// It is an error for the write vertex to advance past +// the end of data. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexWriter:: +set_data2i(const int data[2]) { + set_data2i(data[0], data[1]); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexWriter::set_data3i +// Access: Published +// Description: Sets the write vertex to a particular 3-component +// value, and advances the write vertex. +// +// It is an error for the write vertex to advance past +// the end of data. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexWriter:: +set_data3i(int a, int b, int c) { + nassertv(_data_type != (qpGeomVertexDataType *)NULL); + _writer->set_data3i(inc_pointer(), a, b, c); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexWriter::set_data3i +// Access: Published +// Description: Sets the write vertex to a particular 3-component +// value, and advances the write vertex. +// +// It is an error for the write vertex to advance past +// the end of data. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexWriter:: +set_data3i(const int data[3]) { + set_data3i(data[0], data[1], data[2]); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexWriter::set_data4i +// Access: Published +// Description: Sets the write vertex to a particular 4-component +// value, and advances the write vertex. +// +// It is an error for the write vertex to advance past +// the end of data. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexWriter:: +set_data4i(int a, int b, int c, int d) { + nassertv(_data_type != (qpGeomVertexDataType *)NULL); + _writer->set_data4i(inc_pointer(), a, b, c, d); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexWriter::set_data4i +// Access: Published +// Description: Sets the write vertex to a particular 4-component +// value, and advances the write vertex. +// +// It is an error for the write vertex to advance past +// the end of data. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexWriter:: +set_data4i(const int data[4]) { + set_data4i(data[0], data[1], data[2], data[3]); +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomVertexWriter::add_data1f // Access: Published @@ -489,6 +576,93 @@ add_data1i(int data) { _writer->set_data1i(inc_add_pointer(), data); } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexWriter::add_data2i +// Access: Published +// Description: Sets the write vertex to a particular 2-component +// value, and advances the write vertex. +// +// If the write vertex advances past the end of data, +// implicitly adds a new vertex to the data. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexWriter:: +add_data2i(int a, int b) { + nassertv(_data_type != (qpGeomVertexDataType *)NULL); + _writer->set_data2i(inc_add_pointer(), a, b); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexWriter::add_data2i +// Access: Published +// Description: Sets the write vertex to a particular 2-component +// value, and advances the write vertex. +// +// If the write vertex advances past the end of data, +// implicitly adds a new vertex to the data. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexWriter:: +add_data2i(const int data[2]) { + add_data2i(data[0], data[1]); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexWriter::add_data3i +// Access: Published +// Description: Sets the write vertex to a particular 3-component +// value, and advances the write vertex. +// +// If the write vertex advances past the end of data, +// implicitly adds a new vertex to the data. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexWriter:: +add_data3i(int a, int b, int c) { + nassertv(_data_type != (qpGeomVertexDataType *)NULL); + _writer->set_data3i(inc_add_pointer(), a, b, c); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexWriter::add_data3i +// Access: Published +// Description: Sets the write vertex to a particular 3-component +// value, and advances the write vertex. +// +// If the write vertex advances past the end of data, +// implicitly adds a new vertex to the data. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexWriter:: +add_data3i(const int data[3]) { + add_data3i(data[0], data[1], data[2]); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexWriter::add_data4i +// Access: Published +// Description: Sets the write vertex to a particular 4-component +// value, and advances the write vertex. +// +// If the write vertex advances past the end of data, +// implicitly adds a new vertex to the data. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexWriter:: +add_data4i(int a, int b, int c, int d) { + nassertv(_data_type != (qpGeomVertexDataType *)NULL); + _writer->set_data4i(inc_add_pointer(), a, b, c, d); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexWriter::add_data4i +// Access: Published +// Description: Sets the write vertex to a particular 4-component +// value, and advances the write vertex. +// +// If the write vertex advances past the end of data, +// implicitly adds a new vertex to the data. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexWriter:: +add_data4i(const int data[4]) { + add_data4i(data[0], data[1], data[2], data[3]); +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomVertexWriter::set_pointer // Access: Private diff --git a/panda/src/gobj/qpgeomVertexWriter.cxx b/panda/src/gobj/qpgeomVertexWriter.cxx index dd01f9cee6..5c66d62e1b 100644 --- a/panda/src/gobj/qpgeomVertexWriter.cxx +++ b/panda/src/gobj/qpgeomVertexWriter.cxx @@ -185,14 +185,7 @@ set_data1f(unsigned char *pointer, float data) { break; case qpGeomVertexDataType::NT_packed_8888: - { - maybe_scale_color(data); - if (_data_type->get_contents() == qpGeomVertexDataType::C_argb) { - *(PN_uint32 *)pointer = qpGeomVertexData::pack_8888(0, _a, 0, 0); - } else { - *(PN_uint32 *)pointer = qpGeomVertexData::pack_8888(_a, 0, 0, 0); - } - } + nassertv(false); break; case qpGeomVertexDataType::NT_float32: @@ -243,14 +236,7 @@ set_data2f(unsigned char *pointer, const LVecBase2f &data) { break; case qpGeomVertexDataType::NT_packed_8888: - { - maybe_scale_color(data); - if (_data_type->get_contents() == qpGeomVertexDataType::C_argb) { - *(PN_uint32 *)pointer = qpGeomVertexData::pack_8888(0, _a, _b, 0); - } else { - *(PN_uint32 *)pointer = qpGeomVertexData::pack_8888(_a, _b, 0, 0); - } - } + nassertv(false); break; case qpGeomVertexDataType::NT_float32: @@ -308,14 +294,7 @@ set_data3f(unsigned char *pointer, const LVecBase3f &data) { break; case qpGeomVertexDataType::NT_packed_8888: - { - maybe_scale_color(data); - if (_data_type->get_contents() == qpGeomVertexDataType::C_argb) { - *(PN_uint32 *)pointer = qpGeomVertexData::pack_8888(0, _a, _b, _c); - } else { - *(PN_uint32 *)pointer = qpGeomVertexData::pack_8888(_a, _b, _c, 0); - } - } + nassertv(false); break; case qpGeomVertexDataType::NT_float32: @@ -406,28 +385,206 @@ set_data4f(unsigned char *pointer, const LVecBase4f &data) { // Description: //////////////////////////////////////////////////////////////////// void qpGeomVertexWriter::Writer:: -set_data1i(unsigned char *pointer, int data) { - switch (_data_type->get_numeric_type()) { - case qpGeomVertexDataType::NT_uint8: - *pointer = data; - break; - - case qpGeomVertexDataType::NT_uint16: - *(PN_uint16 *)pointer = data; - break; - - case qpGeomVertexDataType::NT_packed_8888: - { - if (_data_type->get_contents() == qpGeomVertexDataType::C_argb) { - *(PN_uint32 *)pointer = qpGeomVertexData::pack_8888(0, data, 0, 0); - } else { - *(PN_uint32 *)pointer = qpGeomVertexData::pack_8888(_a, data, 0, 0); - } +set_data1i(unsigned char *pointer, int a) { + switch (_data_type->get_num_values()) { + case 1: + switch (_data_type->get_numeric_type()) { + case qpGeomVertexDataType::NT_uint8: + *pointer = a; + break; + + case qpGeomVertexDataType::NT_uint16: + *(PN_uint16 *)pointer = a; + break; + + case qpGeomVertexDataType::NT_packed_8888: + nassertv(false); + break; + + case qpGeomVertexDataType::NT_float32: + *(PN_float32 *)pointer = (float)a; + break; } break; - case qpGeomVertexDataType::NT_float32: - *(PN_float32 *)pointer = (float)data; + case 2: + set_data2i(pointer, a, 0); + break; + + case 3: + set_data3i(pointer, a, 0, 0); + break; + + default: + set_data4i(pointer, a, 0, 0, 0); + break; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexWriter::Writer::set_data2i +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void qpGeomVertexWriter::Writer:: +set_data2i(unsigned char *pointer, int a, int b) { + switch (_data_type->get_num_values()) { + case 1: + set_data1i(pointer, a); + break; + + case 2: + switch (_data_type->get_numeric_type()) { + case qpGeomVertexDataType::NT_uint8: + pointer[0] = a; + pointer[1] = b; + break; + + case qpGeomVertexDataType::NT_uint16: + { + PN_uint16 *pi = (PN_uint16 *)pointer; + pi[0] = a; + pi[1] = b; + } + break; + + case qpGeomVertexDataType::NT_packed_8888: + nassertv(false); + break; + + case qpGeomVertexDataType::NT_float32: + { + PN_float32 *pi = (PN_float32 *)pointer; + pi[0] = a; + pi[1] = b; + } + break; + } + break; + + case 3: + set_data3i(pointer, a, b, 0); + break; + + default: + set_data4i(pointer, a, b, 0, 0); + break; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexWriter::Writer::set_data3i +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void qpGeomVertexWriter::Writer:: +set_data3i(unsigned char *pointer, int a, int b, int c) { + switch (_data_type->get_num_values()) { + case 1: + set_data1i(pointer, a); + break; + + case 2: + set_data2i(pointer, a, b); + break; + + case 3: + switch (_data_type->get_numeric_type()) { + case qpGeomVertexDataType::NT_uint8: + pointer[0] = a; + pointer[1] = b; + pointer[2] = c; + break; + + case qpGeomVertexDataType::NT_uint16: + { + PN_uint16 *pi = (PN_uint16 *)pointer; + pi[0] = a; + pi[1] = b; + pi[2] = c; + } + break; + + case qpGeomVertexDataType::NT_packed_8888: + nassertv(false); + break; + + case qpGeomVertexDataType::NT_float32: + { + PN_float32 *pi = (PN_float32 *)pointer; + pi[0] = a; + pi[1] = b; + pi[2] = c; + } + break; + } + break; + + default: + set_data4i(pointer, a, b, c, 0); + break; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexWriter::Writer::set_data4i +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void qpGeomVertexWriter::Writer:: +set_data4i(unsigned char *pointer, int a, int b, int c, int d) { + switch (_data_type->get_num_values()) { + case 1: + set_data1i(pointer, a); + break; + + case 2: + set_data2i(pointer, a, b); + break; + + case 3: + set_data3i(pointer, a, b, c); + break; + + default: + switch (_data_type->get_numeric_type()) { + case qpGeomVertexDataType::NT_uint8: + pointer[0] = a; + pointer[1] = b; + pointer[2] = c; + pointer[3] = d; + break; + + case qpGeomVertexDataType::NT_uint16: + { + PN_uint16 *pi = (PN_uint16 *)pointer; + pi[0] = a; + pi[1] = b; + pi[2] = c; + pi[3] = d; + } + break; + + case qpGeomVertexDataType::NT_packed_8888: + { + if (_data_type->get_contents() == qpGeomVertexDataType::C_argb) { + *(PN_uint32 *)pointer = qpGeomVertexData::pack_8888(d, a, b, c); + } else { + *(PN_uint32 *)pointer = qpGeomVertexData::pack_8888(a, b, c, d); + } + } + break; + + case qpGeomVertexDataType::NT_float32: + { + PN_float32 *pi = (PN_float32 *)pointer; + pi[0] = a; + pi[1] = b; + pi[2] = c; + pi[3] = d; + } + break; + } break; } } diff --git a/panda/src/gobj/qpgeomVertexWriter.h b/panda/src/gobj/qpgeomVertexWriter.h index 4bfdfd93db..f44d232de0 100644 --- a/panda/src/gobj/qpgeomVertexWriter.h +++ b/panda/src/gobj/qpgeomVertexWriter.h @@ -106,6 +106,12 @@ PUBLISHED: INLINE void set_data4f(const LVecBase4f &data); INLINE void set_data1i(int data); + INLINE void set_data2i(int a, int b); + INLINE void set_data2i(const int data[2]); + INLINE void set_data3i(int a, int b, int c); + INLINE void set_data3i(const int data[3]); + INLINE void set_data4i(int a, int b, int c, int d); + INLINE void set_data4i(const int data[4]); INLINE void add_data1f(float data); INLINE void add_data2f(float x, float y); @@ -116,6 +122,12 @@ PUBLISHED: INLINE void add_data4f(const LVecBase4f &data); INLINE void add_data1i(int data); + INLINE void add_data2i(int a, int b); + INLINE void add_data2i(const int data[2]); + INLINE void add_data3i(int a, int b, int c); + INLINE void add_data3i(const int data[3]); + INLINE void add_data4i(int a, int b, int c, int d); + INLINE void add_data4i(const int data[4]); private: class Writer; @@ -150,7 +162,10 @@ private: virtual void set_data3f(unsigned char *pointer, const LVecBase3f &data); virtual void set_data4f(unsigned char *pointer, const LVecBase4f &data); - virtual void set_data1i(unsigned char *pointer, int data); + virtual void set_data1i(unsigned char *pointer, int a); + virtual void set_data2i(unsigned char *pointer, int a, int b); + virtual void set_data3i(unsigned char *pointer, int a, int b, int c); + virtual void set_data4i(unsigned char *pointer, int a, int b, int c, int d); INLINE unsigned int maybe_scale_color(float data); INLINE void maybe_scale_color(const LVecBase2f &data); diff --git a/panda/src/gobj/transformBlendPalette.I b/panda/src/gobj/transformBlendPalette.I index 6b6644630a..214c9d408d 100644 --- a/panda/src/gobj/transformBlendPalette.I +++ b/panda/src/gobj/transformBlendPalette.I @@ -58,6 +58,49 @@ get_modified() const { } } +//////////////////////////////////////////////////////////////////// +// Function: TransformBlendPalette::get_num_transforms +// Access: Published +// Description: Returns the number of unique VertexTransform objects +// represented in the palette. This will correspond to +// the size of the TransformPalette object that would +// represent the same table. This is also the same +// limit reflected by +// GraphicsStateGuardian::get_max_vertex_transform_indices(). +//////////////////////////////////////////////////////////////////// +INLINE int TransformBlendPalette:: +get_num_transforms() const { + consider_rebuild_index(); + return _num_transforms; +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformBlendPalette::get_max_simultaneous_transforms +// Access: Published +// Description: Returns the maximum number of unique VertexTransform +// objects that are applied to any one vertex +// simultaneously. This is the same limit reflected by +// GraphicsStateGuardian::get_max_vertex_transforms(). +//////////////////////////////////////////////////////////////////// +INLINE int TransformBlendPalette:: +get_max_simultaneous_transforms() const { + consider_rebuild_index(); + return _max_simultaneous_transforms; +} + +//////////////////////////////////////////////////////////////////// +// Function: TransformBlendPalette::consider_rebuild_index +// Access: Private +// Description: Calls rebuild_index() if the index needs to be +// rebuilt. +//////////////////////////////////////////////////////////////////// +INLINE void TransformBlendPalette:: +consider_rebuild_index() const { + if (_blend_index.empty()) { + ((TransformBlendPalette *)this)->rebuild_index(); + } +} + //////////////////////////////////////////////////////////////////// // Function: TransformBlendPalette::CData::Constructor // Access: Public diff --git a/panda/src/gobj/transformBlendPalette.cxx b/panda/src/gobj/transformBlendPalette.cxx index 34081b1215..0bd1b718a0 100644 --- a/panda/src/gobj/transformBlendPalette.cxx +++ b/panda/src/gobj/transformBlendPalette.cxx @@ -95,9 +95,7 @@ remove_blend(int n) { //////////////////////////////////////////////////////////////////// int TransformBlendPalette:: add_blend(const TransformBlend &blend) { - if (_blend_index.empty()) { - rebuild_index(); - } + consider_rebuild_index(); BlendIndex::iterator bi; bi = _blend_index.find(&blend); @@ -148,9 +146,25 @@ void TransformBlendPalette:: rebuild_index() { _blend_index.clear(); - for (int i = 0; i < (int)_blends.size(); i++) { - _blend_index[&_blends[i]] = i; + // We'll also count up these two statistics while we rebuild the + // index. + _num_transforms = 0; + _max_simultaneous_transforms = 0; + + pset transforms; + + for (int i = 0; i < (int)_blends.size(); ++i) { + const TransformBlend &blend = _blends[i]; + _blend_index[&blend] = i; + + for (int ti = 0; ti < blend.get_num_transforms(); ++ti) { + transforms.insert(blend.get_transform(ti)); + } + _max_simultaneous_transforms = max(_max_simultaneous_transforms, + blend.get_num_transforms()); } + + _num_transforms = transforms.size(); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/gobj/transformBlendPalette.h b/panda/src/gobj/transformBlendPalette.h index eb53b21e39..4847b829b4 100644 --- a/panda/src/gobj/transformBlendPalette.h +++ b/panda/src/gobj/transformBlendPalette.h @@ -64,10 +64,14 @@ PUBLISHED: void remove_blend(int n); int add_blend(const TransformBlend &blend); + INLINE int get_num_transforms() const; + INLINE int get_max_simultaneous_transforms() const; + void write(ostream &out, int indent_level) const; private: void clear_index(); + INLINE void consider_rebuild_index() const; void rebuild_index(); private: @@ -84,6 +88,8 @@ private: // invalidating all the pointers into it). typedef pmap > BlendIndex; BlendIndex _blend_index; + int _num_transforms; + int _max_simultaneous_transforms; // Even though we don't store the actual blend palette data in a // CycleData structure, we do need to keep a local cache of the diff --git a/panda/src/gobj/transformPalette.cxx b/panda/src/gobj/transformPalette.cxx index 924a343a8c..ce79d2ade7 100644 --- a/panda/src/gobj/transformPalette.cxx +++ b/panda/src/gobj/transformPalette.cxx @@ -75,7 +75,7 @@ TransformPalette:: // unregistered palettes. //////////////////////////////////////////////////////////////////// void TransformPalette:: -set_transform(int n, VertexTransform *transform) { +set_transform(int n, const VertexTransform *transform) { nassertv(!_is_registered); nassertv(n >= 0 && n < (int)_transforms.size()); _transforms[n] = transform; @@ -102,7 +102,7 @@ remove_transform(int n) { // unregistered palettes. //////////////////////////////////////////////////////////////////// int TransformPalette:: -add_transform(VertexTransform *transform) { +add_transform(const VertexTransform *transform) { nassertr(!_is_registered, -1); int new_index = (int)_transforms.size(); _transforms.push_back(transform); @@ -132,7 +132,8 @@ do_register() { Transforms::iterator ti; for (ti = _transforms.begin(); ti != _transforms.end(); ++ti) { - bool inserted = (*ti)->_palettes.insert(this).second; + VertexTransform *transform = (VertexTransform *)(*ti).p(); + bool inserted = transform->_palettes.insert(this).second; nassertv(inserted); } _is_registered = true; @@ -150,7 +151,8 @@ do_unregister() { Transforms::iterator ti; for (ti = _transforms.begin(); ti != _transforms.end(); ++ti) { - (*ti)->_palettes.erase(this); + VertexTransform *transform = (VertexTransform *)(*ti).p(); + transform->_palettes.erase(this); } _is_registered = false; } diff --git a/panda/src/gobj/transformPalette.h b/panda/src/gobj/transformPalette.h index 213bd9195c..e88a82f244 100644 --- a/panda/src/gobj/transformPalette.h +++ b/panda/src/gobj/transformPalette.h @@ -58,9 +58,9 @@ PUBLISHED: INLINE const VertexTransform *get_transform(int n) const; INLINE UpdateSeq get_modified() const; - void set_transform(int n, VertexTransform *transform); + void set_transform(int n, const VertexTransform *transform); void remove_transform(int n); - int add_transform(VertexTransform *transform); + int add_transform(const VertexTransform *transform); void write(ostream &out) const; @@ -72,7 +72,7 @@ private: private: bool _is_registered; - typedef pvector< PT(VertexTransform) > Transforms; + typedef pvector< CPT(VertexTransform) > Transforms; Transforms _transforms; // This is the data that must be cycled between pipeline stages. diff --git a/panda/src/gobj/vertexTransform.cxx b/panda/src/gobj/vertexTransform.cxx index 639b2979d0..155ebcc707 100644 --- a/panda/src/gobj/vertexTransform.cxx +++ b/panda/src/gobj/vertexTransform.cxx @@ -47,6 +47,24 @@ VertexTransform:: nassertv(_palettes.empty()); } +//////////////////////////////////////////////////////////////////// +// Function: VertexTransform::mult_matrix +// Access: Published, Virtual +// Description: Premultiplies this transform's matrix with the +// indicated previous matrix, so that the result is the +// net composition of the given transform with this +// transform. The result is stored in the parameter +// "result", which should not be the same matrix as +// previous. +//////////////////////////////////////////////////////////////////// +void VertexTransform:: +mult_matrix(LMatrix4f &result, const LMatrix4f &previous) const { + nassertv(&result != &previous); + LMatrix4f me; + get_matrix(me); + result.multiply(me, previous); +} + //////////////////////////////////////////////////////////////////// // Function: VertexTransform::accumulate_matrix // Access: Published, Virtual diff --git a/panda/src/gobj/vertexTransform.h b/panda/src/gobj/vertexTransform.h index b37e89d1f9..9291b1f223 100644 --- a/panda/src/gobj/vertexTransform.h +++ b/panda/src/gobj/vertexTransform.h @@ -48,6 +48,7 @@ PUBLISHED: virtual ~VertexTransform(); virtual void get_matrix(LMatrix4f &matrix) const=0; + virtual void mult_matrix(LMatrix4f &result, const LMatrix4f &previous) const; virtual void accumulate_matrix(LMatrix4f &accum, float weight) const; INLINE UpdateSeq get_modified() const;