new scene graph, transforms and cameras

This commit is contained in:
David Rose 2002-02-26 23:23:39 +00:00
parent 5974c85efe
commit 063c2cafff
55 changed files with 3924 additions and 624 deletions

View File

@ -102,6 +102,17 @@ get_camera() const {
return _camera;
}
////////////////////////////////////////////////////////////////////
// Function: DisplayRegion::get_qpcamera
// Access: Public
// Description: Returns the camera associated with this
// DisplayRegion, or NULL if no camera is associated.
////////////////////////////////////////////////////////////////////
INLINE const NodeChain &DisplayRegion::
get_qpcamera() const {
return _qpcamera;
}
////////////////////////////////////////////////////////////////////
// Function: DisplayRegion::set_cull_frustum
// Access: Public

View File

@ -22,6 +22,7 @@
#include "graphicsWindow.h"
#include "config_display.h"
#include "displayRegion.h"
#include "qpcamera.h"
////////////////////////////////////////////////////////////////////
@ -33,6 +34,7 @@ DisplayRegion::
DisplayRegion(GraphicsLayer *layer) :
_l(0.), _r(1.), _b(0.), _t(1.),
_layer(layer),
_camera_node((qpCamera *)NULL),
_active(true)
{
}
@ -44,10 +46,11 @@ DisplayRegion(GraphicsLayer *layer) :
////////////////////////////////////////////////////////////////////
DisplayRegion::
DisplayRegion(GraphicsLayer *layer, const float l,
const float r, const float b, const float t)
: _l(l), _r(r), _b(b), _t(t),
_layer(layer),
_active(true)
const float r, const float b, const float t) :
_l(l), _r(r), _b(b), _t(t),
_layer(layer),
_camera_node((qpCamera *)NULL),
_active(true)
{
}
@ -62,7 +65,8 @@ DisplayRegion::
DisplayRegion(int xsize, int ysize) :
_l(0.), _r(1.), _b(0.), _t(1.),
_pl(0), _pr(xsize), _pb(0), _pt(ysize),
_layer((GraphicsLayer *)0L),
_layer((GraphicsLayer *)NULL),
_camera_node((qpCamera *)NULL),
_active(true)
{
}
@ -74,8 +78,7 @@ DisplayRegion(int xsize, int ysize) :
////////////////////////////////////////////////////////////////////
DisplayRegion::
DisplayRegion(const DisplayRegion&) {
display_cat.error()
<< "DisplayRegions should not be copied" << endl;
nassertv(false);
}
////////////////////////////////////////////////////////////////////
@ -83,11 +86,9 @@ DisplayRegion(const DisplayRegion&) {
// Access: Private
// Description:
////////////////////////////////////////////////////////////////////
DisplayRegion &DisplayRegion::
operator=(const DisplayRegion&) {
display_cat.error()
<< "DisplayRegions should not be assigned" << endl;
return *this;
void DisplayRegion::
operator = (const DisplayRegion&) {
nassertv(false);
}
////////////////////////////////////////////////////////////////////
@ -97,6 +98,7 @@ operator=(const DisplayRegion&) {
////////////////////////////////////////////////////////////////////
DisplayRegion::
~DisplayRegion() {
set_qpcamera(NodeChain());
}
////////////////////////////////////////////////////////////////////
@ -182,6 +184,41 @@ set_camera(Camera *camera) {
set_cull_frustum(camera);
}
////////////////////////////////////////////////////////////////////
// Function: DisplayRegion::set_qpcamera
// Access: Public
// Description: Sets the camera that is associated with this
// DisplayRegion. There is a one-to-one association
// between cameras and DisplayRegions; if this camera
// was already associated with a different
// DisplayRegion, that association is removed.
//
// The camera is actually set via a NodeChain, which
// clarifies which instance of the camera (if there
// happen to be multiple instances) we should use.
////////////////////////////////////////////////////////////////////
void DisplayRegion::
set_qpcamera(const NodeChain &camera) {
qpCamera *camera_node = (qpCamera *)NULL;
if (!camera.is_empty()) {
DCAST_INTO_V(camera_node, camera.node());
}
if (camera_node != _camera_node) {
if (_camera_node != (qpCamera *)NULL) {
// We need to tell the old camera we're not using him anymore.
_camera_node->remove_display_region(this);
}
_camera_node = camera_node;
if (_camera_node != (qpCamera *)NULL) {
// Now tell the new camera we are using him.
_camera_node->add_display_region(this);
}
}
_qpcamera = camera;
}
////////////////////////////////////////////////////////////////////
// Function: DisplayRegion::output
// Access: Public

View File

@ -17,25 +17,21 @@
////////////////////////////////////////////////////////////////////
#ifndef DISPLAYREGION_H
#define DISPLAYREGION_H
//
////////////////////////////////////////////////////////////////////
// Includes
////////////////////////////////////////////////////////////////////
#include <pandabase.h>
#include <referenceCount.h>
#include <camera.h>
#include "pandabase.h"
#include "referenceCount.h"
#include "camera.h"
#include "nodeChain.h"
#include "plist.h"
////////////////////////////////////////////////////////////////////
// Defines
////////////////////////////////////////////////////////////////////
class GraphicsLayer;
class GraphicsChannel;
class GraphicsWindow;
class GraphicsPipe;
class CullHandler;
class qpCamera;
////////////////////////////////////////////////////////////////////
// Class : DisplayRegion
@ -48,7 +44,12 @@ public:
const float l, const float r,
const float b, const float t);
DisplayRegion(int xsize, int ysize);
virtual ~DisplayRegion();
private:
DisplayRegion(const DisplayRegion &);
void operator = (const DisplayRegion &);
public:
~DisplayRegion();
PUBLISHED:
INLINE void get_dimensions(float &l, float &r, float &b, float &t) const;
@ -66,6 +67,9 @@ PUBLISHED:
void set_camera(Camera *camera);
INLINE Camera *get_camera() const;
void set_qpcamera(const NodeChain &camera);
INLINE const NodeChain &get_qpcamera() const;
INLINE void set_cull_frustum(LensNode *cull_frustum);
INLINE LensNode *get_cull_frustum() const;
@ -98,14 +102,12 @@ protected:
GraphicsLayer *_layer;
PT(Camera) _camera;
NodeChain _qpcamera;
qpCamera *_camera_node;
PT(LensNode) _cull_frustum;
bool _active;
private:
DisplayRegion(const DisplayRegion &);
DisplayRegion& operator=(const DisplayRegion &);
friend class GraphicsLayer;
};

View File

@ -18,6 +18,7 @@
#include "drawCullHandler.h"
#include "geom.h"
#include "transformState.h"
#include "renderState.h"
#include "graphicsStateGuardian.h"
@ -30,7 +31,9 @@
// discovered by the CullTraverser.
////////////////////////////////////////////////////////////////////
void DrawCullHandler::
record_geom(Geom *geom, const RenderState *state) {
record_geom(Geom *geom, const TransformState *transform,
const RenderState *state) {
_gsg->set_transform(transform);
_gsg->set_state(state);
geom->draw(_gsg);
}

View File

@ -40,7 +40,8 @@ public:
INLINE DrawCullHandler(GraphicsStateGuardian *gsg);
// virtual void begin_decal();
virtual void record_geom(Geom *geom, const RenderState *state);
virtual void record_geom(Geom *geom, const TransformState *transform,
const RenderState *state);
// virtual void push_decal();
// virtual void pop_decal();

View File

@ -99,6 +99,7 @@ cull_and_draw_together() {
cull_and_draw_together(win, dr);
}
win->flip();
win->process_events();
}
}
@ -111,19 +112,27 @@ cull_and_draw_together() {
////////////////////////////////////////////////////////////////////
void GraphicsEngine::
cull_and_draw_together(GraphicsWindow *win, DisplayRegion *dr) {
Camera *camera = dr->get_camera();
if (camera == (Camera *)NULL || !camera->is_active()) {
const NodeChain &camera = dr->get_qpcamera();
if (camera.is_empty()) {
// No camera, no draw.
return;
}
Lens *lens = camera->get_lens();
qpCamera *camera_node;
DCAST_INTO_V(camera_node, camera.node());
if (!camera_node->is_active()) {
// Camera inactive, no draw.
return;
}
Lens *lens = camera_node->get_lens();
if (lens == (Lens *)NULL) {
// No lens, no draw.
return;
}
PandaNode *scene = camera->get_qpscene();
PandaNode *scene = camera_node->get_scene();
if (scene == (PandaNode *)NULL) {
// No scene, no draw.
return;
@ -143,9 +152,7 @@ cull_and_draw_together(GraphicsWindow *win, DisplayRegion *dr) {
DrawCullHandler cull_handler(gsg);
qpCullTraverser trav;
trav.set_cull_handler(&cull_handler);
// Here we should figure out the world transform: the camera's
// inverse transform.
trav.set_world_transform(camera.get_rel_transform(NodeChain()));
DisplayRegionStack old_dr = gsg->push_display_region(dr);
gsg->prepare_display_region();

View File

@ -144,6 +144,22 @@ set_state(const RenderState *state) {
_qpstate = _qpstate->issue_delta_set(state, this);
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::set_transform
// Access: Public
// Description: Sets the world transform that will be applied to
// subsequent geometry. This is normally called only
// during the draw process, immediately before issuing
// geometry commands.
////////////////////////////////////////////////////////////////////
INLINE void GraphicsStateGuardian::
set_transform(const TransformState *transform) {
if (transform != _transform) {
_transform = transform;
issue_transform(transform);
}
}
/*
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::get_state

View File

@ -879,6 +879,16 @@ void GraphicsStateGuardian::
end_decal(GeomNode *) {
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::issue_transform
// Access: Public, Virtual
// Description: Sends the indicated transform matrix to the graphics
// API to be applied to future vertices.
////////////////////////////////////////////////////////////////////
void GraphicsStateGuardian::
issue_transform(const TransformState *) {
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::issue_color
// Access: Public, Virtual

View File

@ -35,6 +35,7 @@
#include "renderTraverser.h"
#include "pStatCollector.h"
#include "allTransitionsWrapper.h"
#include "transformState.h"
#include "renderState.h"
#include "notify.h"
@ -142,6 +143,7 @@ public:
INLINE void modify_state(const RenderState *state);
INLINE void set_state(const RenderState *state);
INLINE void set_transform(const TransformState *transform);
RenderBuffer get_render_buffer(int buffer_type);
@ -173,6 +175,7 @@ public:
INLINE void clear_cached_state(void) { _state.clear(); };
virtual void issue_transform(const TransformState *transform);
virtual void issue_color(const ColorAttrib *attrib);
protected:
@ -224,6 +227,7 @@ protected:
State _state;
CPT(RenderState) _qpstate;
CPT(TransformState) _transform;
int _buffer_mask;
Colorf _color_clear_value;

View File

@ -3415,6 +3415,24 @@ issue_polygon_offset(const PolygonOffsetTransition *attrib) {
report_errors();
}
////////////////////////////////////////////////////////////////////
// Function: GLGraphicsStateGuardian::issue_transform
// Access: Public, Virtual
// Description: Sends the indicated transform matrix to the graphics
// API to be applied to future vertices.
////////////////////////////////////////////////////////////////////
void GLGraphicsStateGuardian::
issue_transform(const TransformState *transform) {
#ifdef GSG_VERBOSE
glgsg_cat.debug()
<< "glLoadMatrix(GL_MODELVIEW): " << transform->get_mat() << endl;
#endif
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(transform->get_mat().get_data());
report_errors();
}
////////////////////////////////////////////////////////////////////
// Function: GLGraphicsStateGuardian::issue_texture
// Access: Public, Virtual

View File

@ -156,6 +156,7 @@ public:
virtual void issue_point_shape(const PointShapeTransition *attrib);
virtual void issue_polygon_offset(const PolygonOffsetTransition *attrib);
virtual void issue_transform(const TransformState *transform);
virtual void issue_texture(const TextureAttrib *attrib);
virtual bool wants_normals(void) const;

View File

@ -1217,6 +1217,9 @@ void glxGraphicsWindow::process_events(void)
int got_event;
glxGraphicsWindow* window;
if (_change_mask)
handle_changes();
glxDisplay *glx = _pipe->get_glx_display();
nassertv(glx != (glxDisplay *)NULL);

View File

@ -1,11 +1,12 @@
#define OTHER_LIBS interrogatedb:c dconfig:c dtoolconfig:m \
dtoolutil:c dtoolbase:c dtool:m
#define LOCAL_LIBS gsgbase gobj putil graph linmath express pandabase
#define LOCAL_LIBS event gsgbase gobj putil graph linmath express pandabase
#begin lib_target
#define TARGET pgraph
#define SOURCES \
qpcamera.h qpcamera.I \
colorAttrib.h colorAttrib.I \
config_pgraph.h \
cullHandler.h \
@ -14,6 +15,9 @@
cycleDataReader.h cycleDataReader.I \
cycleDataWriter.h cycleDataWriter.I \
qpgeomNode.h qpgeomNode.I \
qplensNode.h qplensNode.I \
nodeChain.h nodeChain.I \
nodeChainComponent.h nodeChainComponent.I \
pandaNode.h pandaNode.I \
pipeline.h pipeline.I \
pipelineCycler.h pipelineCycler.I \
@ -21,10 +25,11 @@
renderAttrib.h renderAttrib.I \
renderState.h renderState.I \
textureAttrib.h textureAttrib.I \
transformAttrib.h transformAttrib.I
transformState.h transformState.I
#define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx
#define INCLUDED_SOURCES \
qpcamera.cxx \
colorAttrib.cxx \
config_pgraph.cxx \
cullHandler.cxx \
@ -33,6 +38,9 @@
cycleDataReader.cxx \
cycleDataWriter.cxx \
qpgeomNode.cxx \
qplensNode.cxx \
nodeChain.cxx \
nodeChainComponent.cxx \
pandaNode.cxx \
pipeline.cxx \
pipelineCycler.cxx \
@ -40,7 +48,7 @@
renderAttrib.cxx \
renderState.cxx \
textureAttrib.cxx \
transformAttrib.cxx
transformState.cxx
#if $[DONT_COMBINE_PGRAPH]
#define SOURCES $[SOURCES] $[INCLUDED_SOURCES]
@ -49,6 +57,7 @@
#endif
#define INSTALL_HEADERS \
qpcamera.h qpcamera.I \
colorAttrib.h colorAttrib.I \
config_pgraph.h \
cullHandler.h \
@ -57,6 +66,9 @@
cycleDataReader.h cycleDataReader.I \
cycleDataWriter.h cycleDataWriter.I \
qpgeomNode.h qpgeomNode.I \
qplensNode.h qplensNode.I \
nodeChain.h nodeChain.I \
nodeChainComponent.h nodeChainComponent.I \
pandaNode.h pandaNode.I \
pipeline.h pipeline.I \
pipelineCycler.h pipelineCycler.I \
@ -64,7 +76,7 @@
renderAttrib.h renderAttrib.I \
renderState.h renderState.I \
textureAttrib.h textureAttrib.I \
transformAttrib.h transformAttrib.I
transformState.h transformState.I
#define IGATESCAN all

View File

@ -18,13 +18,18 @@
#include "config_pgraph.h"
#include "qpcamera.h"
#include "colorAttrib.h"
#include "qpgeomNode.h"
#include "qplensNode.h"
#include "nodeChain.h"
#include "nodeChainComponent.h"
#include "pandaNode.h"
#include "renderAttrib.h"
#include "renderState.h"
#include "textureAttrib.h"
#include "transformAttrib.h"
#include "colorAttrib.h"
#include "transformState.h"
#include "dconfig.h"
@ -52,18 +57,24 @@ init_libpgraph() {
}
initialized = true;
qpCamera::init_type();
ColorAttrib::init_type();
qpGeomNode::init_type();
qpLensNode::init_type();
NodeChain::init_type();
NodeChainComponent::init_type();
PandaNode::init_type();
RenderAttrib::init_type();
RenderState::init_type();
TextureAttrib::init_type();
TransformAttrib::init_type();
ColorAttrib::init_type();
TransformState::init_type();
ColorAttrib::register_with_read_factory();
qpGeomNode::register_with_read_factory();
PandaNode::register_with_read_factory();
RenderState::register_with_read_factory();
TextureAttrib::register_with_read_factory();
TransformAttrib::register_with_read_factory();
ColorAttrib::register_with_read_factory();
TransformState::register_with_read_factory();
}

View File

@ -18,6 +18,7 @@
#include "cullHandler.h"
#include "geom.h"
#include "transformState.h"
#include "renderState.h"
////////////////////////////////////////////////////////////////////
@ -31,6 +32,7 @@
// it's not intended to be used except for debugging.
////////////////////////////////////////////////////////////////////
void CullHandler::
record_geom(Geom *geom, const RenderState *state) {
cerr << *geom << " " << *state << "\n";
record_geom(Geom *geom, const TransformState *transform,
const RenderState *state) {
cerr << *geom << " " << *transform << " " << *state << "\n";
}

View File

@ -22,6 +22,7 @@
#include "pandabase.h"
class Geom;
class TransformState;
class RenderState;
////////////////////////////////////////////////////////////////////
@ -34,7 +35,8 @@ class RenderState;
class EXPCL_PANDA CullHandler {
public:
// virtual void begin_decal();
virtual void record_geom(Geom *geom, const RenderState *state);
virtual void record_geom(Geom *geom, const TransformState *transform,
const RenderState *state);
// virtual void push_decal();
// virtual void pop_decal();
};

View File

@ -27,7 +27,7 @@ INLINE CycleDataReader<CycleDataType>::
CycleDataReader(const PipelineCycler<CycleDataType> &cycler) :
_cycler(cycler)
{
_pointer = (const CycleDataType *)_cycler.read();
_pointer = _cycler.read();
}
////////////////////////////////////////////////////////////////////

View File

@ -27,7 +27,7 @@ INLINE CycleDataWriter<CycleDataType>::
CycleDataWriter(PipelineCycler<CycleDataType> &cycler) :
_cycler(cycler)
{
_pointer = (CycleDataType *)_cycler.write();
_pointer = _cycler.write();
}
////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,323 @@
// Filename: nodeChain.I
// Created by: drose (25Feb02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: NodeChain::Default Constructor
// Access: Published
// Description: This constructs an empty NodeChain with no nodes. It
// cannot be extended, since you cannot add nodes without
// first specifying the top node. Use the constructor
// that receives a node if you ever want to do anything
// with this chain.
////////////////////////////////////////////////////////////////////
INLINE NodeChain::
NodeChain() :
_error_type(ET_ok)
{
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::Constructor
// Access: Published
// Description: This constructs an empty NodeChain with a single node.
// This chain may now be extended by repeatedly calling
// push_back() with each node below that node in
// sequence.
//
// If the Node pointer is NULL, this quietly creates an
// empty NodeChain.
////////////////////////////////////////////////////////////////////
INLINE NodeChain::
NodeChain(PandaNode *top_node) :
_error_type(ET_ok)
{
if (top_node != (PandaNode *)NULL) {
_head = top_node->get_generic_component();
}
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::Copy Constructor
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
INLINE NodeChain::
NodeChain(const NodeChain &copy) :
_head(copy._head),
_error_type(copy._error_type)
{
uncollapse_head();
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::Copy Assignment Operator
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
INLINE void NodeChain::
operator = (const NodeChain &copy) {
_head = copy._head;
_error_type = copy._error_type;
uncollapse_head();
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::not_found named constructor
// Access: Published, Static
// Description: Creates a NodeChain with the ET_not_found error type
// set.
////////////////////////////////////////////////////////////////////
INLINE NodeChain NodeChain::
not_found() {
NodeChain result;
result._error_type = ET_not_found;
return result;
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::removed named constructor
// Access: Published, Static
// Description: Creates a NodeChain with the ET_removed error type
// set.
////////////////////////////////////////////////////////////////////
INLINE NodeChain NodeChain::
removed() {
NodeChain result;
result._error_type = ET_removed;
return result;
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::fail named constructor
// Access: Published, Static
// Description: Creates a NodeChain with the ET_fail error type
// set.
////////////////////////////////////////////////////////////////////
INLINE NodeChain NodeChain::
fail() {
NodeChain result;
result._error_type = ET_fail;
return result;
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::is_empty
// Access: Published
// Description: Returns true if the NodeChain contains no nodes.
////////////////////////////////////////////////////////////////////
INLINE bool NodeChain::
is_empty() const {
return (_head == (NodeChainComponent *)NULL);
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::is_singleton
// Access: Published
// Description: Returns true if the NodeChain contains exactly one
// node.
////////////////////////////////////////////////////////////////////
INLINE bool NodeChain::
is_singleton() const {
uncollapse_head();
return (_head != (NodeChainComponent *)NULL && _head->is_top_node());
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::get_error_type
// Access: Published
// Description: If is_empty() is true, this returns a code that
// represents the reason why the NodeChain is empty.
////////////////////////////////////////////////////////////////////
INLINE NodeChain::ErrorType NodeChain::
get_error_type() const {
return _error_type;
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::node
// Access: Published
// Description: Returns the bottom node of the path, or NULL if the
// path is empty.
////////////////////////////////////////////////////////////////////
INLINE PandaNode *NodeChain::
node() const {
if (is_empty()) {
return (PandaNode *)NULL;
}
return _head->get_node();
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::has_parent
// Access: Published
// Description: Returns true if the node at the bottom of the
// NodeChain has a parent; i.e. the NodeChain contains at
// least two nodes.
////////////////////////////////////////////////////////////////////
INLINE bool NodeChain::
has_parent() const {
return !is_empty() && !is_singleton();
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::get_parent
// Access: Published
// Description: Returns the NodeChain to the parent of the bottom
// node: that is, this NodeChain, shortened by one node.
////////////////////////////////////////////////////////////////////
INLINE NodeChain NodeChain::
get_parent() const {
nassertr(has_parent(), NodeChain::fail());
NodeChain parent(*this);
parent._head = parent._head->get_next();
return parent;
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::attach_new_node
// Access: Published
// Description: Creates an ordinary PandaNode and attaches it below
// the current NodeChain, returning a new NodeChain that
// references it.
////////////////////////////////////////////////////////////////////
INLINE NodeChain NodeChain::
attach_new_node(const string &name, int sort) const {
nassertr(verify_complete(), NodeChain::fail());
nassertr(!is_empty(), *this);
return attach_new_node(new PandaNode(name), sort);
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::get_net_state
// Access: Published
// Description: Returns the net state on this node from the root.
////////////////////////////////////////////////////////////////////
INLINE CPT(RenderState) NodeChain::
get_net_state() const {
uncollapse_head();
return r_get_net_state(_head);
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::get_net_transform
// Access: Published
// Description: Returns the net transform on this node from the root.
////////////////////////////////////////////////////////////////////
INLINE CPT(TransformState) NodeChain::
get_net_transform() const {
uncollapse_head();
return r_get_net_transform(_head);
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::get_mat
// Access: Published
// Description: Returns the matrix that describes the coordinate
// space of this node, relative to the other
// node's coordinate space. If either NodeChain is
// empty, it is taken to indicate the top of the graph.
////////////////////////////////////////////////////////////////////
INLINE const LMatrix4f &NodeChain::
get_mat(const NodeChain &other) const {
CPT(TransformState) transform = get_rel_transform(other);
// We can safely assume the transform won't go away when the
// function returns, since its reference count is also held in the
// cache. This assumption allows us to return a reference to the
// matrix, instead of having to return a matrix on the stack.
nassertr(transform->get_ref_count() > 1, LMatrix4f::ident_mat());
return transform->get_mat();
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::operator ==
// Access: Published
// Description: Returns true if the two chains are equivalent; that
// is, if they contain the same list of nodes in the same
// order.
////////////////////////////////////////////////////////////////////
INLINE bool NodeChain::
operator == (const NodeChain &other) const {
return (compare_to(other) == 0);
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::operator !=
// Access: Published
// Description: Returns true if the two chains are not equivalent.
////////////////////////////////////////////////////////////////////
INLINE bool NodeChain::
operator != (const NodeChain &other) const {
return (compare_to(other) != 0);
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::operator <
// Access: Published
// Description: Returns true if this NodeChain sorts before the other
// one, false otherwise. The sorting order of two
// nonequivalent NodeChains is consistent but undefined,
// and is useful only for storing NodeChains in a sorted
// container like an STL set.
////////////////////////////////////////////////////////////////////
INLINE bool NodeChain::
operator < (const NodeChain &other) const {
return (compare_to(other) < 0);
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::compare_to
// Access: Published
// Description: Returns a number less than zero if this NodeChain
// sorts before the other one, greater than zero if it
// sorts after, or zero if they are equivalent.
//
// Two NodeChains are considered equivalent if they
// consist of exactly the same list of nodes in the same
// order. Otherwise, they are different; different
// NodeChains will be ranked in a consistent but
// undefined ordering; the ordering is useful only for
// placing the NodeChains in a sorted container like an
// STL set.
////////////////////////////////////////////////////////////////////
INLINE int NodeChain::
compare_to(const NodeChain &other) const {
return r_compare_to(_head, other._head);
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::output
// Access: Published
// Description: Writes a sensible description of the NodeChain to the
// indicated output stream.
////////////////////////////////////////////////////////////////////
INLINE void NodeChain::
output(ostream &out) const {
if (_head == (NodeChainComponent *)NULL) {
out << "(empty)";
} else {
r_output(out, _head);
}
}
INLINE ostream &operator << (ostream &out, const NodeChain &node_chain) {
node_chain.output(out);
return out;
}

View File

@ -0,0 +1,514 @@
// Filename: nodeChain.cxx
// Created by: drose (25Feb02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#include "nodeChain.h"
#include "node.h"
#include "namedNode.h"
#include "config_pgraph.h"
#include "plist.h"
TypeHandle NodeChain::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: NodeChain::get_num_nodes
// Access: Published
// Description: Returns the number of nodes in the path.
////////////////////////////////////////////////////////////////////
int NodeChain::
get_num_nodes() const {
if (is_empty()) {
return 0;
}
uncollapse_head();
return _head->get_length();
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::get_node
// Access: Published
// Description: Returns the nth node of the path, where 0 is the
// bottom node and get_num_nodes() - 1 is the top node.
// This requires iterating through the path.
////////////////////////////////////////////////////////////////////
PandaNode *NodeChain::
get_node(int index) const {
nassertr(index >= 0 && index < get_num_nodes(), NULL);
uncollapse_head();
NodeChainComponent *comp = _head;
while (index > 0) {
// If this assertion fails, the index was out of range; the
// component's length must have been invalid.
nassertr(comp != (NodeChainComponent *)NULL, NULL);
comp = comp->get_next();
index--;
}
// If this assertion fails, the index was out of range; the
// component's length must have been invalid.
nassertr(comp != (NodeChainComponent *)NULL, NULL);
return comp->get_node();
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::get_top_node
// Access: Published
// Description: Returns the top node of the path, or NULL if the path
// is empty. This requires iterating through the path.
////////////////////////////////////////////////////////////////////
PandaNode *NodeChain::
get_top_node() const {
if (is_empty()) {
return (PandaNode *)NULL;
}
uncollapse_head();
NodeChainComponent *comp = _head;
while (!comp->is_top_node()) {
comp = comp->get_next();
nassertr(comp != (NodeChainComponent *)NULL, NULL);
}
return comp->get_node();
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::reparent_to
// Access: Published
// Description: Removes the bottom node of the NodeChain from its
// current parent and attaches it to the bottom node of
// the indicated NodeChain.
////////////////////////////////////////////////////////////////////
void NodeChain::
reparent_to(const NodeChain &other, int sort) {
nassertv(other.verify_complete());
nassertv_always(!is_empty());
nassertv_always(!other.is_empty());
uncollapse_head();
other.uncollapse_head();
PandaNode::reparent(other._head, _head, sort);
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::wrt_reparent_to
// Access: Published
// Description: This functions identically to reparent_to(), except
// the transform on this node is also adjusted so that
// the node remains in the same place in world
// coordinates, even if it is reparented into a
// different coordinate system.
////////////////////////////////////////////////////////////////////
void NodeChain::
wrt_reparent_to(const NodeChain &other, int sort) {
nassertv(other.verify_complete());
nassertv_always(!is_empty());
nassertv_always(!other.is_empty());
//****
/*
LMatrix4f mat = get_mat(other);
set_mat(mat);
*/
reparent_to(other, sort);
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::instance_to
// Access: Published
// Description: Adds the bottom node of the NodeChain as a child of
// the bottom node of the indicated other NodeChain. Any
// other parent-child relations of the node are
// unchanged; in particular, the node is not removed
// from its existing parent, if any.
//
// If the node already had an existing parent, this
// method will create a new instance of the node within
// the scene graph.
//
// This does not change the NodeChain itself, but does
// return a new NodeChain that reflects the new instance
// node.
////////////////////////////////////////////////////////////////////
NodeChain NodeChain::
instance_to(const NodeChain &other, int sort) const {
nassertr(verify_complete(), NodeChain::fail());
nassertr(!is_empty(), NodeChain::fail());
nassertr(!other.is_empty(), NodeChain::fail());
uncollapse_head();
other.uncollapse_head();
NodeChain new_instance;
new_instance._head = PandaNode::attach(other._head, node(), sort);
return new_instance;
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::copy_to
// Access: Published
// Description: Functions exactly like instance_to(), except a deep
// copy is made of the bottom node and all of its
// descendents, which is then parented to the indicated
// node. A NodeChain to the newly created copy is
// returned.
//
// Certain kinds of nodes may not be copied; if one of
// these is encountered, the node will be "copied" as
// the nearest copyable base class. For instance, a
// Camera node in the graph will become a simple
// NamedNode.
////////////////////////////////////////////////////////////////////
NodeChain NodeChain::
copy_to(const NodeChain &other, int sort) const {
//*****
return instance_to(other, sort);
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::attach_new_node
// Access: Published
// Description: Attaches a new node, with or without existing
// parents, to the scene graph below the bottom node of
// this NodeChain. This is the preferred way to add
// nodes to the graph.
//
// This does *not* automatically extend the current
// NodeChain to reflect the attachment; however, a
// NodeChain that does reflect this extension is
// returned.
////////////////////////////////////////////////////////////////////
NodeChain NodeChain::
attach_new_node(PandaNode *node, int sort) const {
nassertr(verify_complete(), NodeChain::fail());
nassertr(!is_empty(), NodeChain());
nassertr(node != (PandaNode *)NULL, NodeChain());
uncollapse_head();
NodeChain new_path(*this);
new_path._head = PandaNode::attach(_head, node, sort);
return new_path;
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::remove_node
// Access: Published
// Description: Disconnects the bottom node from the scene graph.
// This will also delete the node if there are no other
// pointers to it.
//
// Normally, this should be called only when you are
// really done with the node. If you want to remove a
// node from the scene graph but keep it around for
// later, you should probably use reparent_to() and put
// it under a holding node instead.
//
// After the node is removed, the NodeChain will have
// been cleared.
////////////////////////////////////////////////////////////////////
void NodeChain::
remove_node() {
nassertv(_error_type != ET_not_found);
if (is_empty()) {
// If we have no arcs (maybe we were already removed), quietly do
// nothing except to ensure the NodeChain is clear.
(*this) = NodeChain::removed();
return;
}
uncollapse_head();
PandaNode::detach(_head);
(*this) = NodeChain::removed();
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::get_rel_state
// Access: Published
// Description: Returns the state changes that must be made to
// transition from the render state of this node to the
// render state of the other node.
////////////////////////////////////////////////////////////////////
CPT(RenderState) NodeChain::
get_rel_state(const NodeChain &other) const {
if (is_empty()) {
return other.get_net_state();
}
if (other.is_empty()) {
return get_net_state()->invert_compose(RenderState::make_empty());
}
nassertr(verify_complete(), RenderState::make_empty());
nassertr(other.verify_complete(), RenderState::make_empty());
int a_count, b_count;
find_common_ancestor(*this, other, a_count, b_count);
CPT(RenderState) a_state = r_get_partial_state(_head, a_count);
CPT(RenderState) b_state = r_get_partial_state(other._head, b_count);
return a_state->invert_compose(b_state);
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::get_rel_transform
// Access: Published
// Description: Returns the relative transform from this node to the
// other node; i.e. the transformation of the other node
// as seen from this node.
////////////////////////////////////////////////////////////////////
CPT(TransformState) NodeChain::
get_rel_transform(const NodeChain &other) const {
if (is_empty()) {
return other.get_net_transform();
}
if (other.is_empty()) {
return get_net_transform()->invert_compose(TransformState::make_identity());
}
nassertr(verify_complete(), TransformState::make_identity());
nassertr(other.verify_complete(), TransformState::make_identity());
int a_count, b_count;
find_common_ancestor(*this, other, a_count, b_count);
CPT(TransformState) a_transform = r_get_partial_transform(_head, a_count);
CPT(TransformState) b_transform = r_get_partial_transform(other._head, b_count);
return a_transform->invert_compose(b_transform);
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::verify_complete
// Access: Published
// Description: Returns true if all of the nodes described in the
// NodeChain are connected *and* the top node is the top
// of the graph, or false otherwise.
////////////////////////////////////////////////////////////////////
bool NodeChain::
verify_complete() const {
if (is_empty()) {
return true;
}
uncollapse_head();
const NodeChainComponent *comp = _head;
nassertr(comp != (const NodeChainComponent *)NULL, false);
PandaNode *node = comp->get_node();
nassertr(node != (const PandaNode *)NULL, false);
int length = comp->get_length();
comp = comp->get_next();
length--;
while (comp != (const NodeChainComponent *)NULL) {
PandaNode *next_node = comp->get_node();
nassertr(next_node != (const PandaNode *)NULL, false);
if (next_node->find_child(node) < 0) {
return false;
}
if (comp->get_length() != length) {
return false;
}
node = next_node;
comp = comp->get_next();
length--;
}
// Now that we've reached the top, we should have no parents.
return length == 0 && node->get_num_parents() == 0;
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::uncollapse_head
// Access: Private
// Description: Quietly and transparently uncollapses the _head
// pointer if it needs it.
////////////////////////////////////////////////////////////////////
void NodeChain::
uncollapse_head() const {
if (_head != (NodeChainComponent *)NULL && _head->is_collapsed()) {
((NodeChain *)this)->_head = _head->uncollapse();
}
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::find_common_ancestor
// Access: Private, Static
// Description: Walks up from both NodeChains to find the first node
// that both have in common, if any. Fills a_count and
// b_count with the number of nodes below the common
// node in each chain.
////////////////////////////////////////////////////////////////////
void NodeChain::
find_common_ancestor(const NodeChain &a, const NodeChain &b,
int &a_count, int &b_count) {
nassertv(!a.is_empty() && !b.is_empty());
a.uncollapse_head();
b.uncollapse_head();
NodeChainComponent *ac = a._head;
NodeChainComponent *bc = b._head;
a_count = 0;
b_count = 0;
// Shorten up the longer one until they are the same length.
while (ac->get_length() > bc->get_length()) {
nassertv(ac != (NodeChainComponent *)NULL);
ac = ac->get_next();
a_count++;
}
while (bc->get_length() > ac->get_length()) {
nassertv(bc != (NodeChainComponent *)NULL);
bc = bc->get_next();
b_count++;
}
// Now shorten them both up until we reach the same component.
while (ac != bc) {
// These shouldn't go to NULL unless they both go there together.
nassertv(ac != (NodeChainComponent *)NULL);
nassertv(bc != (NodeChainComponent *)NULL);
ac = ac->get_next();
a_count++;
bc = bc->get_next();
b_count++;
}
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::r_get_net_state
// Access: Private
// Description: Recursively determines the net state chnages to the
// indicated component node from the root of the graph.
////////////////////////////////////////////////////////////////////
CPT(RenderState) NodeChain::
r_get_net_state(NodeChainComponent *comp) const {
if (comp == (NodeChainComponent *)NULL) {
return RenderState::make_empty();
} else {
CPT(RenderState) state = comp->get_node()->get_state();
return r_get_net_state(comp->get_next())->compose(state);
}
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::r_get_partial_state
// Access: Private
// Description: Recursively determines the net state changes to the
// indicated component node from the nth node above it.
////////////////////////////////////////////////////////////////////
CPT(RenderState) NodeChain::
r_get_partial_state(NodeChainComponent *comp, int n) const {
nassertr(comp != (NodeChainComponent *)NULL, RenderState::make_empty());
if (n == 0) {
return RenderState::make_empty();
} else {
CPT(RenderState) state = comp->get_node()->get_state();
return r_get_partial_state(comp->get_next(), n - 1)->compose(state);
}
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::r_get_net_transform
// Access: Private
// Description: Recursively determines the net transform to the
// indicated component node from the root of the graph.
////////////////////////////////////////////////////////////////////
CPT(TransformState) NodeChain::
r_get_net_transform(NodeChainComponent *comp) const {
if (comp == (NodeChainComponent *)NULL) {
return TransformState::make_identity();
} else {
CPT(TransformState) transform = comp->get_node()->get_transform();
return r_get_net_transform(comp->get_next())->compose(transform);
}
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::r_get_partial_transform
// Access: Private
// Description: Recursively determines the net transform to the
// indicated component node from the nth node above it.
////////////////////////////////////////////////////////////////////
CPT(TransformState) NodeChain::
r_get_partial_transform(NodeChainComponent *comp, int n) const {
nassertr(comp != (NodeChainComponent *)NULL, TransformState::make_identity());
if (n == 0) {
return TransformState::make_identity();
} else {
CPT(TransformState) transform = comp->get_node()->get_transform();
return r_get_partial_transform(comp->get_next(), n - 1)->compose(transform);
}
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::r_output
// Access: Private
// Description: The recursive implementation of output(), this writes
// the names of each node component in order from
// beginning to end, by first walking to the end of the
// linked list and then outputting from there.
////////////////////////////////////////////////////////////////////
void NodeChain::
r_output(ostream &out, NodeChainComponent *comp) const {
NodeChainComponent *next = comp->get_next();
if (next != (NodeChainComponent *)NULL) {
// This is not the head of the list; keep going up.
r_output(out, next);
out << "/";
}
// Now output this component.
PandaNode *node = comp->get_node();
if (node->has_name()) {
out << node->get_name();
} else {
out << "+" << node->get_type();
}
out << "[" << comp->get_length() << "]";
}
////////////////////////////////////////////////////////////////////
// Function: NodeChain::r_compare_to
// Access: Private, Static
// Description: The recursive implementation of compare_to(). Returns
// < 0 if a sorts before b, > 0 if b sorts before a, or
// == 0 if they are equivalent.
////////////////////////////////////////////////////////////////////
int NodeChain::
r_compare_to(const NodeChainComponent *a, const NodeChainComponent *b) {
if (a == b) {
return 0;
} else if (a == (const NodeChainComponent *)NULL) {
return -1;
} else if (b == (const NodeChainComponent *)NULL) {
return 1;
} else if (a->get_node() != b->get_node()) {
return a->get_node() - b->get_node();
} else {
return r_compare_to(a->get_next(), b->get_next());
}
}

View File

@ -0,0 +1,176 @@
// Filename: nodeChain.h
// Created by: drose (25Feb02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#ifndef NODECHAIN_H
#define NODECHAIN_H
#include "pandabase.h"
#include "pandaNode.h"
#include "renderState.h"
#include "transformState.h"
#include "nodeChainComponent.h"
#include "pointerTo.h"
#include "referenceCount.h"
#include "notify.h"
#include "typedObject.h"
////////////////////////////////////////////////////////////////////
// Class : NodeChain
// Description : A NodeChain is the fundamental system for
// disambiguating instances and manipulating the scene
// graph. NodeChain is the base class of NodePath,
// which adds a bit more high-level functionality, but
// the fundamental scene graph manipulations are defined
// at the NodeChain level.
//
// A NodeChain is a list of connected nodes from the
// root of the graph to any sub-node. Each NodeChain
// therefore unqiuely describes one instance of a node.
//
// NodeChains themselves are lightweight objects that
// may easily be copied and passed by value. Their data
// is stored as a series of NodeChainComponents that are
// stored on the nodes. Holding a NodeChain will keep a
// reference count to all the nodes in the chain.
// However, if any node in the chain is removed or
// reparented (perhaps through a different NodeChain),
// the NodeChain will automatically be updated to
// reflect the changes.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA NodeChain {
PUBLISHED:
// This enumeration is returned by get_error_type() for an empty
// NodeChain to report the reason it's empty.
enum ErrorType {
ET_ok = 0, // i.e. not empty, or never assigned to anything.
ET_not_found, // returned from a failed find() or similar function.
ET_removed, // remove_node() was previously called on this NodeChain.
ET_fail, // general failure return from some function.
};
INLINE NodeChain();
INLINE NodeChain(PandaNode *top_node);
INLINE NodeChain(const NodeChain &copy);
INLINE void operator = (const NodeChain &copy);
INLINE static NodeChain not_found();
INLINE static NodeChain removed();
INLINE static NodeChain fail();
// Methods to query a NodeChain's contents.
INLINE bool is_empty() const;
INLINE bool is_singleton() const;
int get_num_nodes() const;
PandaNode *get_node(int index) const;
INLINE ErrorType get_error_type() const;
PandaNode *get_top_node() const;
INLINE PandaNode *node() const;
// Methods that return collections of NodePaths derived from or
// related to this one.
/*
NodeChainCollection get_siblings() const;
NodeChainCollection get_children() const;
INLINE int get_num_children() const;
INLINE NodeChain get_child(int n) const;
*/
INLINE bool has_parent() const;
INLINE NodeChain get_parent() const;
/*
INLINE NodeChain find_path_down_to(Node *dnode) const;
INLINE NodeChain find(const string &chain) const;
NodeChainCollection
find_all_paths_down_to(Node *dnode) const;
NodeChainCollection
find_all_matches(const string &chain) const;
*/
// Methods that actually move nodes around in the scene graph. The
// optional "sort" parameter can be used to force a particular
// ordering between sibling nodes, useful when dealing with LOD's
// and similar switch nodes. If the sort value is the same, nodes
// will be arranged in the order they were added.
void reparent_to(const NodeChain &other, int sort = 0);
void wrt_reparent_to(const NodeChain &other, int sort = 0);
NodeChain instance_to(const NodeChain &other, int sort = 0) const;
NodeChain copy_to(const NodeChain &other, int sort = 0) const;
NodeChain attach_new_node(PandaNode *node, int sort = 0) const;
INLINE NodeChain attach_new_node(const string &name, int sort = 0) const;
void remove_node();
// Relative transform and state changes between nodes.
CPT(RenderState) get_rel_state(const NodeChain &other) const;
INLINE CPT(RenderState) get_net_state() const;
CPT(TransformState) get_rel_transform(const NodeChain &other) const;
INLINE CPT(TransformState) get_net_transform() const;
INLINE const LMatrix4f &get_mat(const NodeChain &other) const;
bool verify_complete() const;
public:
INLINE bool operator == (const NodeChain &other) const;
INLINE bool operator != (const NodeChain &other) const;
INLINE bool operator < (const NodeChain &other) const;
INLINE int compare_to(const NodeChain &other) const;
INLINE void output(ostream &out) const;
private:
void uncollapse_head() const;
static void find_common_ancestor(const NodeChain &a, const NodeChain &b,
int &a_count, int &b_count);
CPT(RenderState) r_get_net_state(NodeChainComponent *comp) const;
CPT(RenderState) r_get_partial_state(NodeChainComponent *comp, int n) const;
CPT(TransformState) r_get_net_transform(NodeChainComponent *comp) const;
CPT(TransformState) r_get_partial_transform(NodeChainComponent *comp, int n) const;
void r_output(ostream &out, NodeChainComponent *comp) const;
static int r_compare_to(const NodeChainComponent *a, const NodeChainComponent *v);
PT(NodeChainComponent) _head;
ErrorType _error_type;
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
register_type(_type_handle, "NodeChain");
}
private:
static TypeHandle _type_handle;
};
INLINE ostream &operator << (ostream &out, const NodeChain &node_chain);
#include "nodeChain.I"
#endif

View File

@ -0,0 +1,203 @@
// Filename: nodeChainComponent.I
// Created by: drose (25Feb02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: NodeChainComponent::CData::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE NodeChainComponent::CData::
CData() {
_length = 1;
}
////////////////////////////////////////////////////////////////////
// Function: NodeChainComponent::CData::Copy Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE NodeChainComponent::CData::
CData(const NodeChainComponent::CData &copy) :
_next(copy._next),
_length(copy._length)
{
}
////////////////////////////////////////////////////////////////////
// Function: NodeChainComponent::Constructor
// Access: Private
// Description: Constructs a new NodeChainComponent from the
// indicated node. Don't try to call this directly; ask
// the PandaNode to do it for you.
////////////////////////////////////////////////////////////////////
INLINE NodeChainComponent::
NodeChainComponent(PandaNode *node, NodeChainComponent *next) :
_node(node)
{
#ifdef DO_MEMORY_USAGE
MemoryUsage::update_type(this, get_class_type());
#endif
CDWriter cdata(_cycler);
cdata->_next = next;
if (next != (NodeChainComponent *)NULL) {
cdata->_length = next->get_length() + 1;
}
}
////////////////////////////////////////////////////////////////////
// Function: NodeChainComponent::Copy Constructor
// Access: Private
// Description: NodeChainComponents should not be copied.
////////////////////////////////////////////////////////////////////
INLINE NodeChainComponent::
NodeChainComponent(const NodeChainComponent &copy) {
nassertv(false);
}
////////////////////////////////////////////////////////////////////
// Function: NodeChainComponent::Copy Assignment Operator
// Access: Private
// Description: NodeChainComponents should not be copied.
////////////////////////////////////////////////////////////////////
INLINE void NodeChainComponent::
operator = (const NodeChainComponent &copy) {
nassertv(false);
}
////////////////////////////////////////////////////////////////////
// Function: NodeChainComponent::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE NodeChainComponent::
~NodeChainComponent() {
nassertv(_node != (PandaNode *)NULL);
_node->delete_component(this);
}
////////////////////////////////////////////////////////////////////
// Function: NodeChainComponent::get_node
// Access: Public
// Description: Returns the node referenced by this component.
////////////////////////////////////////////////////////////////////
INLINE PandaNode *NodeChainComponent::
get_node() const {
return _node;
}
////////////////////////////////////////////////////////////////////
// Function: NodeChainComponent::is_top_node
// Access: Public
// Description: Returns true if this component represents the top
// node in the chain.
////////////////////////////////////////////////////////////////////
INLINE bool NodeChainComponent::
is_top_node() const {
CDReader cdata(_cycler);
return (cdata->_next == (NodeChainComponent *)NULL);
}
////////////////////////////////////////////////////////////////////
// Function: NodeChainComponent::is_collapsed
// Access: Public
// Description: Returns true if this component has been collapsed
// with another component. In this case, the component
// itself is invalid, and the collapsed component should
// be used instead.
////////////////////////////////////////////////////////////////////
INLINE bool NodeChainComponent::
is_collapsed() const {
CDReader cdata(_cycler);
return (cdata->_length == 0);
}
////////////////////////////////////////////////////////////////////
// Function: NodeChainComponent::get_length
// Access: Public
// Description: Returns the length of the chain.
////////////////////////////////////////////////////////////////////
INLINE int NodeChainComponent::
get_length() const {
CDReader cdata(_cycler);
nassertr(!is_collapsed(), 0);
return cdata->_length;
}
////////////////////////////////////////////////////////////////////
// Function: NodeChainComponent::get_collapsed
// Access: Public
// Description: If is_collapsed() returns true, this is the component
// that this one has been collapsed with, and should be
// replaced with.
////////////////////////////////////////////////////////////////////
INLINE NodeChainComponent *NodeChainComponent::
get_collapsed() const {
CDReader cdata(_cycler);
nassertr(is_collapsed(), (NodeChainComponent *)NULL);
return cdata->_next;
}
////////////////////////////////////////////////////////////////////
// Function: NodeChainComponent::set_next
// Access: Private
// Description: Sets the next pointer in the chain.
////////////////////////////////////////////////////////////////////
INLINE void NodeChainComponent::
set_next(NodeChainComponent *next) {
CDWriter cdata(_cycler);
nassertv(!is_collapsed());
nassertv(next != (NodeChainComponent *)NULL);
cdata->_next = next;
}
////////////////////////////////////////////////////////////////////
// Function: NodeChainComponent::set_top_node
// Access: Private
// Description: Severs any connection to the next pointer in the
// chain and makes this component a top node.
////////////////////////////////////////////////////////////////////
INLINE void NodeChainComponent::
set_top_node() {
CDWriter cdata(_cycler);
nassertv(!is_collapsed());
cdata->_next = (NodeChainComponent *)NULL;
}
////////////////////////////////////////////////////////////////////
// Function: NodeChainComponent::collapse_with
// Access: Private
// Description: Indicates that this component pointer is no longer
// valid, and that the indicated component should be
// used instead. This is done whenever two
// NodeChainComponents have been collapsed together due
// to an instance being removed higher up in the graph.
////////////////////////////////////////////////////////////////////
INLINE void NodeChainComponent::
collapse_with(NodeChainComponent *next) {
CDWriter cdata(_cycler);
nassertv(!is_collapsed());
nassertv(next != (NodeChainComponent *)NULL);
cdata->_next = next;
cdata->_length = 0;
// We indicate a component has been collapsed by setting its length
// to zero.
}

View File

@ -0,0 +1,102 @@
// Filename: nodeChainComponent.cxx
// Created by: drose (25Feb02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#include "nodeChainComponent.h"
TypeHandle NodeChainComponent::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: NodeChainComponent::CData::make_copy
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
CycleData *NodeChainComponent::CData::
make_copy() const {
return new CData(*this);
}
////////////////////////////////////////////////////////////////////
// Function: NodeChainComponent::get_next
// Access: Public
// Description: Returns the next component in the chain.
////////////////////////////////////////////////////////////////////
NodeChainComponent *NodeChainComponent::
get_next() const {
CDReader cdata(_cycler);
nassertr(!is_collapsed(), (NodeChainComponent *)NULL);
NodeChainComponent *next = cdata->_next;
// If the next component has been collapsed, transparently update
// the pointer to get the actual node, and store the new pointer,
// before we return. Collapsing can happen at any time to any
// component in the chain and we have to deal with it.
if (next != (NodeChainComponent *)NULL && next->is_collapsed()) {
next = next->uncollapse();
((NodeChainComponent *)this)->set_next(next);
}
return next;
}
////////////////////////////////////////////////////////////////////
// Function: NodeChainComponent::fix_length
// Access: Public
// Description: Checks that the length indicated by the component is
// one more than the length of its predecessor. If this
// is broken, fixes it and returns true indicating the
// component has been changed; otherwise, returns false.
////////////////////////////////////////////////////////////////////
bool NodeChainComponent::
fix_length() {
int length_should_be = 1;
if (!is_top_node()) {
length_should_be = get_next()->get_length() + 1;
}
if (get_length() == length_should_be) {
return false;
}
CDWriter cdata(_cycler);
cdata->_length = length_should_be;
return true;
}
////////////////////////////////////////////////////////////////////
// Function: NodeChainComponent::uncollapse
// Access: Public
// Description: Returns this component pointer if the component is
// not collapsed; or if it has been collapsed, returns
// the pointer it has been collapsed into.
//
// Collapsing can happen at any time to any component in
// the chain and we have to deal with it. It happens
// when a node is removed further up the chain that
// results in two instances becoming the same thing.
////////////////////////////////////////////////////////////////////
NodeChainComponent *NodeChainComponent::
uncollapse() {
NodeChainComponent *comp = this;
while (comp->is_collapsed()) {
comp = comp->get_collapsed();
}
return comp;
}

View File

@ -0,0 +1,106 @@
// Filename: nodeChainComponent.h
// Created by: drose (25Feb02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#ifndef NODECHAINCOMPONENT_H
#define NODECHAINCOMPONENT_H
#include "pandabase.h"
#include "pandaNode.h"
#include "pointerTo.h"
#include "referenceCount.h"
#include "cycleData.h"
#include "cycleDataReader.h"
#include "cycleDataWriter.h"
////////////////////////////////////////////////////////////////////
// Class : NodeChainComponent
// Description : This is one component of a NodeChain. These are
// stored on each PandaNode, as many as one for each of
// the possible instances of the node (but they only
// exist when they are requested, to minimize memory
// waste). A NodeChain represents a singly-linked list
// of these from an arbitrary component in the graph to
// the root.
//
// This whole NodeChain system is used to disambiguate
// instances in the scene graph, and the
// NodeChainComponents are stored in the nodes
// themselves to allow the nodes to keep these up to
// date as the scene graph is manipulated.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA NodeChainComponent : public ReferenceCount {
private:
INLINE NodeChainComponent(PandaNode *node, NodeChainComponent *next = NULL);
INLINE NodeChainComponent(const NodeChainComponent &copy);
INLINE void operator = (const NodeChainComponent &copy);
public:
INLINE ~NodeChainComponent();
INLINE PandaNode *get_node() const;
INLINE bool is_top_node() const;
INLINE bool is_collapsed() const;
NodeChainComponent *get_next() const;
INLINE int get_length() const;
INLINE NodeChainComponent *get_collapsed() const;
bool fix_length();
NodeChainComponent *uncollapse();
private:
INLINE void set_next(NodeChainComponent *next);
INLINE void set_top_node();
INLINE void collapse_with(NodeChainComponent *next);
PT(PandaNode) _node;
// This is the data that must be cycled between pipeline stages.
class EXPCL_PANDA CData : public CycleData {
public:
INLINE CData();
CData(const CData &copy);
virtual CycleData *make_copy() const;
PT(NodeChainComponent) _next;
int _length;
};
PipelineCycler<CData> _cycler;
typedef CycleDataReader<CData> CDReader;
typedef CycleDataWriter<CData> CDWriter;
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
ReferenceCount::init_type();
register_type(_type_handle, "NodeChainComponent",
ReferenceCount::get_class_type());
}
private:
static TypeHandle _type_handle;
friend class PandaNode;
};
#include "nodeChainComponent.I"
#endif

View File

@ -62,6 +62,42 @@ get_sort() const {
return _sort;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::UpConnection::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE PandaNode::UpConnection::
UpConnection(PandaNode *parent) :
_parent(parent)
{
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::UpConnection::operator <
// Access: Public
// Description: Sorts the up connections of a node by pointer. This
// is different from the down connections of a node,
// which are sorted by the specified _sort number. This
// makes it easy to locate a particular parent of a node
// by pointer, or to test for a parent-child
// relationship given two node pointers.
////////////////////////////////////////////////////////////////////
INLINE bool PandaNode::UpConnection::
operator < (const UpConnection &other) const {
return _parent < other._parent;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::UpConnection::get_parent
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE PandaNode *PandaNode::UpConnection::
get_parent() const {
return _parent;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::CData::Constructor
// Access: Public
@ -69,7 +105,8 @@ get_sort() const {
////////////////////////////////////////////////////////////////////
INLINE PandaNode::CData::
CData() {
_state_changes = RenderState::make_empty();
_state = RenderState::make_empty();
_transform = TransformState::make_identity();
}
////////////////////////////////////////////////////////////////////
@ -140,7 +177,7 @@ INLINE PandaNode *PandaNode::
get_parent(int n) const {
CDReader cdata(_cycler);
nassertr(n >= 0 && n < (int)cdata->_up.size(), NULL);
return cdata->_up[n];
return cdata->_up[n].get_parent();
}
////////////////////////////////////////////////////////////////////
@ -152,7 +189,7 @@ get_parent(int n) const {
INLINE int PandaNode::
find_parent(PandaNode *node) const {
CDReader cdata(_cycler);
Up::const_iterator ui = cdata->_up.find(node);
Up::const_iterator ui = cdata->_up.find(UpConnection(node));
if (ui == cdata->_up.end()) {
return -1;
}
@ -212,7 +249,7 @@ get_child_sort(int n) const {
INLINE void PandaNode::
set_attrib(const RenderAttrib *attrib, int override) {
CDWriter cdata(_cycler);
cdata->_state_changes = cdata->_state_changes->add(attrib, override);
cdata->_state = cdata->_state->add(attrib, override);
}
////////////////////////////////////////////////////////////////////
@ -227,9 +264,9 @@ set_attrib(const RenderAttrib *attrib, int override) {
INLINE const RenderAttrib *PandaNode::
get_attrib(TypeHandle type) const {
CDReader cdata(_cycler);
int index = cdata->_state_changes->find_attrib(type);
int index = cdata->_state->find_attrib(type);
if (index >= 0) {
return cdata->_state_changes->get_attrib(index);
return cdata->_state->get_attrib(index);
}
return NULL;
}
@ -245,7 +282,7 @@ get_attrib(TypeHandle type) const {
INLINE void PandaNode::
clear_attrib(TypeHandle type) {
CDWriter cdata(_cycler);
cdata->_state_changes = cdata->_state_changes->remove(type);
cdata->_state = cdata->_state->remove(type);
}
////////////////////////////////////////////////////////////////////
@ -261,7 +298,7 @@ clear_attrib(TypeHandle type) {
INLINE void PandaNode::
set_state(const RenderState *state) {
CDWriter cdata(_cycler);
cdata->_state_changes = state;
cdata->_state = state;
}
////////////////////////////////////////////////////////////////////
@ -276,7 +313,7 @@ set_state(const RenderState *state) {
INLINE const RenderState *PandaNode::
get_state() const {
CDReader cdata(_cycler);
return cdata->_state_changes;
return cdata->_state;
}
////////////////////////////////////////////////////////////////////
@ -290,7 +327,46 @@ get_state() const {
INLINE void PandaNode::
clear_state() {
CDWriter cdata(_cycler);
cdata->_state_changes = RenderState::make_empty();
cdata->_state = RenderState::make_empty();
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::set_transform
// Access: Published
// Description: Sets the transform that will be applied to this node
// and below. This defines a new coordinate space at
// this point in the scene graph and below.
////////////////////////////////////////////////////////////////////
INLINE void PandaNode::
set_transform(const TransformState *transform) {
CDWriter cdata(_cycler);
cdata->_transform = transform;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::get_transform
// Access: Published
// Description: Returns the transform that has been set on this
// particular node. This is not the net transform from
// the root, but simply the transform on this particular
// node.
////////////////////////////////////////////////////////////////////
INLINE const TransformState *PandaNode::
get_transform() const {
CDReader cdata(_cycler);
return cdata->_transform;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::clear_transform
// Access: Published
// Description: Resets the transform on this node to the identity
// transform.
////////////////////////////////////////////////////////////////////
INLINE void PandaNode::
clear_transform() {
CDWriter cdata(_cycler);
cdata->_transform = TransformState::make_identity();
}
////////////////////////////////////////////////////////////////////

View File

@ -17,6 +17,8 @@
////////////////////////////////////////////////////////////////////
#include "pandaNode.h"
#include "config_pgraph.h"
#include "nodeChainComponent.h"
#include "bamReader.h"
#include "bamWriter.h"
#include "indent.h"
@ -34,9 +36,11 @@ PandaNode::CData::
CData(const PandaNode::CData &copy) :
_down(copy._down),
_up(copy._up),
_chains(copy._chains),
_node_bounds(copy._node_bounds),
_subgraph_bounds(copy._subgraph_bounds),
_state_changes(copy._state_changes)
_state(copy._state),
_transform(copy._transform)
{
}
@ -74,9 +78,11 @@ PandaNode(const PandaNode &copy) :
{
// Copying a node does not copy its children.
// Copy the other node's bounding volume.
// Copy the other node's state and bounding volume.
CDReader copy_cdata(copy._cycler);
CDWriter cdata(_cycler);
cdata->_state = copy_cdata->_state;
cdata->_transform = copy_cdata->_transform;
cdata->_node_bounds = copy_cdata->_node_bounds;
}
@ -91,9 +97,11 @@ operator = (const PandaNode &copy) {
Namable::operator = (copy);
ReferenceCount::operator = (copy);
// Copy the other node's bounding volume.
// Copy the other node's state and bounding volume.
CDReader copy_cdata(copy._cycler);
CDWriter cdata(_cycler);
cdata->_state = copy_cdata->_state;
cdata->_transform = copy_cdata->_transform;
cdata->_node_bounds = copy_cdata->_node_bounds;
}
@ -144,19 +152,32 @@ find_child(PandaNode *node) const {
//
// If the same child is added to a node more than once,
// the previous instance is first removed.
//
// The return value is the index of the new child.
////////////////////////////////////////////////////////////////////
int PandaNode::
add_child(PandaNode *child, int sort) {
remove_child(child);
void PandaNode::
add_child(PandaNode *child_node, int sort) {
// Ensure the child_node is not deleted while we do this.
PT(PandaNode) keep_child = child_node;
remove_child(child_node);
CDWriter cdata(_cycler);
CDWriter cdata_child(child->_cycler);
CDWriter cdata_child(child_node->_cycler);
cdata->_down.insert(DownConnection(child_node, sort));
cdata_child->_up.insert(UpConnection(this));
Down::iterator ci = cdata->_down.insert(DownConnection(child, sort));
cdata_child->_up.insert(this);
// We also have to adjust any NodeChainComponents the child might
// have that reference the child as a top node. Any other
// components we can leave alone, because we are making a new
// instance of the child.
Chains::iterator ci;
for (ci = cdata_child->_chains.begin();
ci != cdata_child->_chains.end();
++ci) {
if ((*ci)->is_top_node()) {
(*ci)->set_next(get_generic_component());
}
}
return ci - cdata->_down.begin();
child_node->fix_chain_lengths();
}
////////////////////////////////////////////////////////////////////
@ -169,12 +190,41 @@ remove_child(int n) {
CDWriter cdata(_cycler);
nassertv(n >= 0 && n < (int)cdata->_down.size());
PandaNode *child = cdata->_down[n].get_child();
CDWriter cdata_child(child->_cycler);
PT(PandaNode) child_node = cdata->_down[n].get_child();
CDWriter cdata_child(child_node->_cycler);
cdata->_down.erase(cdata->_down.begin() + n);
int num_erased = cdata_child->_up.erase(this);
int num_erased = cdata_child->_up.erase(UpConnection(this));
nassertv(num_erased == 1);
// Now sever any NodeChainComponents on the child that reference
// this node. If we have multiple of these, we have to collapse
// them together.
NodeChainComponent *collapsed = (NodeChainComponent *)NULL;
Chains::iterator ci;
ci = cdata_child->_chains.begin();
while (ci != cdata_child->_chains.end()) {
Chains::iterator cnext = ci;
++cnext;
if (!(*ci)->is_top_node() && (*ci)->get_next()->get_node() == this) {
if (collapsed == (NodeChainComponent *)NULL) {
(*ci)->set_top_node();
collapsed = (*ci);
} else {
// This is a different component that used to reference a
// different instance, but now it's all just the same topnode.
// We have to collapse this and the previous one together.
// However, there might be some NodeChains out there that
// still keep a pointer to this one, so we can't remove it
// altogether.
(*ci)->collapse_with(collapsed);
cdata_child->_chains.erase(ci);
}
}
ci = cnext;
}
child_node->fix_chain_lengths();
}
////////////////////////////////////////////////////////////////////
@ -185,24 +235,49 @@ remove_child(int n) {
// already a child of the node.
////////////////////////////////////////////////////////////////////
bool PandaNode::
remove_child(PandaNode *child) {
CDWriter cdata_child(child->_cycler);
remove_child(PandaNode *child_node) {
// Ensure the child_node is not deleted while we do this.
PT(PandaNode) keep_child = child_node;
CDWriter cdata_child(child_node->_cycler);
// First, look for and remove this node from the child's parent
// list.
int num_erased = cdata_child->_up.erase(this);
int num_erased = cdata_child->_up.erase(UpConnection(this));
if (num_erased == 0) {
// No such node; it wasn't our child to begin with.
return false;
}
// Now sever any NodeChainComponents on the child that reference
// this node. If we have multiple of these, we have to collapse
// them together (see above).
NodeChainComponent *collapsed = (NodeChainComponent *)NULL;
Chains::iterator ci;
ci = cdata_child->_chains.begin();
while (ci != cdata_child->_chains.end()) {
Chains::iterator cnext = ci;
++cnext;
if (!(*ci)->is_top_node() && (*ci)->get_next()->get_node() == this) {
if (collapsed == (NodeChainComponent *)NULL) {
(*ci)->set_top_node();
collapsed = (*ci);
} else {
(*ci)->collapse_with(collapsed);
cdata_child->_chains.erase(ci);
}
}
ci = cnext;
}
child_node->fix_chain_lengths();
CDWriter cdata(_cycler);
// Now, look for and remove the child node from our down list.
Down::iterator ci;
for (ci = cdata->_down.begin(); ci != cdata->_down.end(); ++ci) {
if ((*ci).get_child() == child) {
cdata->_down.erase(ci);
Down::iterator di;
for (di = cdata->_down.begin(); di != cdata->_down.end(); ++di) {
if ((*di).get_child() == child_node) {
cdata->_down.erase(di);
return true;
}
}
@ -222,9 +297,32 @@ remove_all_children() {
CDWriter cdata(_cycler);
Down::iterator ci;
for (ci = cdata->_down.begin(); ci != cdata->_down.end(); ++ci) {
PandaNode *child = (*ci).get_child();
CDWriter child_cdata(child->_cycler);
child_cdata->_up.erase(this);
PT(PandaNode) child_node = (*ci).get_child();
CDWriter cdata_child(child_node->_cycler);
cdata_child->_up.erase(UpConnection(this));
// Now sever any NodeChainComponents on the child that reference
// this node. If we have multiple of these, we have to collapse
// them together (see above).
NodeChainComponent *collapsed = (NodeChainComponent *)NULL;
Chains::iterator ci;
ci = cdata_child->_chains.begin();
while (ci != cdata_child->_chains.end()) {
Chains::iterator cnext = ci;
++cnext;
if (!(*ci)->is_top_node() && (*ci)->get_next()->get_node() == this) {
if (collapsed == (NodeChainComponent *)NULL) {
(*ci)->set_top_node();
collapsed = (*ci);
} else {
(*ci)->collapse_with(collapsed);
cdata_child->_chains.erase(ci);
}
}
ci = cnext;
}
child_node->fix_chain_lengths();
}
}
@ -247,8 +345,11 @@ void PandaNode::
write(ostream &out, int indent_level) const {
indent(out, indent_level) << *this;
CDReader cdata(_cycler);
if (!cdata->_state_changes->is_empty()) {
out << " (" << *cdata->_state_changes << ")";
if (!cdata->_transform->is_identity()) {
out << " T";
}
if (!cdata->_state->is_empty()) {
out << " (" << *cdata->_state << ")";
}
out << "\n";
}
@ -269,6 +370,286 @@ is_geom_node() const {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::attach
// Access: Private, Static
// Description: Creates a new parent-child relationship, and returns
// the new NodeChainComponent. If the child was already
// attached to the indicated parent, repositions it and
// returns the original NodeChainComponent.
////////////////////////////////////////////////////////////////////
PT(NodeChainComponent) PandaNode::
attach(NodeChainComponent *parent, PandaNode *child_node, int sort) {
nassertr(parent != (NodeChainComponent *)NULL, (NodeChainComponent *)NULL);
// See if the child was already attached to the parent. If it was,
// we'll use that same NodeChainComponent.
PT(NodeChainComponent) child = get_component(parent, child_node);
if (child == (NodeChainComponent *)NULL) {
// The child was not already attached to the parent, so get a new
// component.
child = get_top_component(child_node);
}
reparent(parent, child, sort);
return child;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::detach
// Access: Private, Static
// Description: Breaks a parent-child relationship.
////////////////////////////////////////////////////////////////////
void PandaNode::
detach(NodeChainComponent *child) {
nassertv(child != (NodeChainComponent *)NULL);
nassertv(!child->is_top_node());
PandaNode *child_node = child->get_node();
PandaNode *parent_node = child->get_next()->get_node();
// Break the NodeChainComponent connection.
child->set_top_node();
CDWriter cdata_child(child_node->_cycler);
// Any other components in the same child_node that previously
// referenced the same parent has now become invalid and must be
// collapsed into this one and removed from the chains set.
Chains::iterator ci;
ci = cdata_child->_chains.begin();
while (ci != cdata_child->_chains.end()) {
Chains::iterator cnext = ci;
++cnext;
if ((*ci) != child && !(*ci)->is_top_node() &&
(*ci)->get_next()->get_node() == parent_node) {
(*ci)->collapse_with(child);
cdata_child->_chains.erase(ci);
}
ci = cnext;
}
child_node->fix_chain_lengths();
// Now look for the child and break the actual connection.
// First, look for and remove the parent node from the child's up
// list.
int num_erased = cdata_child->_up.erase(UpConnection(parent_node));
nassertv(num_erased == 1);
CDWriter cdata_parent(parent_node->_cycler);
// Now, look for and remove the child node from the parent's down list.
Down::iterator di;
for (di = cdata_parent->_down.begin();
di != cdata_parent->_down.end();
++di) {
if ((*di).get_child() == child_node) {
cdata_parent->_down.erase(di);
return;
}
}
// We shouldn't get here unless there was a parent-child mismatch.
nassertv(false);
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::reparent
// Access: Private, Static
// Description: Switches a node from one parent to another.
////////////////////////////////////////////////////////////////////
void PandaNode::
reparent(NodeChainComponent *new_parent, NodeChainComponent *child, int sort) {
nassertv(new_parent != (NodeChainComponent *)NULL);
nassertv(child != (NodeChainComponent *)NULL);
if (!child->is_top_node()) {
detach(child);
}
// Adjust the NodeChainComponents.
child->set_next(new_parent);
PandaNode *child_node = child->get_node();
PandaNode *parent_node = new_parent->get_node();
// Now reattach at the indicated sort position.
CDWriter cdata_parent(parent_node->_cycler);
CDWriter cdata_child(child_node->_cycler);
cdata_parent->_down.insert(DownConnection(child_node, sort));
cdata_child->_up.insert(UpConnection(parent_node));
cdata_child->_chains.insert(child);
child_node->fix_chain_lengths();
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::get_component
// Access: Private, Static
// Description: Returns the NodeChainComponent based on the indicated
// child of the given parent, or NULL if there is no
// such parent-child relationship.
////////////////////////////////////////////////////////////////////
PT(NodeChainComponent) PandaNode::
get_component(NodeChainComponent *parent, PandaNode *child_node) {
nassertr(parent != (NodeChainComponent *)NULL, (NodeChainComponent *)NULL);
PandaNode *parent_node = parent->get_node();
{
CDReader cdata_child(child_node->_cycler);
// First, walk through the list of NodeChainComponents we already
// have on the child, looking for one that already exists,
// referencing the indicated parent component.
Chains::iterator ci;
for (ci = cdata_child->_chains.begin();
ci != cdata_child->_chains.end();
++ci) {
if ((*ci)->get_next() == parent) {
// If we already have such a component, just return it.
return (*ci);
}
}
}
// We don't already have a NodeChainComponent referring to this
// parent-child relationship. Are they actually related?
int child_index = child_node->find_parent(parent_node);
if (child_index >= 0) {
// They are. Create and return a new one.
PT(NodeChainComponent) child =
new NodeChainComponent(child_node, parent);
CDWriter cdata_child(child_node->_cycler);
cdata_child->_chains.insert(child);
return child;
} else {
// They aren't related. Return NULL.
return NULL;
}
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::get_top_component
// Access: Private, Static
// Description: Returns a NodeChainComponent referencing the
// indicated node as a singleton. It is invalid to call
// this for a node that has parents, unless you are
// about to create a new instance (and immediately
// reconnect the NodeChainComponent elsewhere).
////////////////////////////////////////////////////////////////////
PT(NodeChainComponent) PandaNode::
get_top_component(PandaNode *child_node) {
{
CDReader cdata_child(child_node->_cycler);
// Walk through the list of NodeChainComponents we already have on
// the child, looking for one that already exists as a top node.
Chains::const_iterator ci;
for (ci = cdata_child->_chains.begin();
ci != cdata_child->_chains.end();
++ci) {
if ((*ci)->is_top_node()) {
// If we already have such a component, just return it.
return (*ci);
}
}
}
// We don't already have such a NodeChainComponent; create and
// return a new one.
PT(NodeChainComponent) child =
new NodeChainComponent(child_node, (NodeChainComponent *)NULL);
CDWriter cdata_child(child_node->_cycler);
cdata_child->_chains.insert(child);
return child;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::get_generic_component
// Access: Private
// Description: Returns a NodeChainComponent referencing this node as
// a chain from the root. It is only valid to call this
// if there is an unambiguous path from the root;
// otherwise, a warning will be issued and one path will
// be chosen arbitrarily.
////////////////////////////////////////////////////////////////////
PT(NodeChainComponent) PandaNode::
get_generic_component() {
int num_parents = get_num_parents();
if (num_parents == 0) {
return get_top_component(this);
} else {
if (num_parents != 1) {
pgraph_cat.warning()
<< *this << " has " << num_parents
<< " parents; choosing arbitrary path to root.\n";
}
PT(NodeChainComponent) parent = get_parent(0)->get_generic_component();
return get_component(parent, this);
}
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::delete_component
// Access: Private
// Description: Removes a NodeChainComponent from the set prior to
// its deletion. This should only be called by the
// NodeChainComponent destructor.
////////////////////////////////////////////////////////////////////
void PandaNode::
delete_component(NodeChainComponent *component) {
// We have to remove the component from all of the pipeline stages,
// not just the current one.
int max_num_erased = 0;
int num_stages = _cycler.get_num_stages();
for (int i = 0; i < num_stages; i++) {
if (_cycler.is_stage_unique(i)) {
CData *cdata = _cycler.write_stage(i);
int num_erased = cdata->_chains.erase(component);
max_num_erased = max(max_num_erased, num_erased);
_cycler.release_write_stage(i, cdata);
}
}
nassertv(max_num_erased == 1);
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::fix_chain_lengths
// Access: Private
// Description: Recursively fixes the _length member of each
// NodeChainComponent at this level and below, after an
// add or delete child operation that might have messed
// these up.
////////////////////////////////////////////////////////////////////
void PandaNode::
fix_chain_lengths() {
CDReader cdata(_cycler);
bool any_wrong = false;
Chains::const_iterator ci;
for (ci = cdata->_chains.begin(); ci != cdata->_chains.end(); ++ci) {
if ((*ci)->fix_length()) {
any_wrong = true;
}
}
// If any chains were updated, we have to recurse on all of our
// children, since any one of those chains might be shared by any of
// our child nodes.
if (any_wrong) {
Down::const_iterator di;
for (di = cdata->_down.begin(); di != cdata->_down.end(); ++di) {
(*di).get_child()->fix_chain_lengths();
}
}
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::register_with_read_factory
// Access: Public, Static

View File

@ -26,6 +26,7 @@
#include "cycleDataWriter.h"
#include "pipelineCycler.h"
#include "renderState.h"
#include "transformState.h"
#include "typedWritable.h"
#include "boundedObject.h"
@ -35,6 +36,8 @@
#include "ordered_vector.h"
#include "pointerTo.h"
class NodeChainComponent;
////////////////////////////////////////////////////////////////////
// Class : PandaNode
// Description : A basic node of the scene graph or data graph. This
@ -58,9 +61,9 @@ PUBLISHED:
INLINE int get_child_sort(int n) const;
int find_child(PandaNode *node) const;
int add_child(PandaNode *child, int sort = 0);
void add_child(PandaNode *child_node, int sort = 0);
void remove_child(int n);
bool remove_child(PandaNode *child);
bool remove_child(PandaNode *child_node);
/*
bool stash_child(PandaNode *child);
@ -80,12 +83,31 @@ PUBLISHED:
INLINE const RenderState *get_state() const;
INLINE void clear_state();
INLINE void set_transform(const TransformState *transform);
INLINE const TransformState *get_transform() const;
INLINE void clear_transform();
virtual void output(ostream &out) const;
virtual void write(ostream &out, int indent_level) const;
public:
virtual bool is_geom_node() const;
private:
// parent-child manipulation for NodeChain support. Don't try to
// call these directly.
static PT(NodeChainComponent) attach(NodeChainComponent *parent,
PandaNode *child, int sort);
static void detach(NodeChainComponent *child);
static void reparent(NodeChainComponent *new_parent,
NodeChainComponent *child, int sort);
static PT(NodeChainComponent) get_component(NodeChainComponent *parent,
PandaNode *child);
static PT(NodeChainComponent) get_top_component(PandaNode *child);
PT(NodeChainComponent) get_generic_component();
void delete_component(NodeChainComponent *component);
void fix_chain_lengths();
private:
class EXPCL_PANDA DownConnection {
public:
@ -102,13 +124,27 @@ private:
int _sort;
};
typedef ov_multiset<DownConnection> Down;
// Parent pointers are not reference counted. That way, parents and
// children do not circularly reference each other. In fact, parent
// pointers are just simple pointers, with no additional data. We
// don't really need to keep the parent pointers around, but it's
// nice to be able to walk up the graph.
typedef ov_set<PandaNode *> Up;
class EXPCL_PANDA UpConnection {
public:
INLINE UpConnection(PandaNode *child);
INLINE bool operator < (const UpConnection &other) const;
INLINE PandaNode *get_parent() const;
private:
// Parent pointers are not reference counted. That way, parents and
// children do not circularly reference each other.
PandaNode *_parent;
};
typedef ov_set<UpConnection> Up;
// We also maintain a set of NodeChainComponents in the node. This
// represents the set of instances of this node that we have
// requested a NodeChain for. We don't keep reference counts; when
// each NodeChainComponent destructs, it removes itself from this
// set.
typedef pset<NodeChainComponent *> Chains;
// This is the data that must be cycled between pipeline stages.
class EXPCL_PANDA CData : public CycleData {
public:
@ -118,10 +154,12 @@ private:
Down _down;
Up _up;
Chains _chains;
BoundedObject _node_bounds;
BoundedObject _subgraph_bounds;
CPT(RenderState) _state_changes;
CPT(RenderState) _state;
CPT(TransformState) _transform;
};
PipelineCycler<CData> _cycler;
@ -175,6 +213,8 @@ private:
static TypeHandle _type_handle;
friend class PandaNode::Children;
friend class NodeChain;
friend class NodeChainComponent;
};
INLINE ostream &operator << (ostream &out, const PandaNode &node) {

View File

@ -6,4 +6,6 @@
#include "cycleDataReader.cxx"
#include "cycleDataWriter.cxx"
#include "qpgeomNode.cxx"
#include "pandaNode.cxx"
#include "qplensNode.cxx"
#include "nodeChain.cxx"
#include "nodeChainComponent.cxx"

View File

@ -1,3 +1,4 @@
#include "pandaNode.cxx"
#include "pipeline.cxx"
#include "pipelineCycler.cxx"
#include "pipelineCyclerBase.cxx"
@ -5,5 +6,4 @@
#include "renderState.cxx"
#include "test_pgraph.cxx"
#include "textureAttrib.cxx"
#include "transformAttrib.cxx"
#include "transformState.cxx"

View File

@ -28,3 +28,36 @@ PipelineCycler(Pipeline *pipeline) :
PipelineCyclerBase(new CycleDataType, pipeline)
{
}
////////////////////////////////////////////////////////////////////
// Function: PipelineCycler::read
// Access: Public
// Description: See PipelineCyclerBase::read().
////////////////////////////////////////////////////////////////////
template<class CycleDataType>
INLINE const CycleDataType *PipelineCycler<CycleDataType>::
read() const {
return (const CycleDataType *)PipelineCyclerBase::read();
}
////////////////////////////////////////////////////////////////////
// Function: PipelineCycler::write
// Access: Public
// Description: See PipelineCyclerBase::write().
////////////////////////////////////////////////////////////////////
template<class CycleDataType>
INLINE CycleDataType *PipelineCycler<CycleDataType>::
write() {
return (CycleDataType *)PipelineCyclerBase::write();
}
////////////////////////////////////////////////////////////////////
// Function: PipelineCycler::write_stage
// Access: Public
// Description: See PipelineCyclerBase::write_stage().
////////////////////////////////////////////////////////////////////
template<class CycleDataType>
INLINE CycleDataType *PipelineCycler<CycleDataType>::
write_stage(int n) {
return (CycleDataType *)PipelineCyclerBase::write_stage(n);
}

View File

@ -53,6 +53,10 @@ template<class CycleDataType>
class PipelineCycler : public PipelineCyclerBase {
public:
INLINE PipelineCycler(Pipeline *pipeline = NULL);
INLINE const CycleDataType *read() const;
INLINE CycleDataType *write();
INLINE CycleDataType *write_stage(int n);
};
#include "pipelineCycler.I"

View File

@ -107,3 +107,56 @@ release_write(CycleData *pointer) {
nassertv(_write_count > 0);
_write_count--;
}
////////////////////////////////////////////////////////////////////
// Function: PipelineCyclerBase::get_num_stages
// Access: Public
// Description: Returns the number of stages in the pipeline.
////////////////////////////////////////////////////////////////////
INLINE int PipelineCyclerBase::
get_num_stages() {
return 1;
}
////////////////////////////////////////////////////////////////////
// Function: PipelineCyclerBase::is_stage_unique
// Access: Public
// Description: Returns true if the nth stage is a different pointer
// than the previous stage, or false if its pointer is
// shared with the previous one.
////////////////////////////////////////////////////////////////////
INLINE bool PipelineCyclerBase::
is_stage_unique(int n) const {
nassertr(n == 0, false);
return true;
}
////////////////////////////////////////////////////////////////////
// Function: PipelineCyclerBase::write_stage
// Access: Public
// Description: Returns a pointer suitable for writing to the nth
// stage of the pipeline. This is for special
// applications that need to update the entire pipeline
// at once (for instance, to remove an invalid pointer).
// This pointer should later be released with
// release_write_stage().
////////////////////////////////////////////////////////////////////
INLINE CycleData *PipelineCyclerBase::
write_stage(int n) {
nassertr(n == 0, (CycleData *)NULL);
_stage_count++;
return _data;
}
////////////////////////////////////////////////////////////////////
// Function: PipelineCyclerBase::release_write_stage
// Access: Public
// Description: Releases a pointer previously obtained via a call to
// write_stage().
////////////////////////////////////////////////////////////////////
INLINE void PipelineCyclerBase::
release_write_stage(int n, CycleData *pointer) {
nassertv(n == 0 && pointer == _data);
nassertv(_stage_count > 0);
_stage_count--;
}

View File

@ -29,7 +29,8 @@ PipelineCyclerBase(CycleData *initial_data, Pipeline *pipeline) :
_data(initial_data),
_pipeline(pipeline),
_read_count(0),
_write_count(0)
_write_count(0),
_stage_count(0)
{
if (_pipeline == (Pipeline *)NULL) {
_pipeline = Pipeline::get_render_pipeline();
@ -43,6 +44,6 @@ PipelineCyclerBase(CycleData *initial_data, Pipeline *pipeline) :
////////////////////////////////////////////////////////////////////
PipelineCyclerBase::
~PipelineCyclerBase() {
nassertv(_read_count == 0 && _write_count == 0);
nassertv(_read_count == 0 && _write_count == 0 && _stage_count == 0);
}

View File

@ -43,10 +43,15 @@ public:
INLINE void increment_write(CycleData *pointer);
INLINE void release_write(CycleData *pointer);
INLINE int get_num_stages();
INLINE bool is_stage_unique(int n) const;
INLINE CycleData *write_stage(int n);
INLINE void release_write_stage(int n, CycleData *pointer);
private:
PT(CycleData) _data;
Pipeline *_pipeline;
short _read_count, _write_count;
short _read_count, _write_count, _stage_count;
};
#include "pipelineCyclerBase.I"

View File

@ -0,0 +1,87 @@
// Filename: qpcamera.I
// Created by: drose (26Feb02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: qpCamera::set_active
// Access: Published
// Description: Sets the active flag on the camera. When the camera
// is not active, nothing will be rendered.
////////////////////////////////////////////////////////////////////
INLINE void qpCamera::
set_active(bool active) {
_active = active;
}
////////////////////////////////////////////////////////////////////
// Function: qpCamera::is_active
// Access: Published
// Description: Returns the current setting of the active flag on the
// camera.
////////////////////////////////////////////////////////////////////
INLINE bool qpCamera::
is_active() const {
return _active;
}
////////////////////////////////////////////////////////////////////
// Function: qpCamera::set_scene
// Access: Published
// Description: Sets the scene that will be rendered by the camera.
// This is normally the root node of a scene graph,
// typically a node called 'render', although it could
// represent the root of any subgraph.
////////////////////////////////////////////////////////////////////
INLINE void qpCamera::
set_scene(PandaNode *scene) {
_scene = scene;
}
////////////////////////////////////////////////////////////////////
// Function: qpCamera::get_scene
// Access: Published
// Description: Returns the scene that will be rendered by the
// camera. See set_scene().
////////////////////////////////////////////////////////////////////
INLINE PandaNode *qpCamera::
get_scene() const {
return _scene;
}
////////////////////////////////////////////////////////////////////
// Function: qpCamera::get_num_display_regions
// Access: Published
// Description: Returns the number of display regions associated with
// the camera.
////////////////////////////////////////////////////////////////////
INLINE int qpCamera::
get_num_display_regions() const {
return _display_regions.size();
}
////////////////////////////////////////////////////////////////////
// Function: qpCamera::get_display_region
// Access: Published
// Description: Returns the nth display region associated with the
// camera.
////////////////////////////////////////////////////////////////////
INLINE DisplayRegion *qpCamera::
get_display_region(int n) const {
nassertr(n >= 0 && n < (int)_display_regions.size(), (DisplayRegion *)NULL);
return _display_regions[n];
}

View File

@ -0,0 +1,144 @@
// Filename: qpcamera.cxx
// Created by: drose (26Feb02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#include "pandabase.h"
#include "qpcamera.h"
#include "lens.h"
#include "throw_event.h"
TypeHandle qpCamera::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: qpCamera::Constructor
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
qpCamera::
qpCamera(const string &name) :
qpLensNode(name),
_active(true),
_scene((PandaNode *)NULL)
{
}
////////////////////////////////////////////////////////////////////
// Function: qpCamera::Copy Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
qpCamera::
qpCamera(const qpCamera &copy) :
qpLensNode(copy),
_active(copy._active),
_scene(copy._scene)
{
}
////////////////////////////////////////////////////////////////////
// Function: qpCamera::Copy Assignment Operator
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
void qpCamera::
operator = (const qpCamera &copy) {
qpLensNode::operator = (copy);
_active = copy._active;
_scene = copy._scene;
}
////////////////////////////////////////////////////////////////////
// Function: qpCamera::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
qpCamera::
~qpCamera() {
// We don't have to destroy the display region(s) associated with
// the camera; they're responsible for themselves. However, they
// should have removed themselves before we destruct, or something
// went wrong.
nassertv(_display_regions.empty());
}
////////////////////////////////////////////////////////////////////
// Function: qpCamera::make_copy
// Access: Public, Virtual
// Description: Returns a newly-allocated Node that is a shallow copy
// of this one. It will be a different Node pointer,
// but its internal data may or may not be shared with
// that of the original Node.
////////////////////////////////////////////////////////////////////
PandaNode *qpCamera::
make_copy() const {
return new qpCamera(*this);
}
////////////////////////////////////////////////////////////////////
// Function: qpCamera::safe_to_flatten
// Access: Public, Virtual
// Description: Returns true if it is generally safe to flatten out
// this particular kind of Node by duplicating
// instances, false otherwise (for instance, a qpCamera
// cannot be safely flattened, because the qpCamera
// pointer itself is meaningful).
////////////////////////////////////////////////////////////////////
bool qpCamera::
safe_to_flatten() const {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: qpCamera::safe_to_transform
// Access: Public, Virtual
// Description: Returns true if it is generally safe to transform
// this particular kind of Node by calling the xform()
// method, false otherwise. For instance, it's usually
// a bad idea to attempt to xform a Character.
////////////////////////////////////////////////////////////////////
bool qpCamera::
safe_to_transform() const {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: qpCamera::add_display_region
// Access: Private
// Description: Adds the indicated DisplayRegion to the set of
// DisplayRegions shared by the camera. This is only
// intended to be called from the DisplayRegion.
////////////////////////////////////////////////////////////////////
void qpCamera::
add_display_region(DisplayRegion *display_region) {
_display_regions.push_back(display_region);
}
////////////////////////////////////////////////////////////////////
// Function: qpCamera::remove_display_region
// Access: Private
// Description: Removes the indicated DisplayRegion from the set of
// DisplayRegions shared by the camera. This is only
// intended to be called from the DisplayRegion.
////////////////////////////////////////////////////////////////////
void qpCamera::
remove_display_region(DisplayRegion *display_region) {
DisplayRegions::iterator dri =
find(_display_regions.begin(), _display_regions.end(), display_region);
if (dri != _display_regions.end()) {
_display_regions.erase(dri);
}
}

View File

@ -0,0 +1,90 @@
// Filename: qpcamera.h
// Created by: drose (26Feb02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#ifndef QPCAMERA_H
#define QPCAMERA_H
#include "pandabase.h"
#include "qplensNode.h"
class DisplayRegion;
class PandaNode;
////////////////////////////////////////////////////////////////////
// Class : qpCamera
// Description : A node that can be positioned around in the scene
// graph to represent a point of view for rendering a
// scene.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA qpCamera : public qpLensNode {
PUBLISHED:
qpCamera(const string &name);
public:
qpCamera(const qpCamera &copy);
void operator = (const qpCamera &copy);
virtual ~qpCamera();
virtual PandaNode *make_copy() const;
virtual bool safe_to_flatten() const;
virtual bool safe_to_transform() const;
PUBLISHED:
INLINE void set_active(bool active);
INLINE bool is_active() const;
INLINE void set_scene(PandaNode *scene);
INLINE PandaNode *get_scene() const;
INLINE int get_num_display_regions() const;
INLINE DisplayRegion *get_display_region(int n) const;
private:
void add_display_region(DisplayRegion *display_region);
void remove_display_region(DisplayRegion *display_region);
bool _active;
PandaNode *_scene;
typedef pvector<DisplayRegion *> DisplayRegions;
DisplayRegions _display_regions;
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
qpLensNode::init_type();
register_type(_type_handle, "qpCamera",
qpLensNode::get_class_type());
}
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
private:
static TypeHandle _type_handle;
friend class DisplayRegion;
};
#include "qpcamera.I"
#endif

View File

@ -17,6 +17,7 @@
////////////////////////////////////////////////////////////////////
#include "qpcullTraverser.h"
#include "transformState.h"
#include "renderState.h"
#include "cullHandler.h"
#include "dcast.h"
@ -30,7 +31,7 @@
qpCullTraverser::
qpCullTraverser() {
_initial_state = RenderState::make_empty();
_world_transform = DCAST(TransformAttrib, TransformAttrib::make_identity());
_world_transform = DCAST(TransformState, TransformState::make_identity());
_cull_handler = (CullHandler *)NULL;
}
@ -49,17 +50,11 @@ set_initial_state(const RenderState *initial_state) {
////////////////////////////////////////////////////////////////////
// Function: qpCullTraverser::set_world_transform
// Access: Public
// Description: Specifies the net inverse transform of the camera
// viewing the scene. Normally, this is the same as the
// transform specified in the initial state, but it
// might be different if we are culling a subgraph,
// for instance.
//
// This is used to evaluate camera-dependent attributes
// like billboards and LOD nodes.
// Description: Specifies the position of the world relative to the
// camera.
////////////////////////////////////////////////////////////////////
void qpCullTraverser::
set_world_transform(const TransformAttrib *world_transform) {
set_world_transform(const TransformState *world_transform) {
_world_transform = world_transform;
}
@ -67,7 +62,7 @@ set_world_transform(const TransformAttrib *world_transform) {
// Function: qpCullTraverser::set_cull_handler
// Access: Public
// Description: Specifies the object that will receive the culled
// Geoms. This must be called before traverse().
// Geoms. This must be set before calling traverse().
////////////////////////////////////////////////////////////////////
void qpCullTraverser::
set_cull_handler(CullHandler *cull_handler) {
@ -83,7 +78,7 @@ void qpCullTraverser::
traverse(PandaNode *root) {
nassertv(_cull_handler != (CullHandler *)NULL);
r_traverse(root, _initial_state, 0);
r_traverse(root, _world_transform, _initial_state, 0);
}
////////////////////////////////////////////////////////////////////
@ -92,7 +87,9 @@ traverse(PandaNode *root) {
// Description: The recursive traversal implementation.
////////////////////////////////////////////////////////////////////
void qpCullTraverser::
r_traverse(PandaNode *node, const RenderState *state, int flags) {
r_traverse(PandaNode *node, const TransformState *transform,
const RenderState *state, int flags) {
CPT(TransformState) next_transform = transform->compose(node->get_transform());
CPT(RenderState) next_state = state->compose(node->get_state());
if (node->is_geom_node()) {
@ -104,7 +101,7 @@ r_traverse(PandaNode *node, const RenderState *state, int flags) {
Geom *geom = geom_node->get_geom(i);
CPT(RenderState) geom_state =
next_state->compose(geom_node->get_geom_state(i));
_cull_handler->record_geom(geom, geom_state);
_cull_handler->record_geom(geom, next_transform, geom_state);
}
}
@ -112,6 +109,6 @@ r_traverse(PandaNode *node, const RenderState *state, int flags) {
PandaNode::Children cr = node->get_children();
int num_children = cr.get_num_children();
for (int i = 0; i < num_children; i++) {
r_traverse(cr.get_child(i), next_state, flags);
r_traverse(cr.get_child(i), next_transform, next_state, flags);
}
}

View File

@ -22,7 +22,7 @@
#include "pandabase.h"
#include "renderState.h"
#include "transformAttrib.h"
#include "transformState.h"
#include "pointerTo.h"
class PandaNode;
@ -41,16 +41,17 @@ public:
qpCullTraverser();
void set_initial_state(const RenderState *initial_state);
void set_world_transform(const TransformAttrib *world_transform);
void set_world_transform(const TransformState *world_transform);
void set_cull_handler(CullHandler *cull_handler);
void traverse(PandaNode *root);
private:
void r_traverse(PandaNode *node, const RenderState *state, int flags);
void r_traverse(PandaNode *node, const TransformState *transform,
const RenderState *state, int flags);
CPT(RenderState) _initial_state;
CPT(TransformAttrib) _world_transform;
CPT(TransformState) _world_transform;
CullHandler *_cull_handler;
};

View File

@ -0,0 +1,89 @@
// Filename: qplensNode.I
// Created by: drose (26Feb02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: qpLensNode::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE qpLensNode::
qpLensNode(const string &name) :
PandaNode(name)
{
}
////////////////////////////////////////////////////////////////////
// Function: qpLensNode::Copy Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE qpLensNode::
qpLensNode(const qpLensNode &copy) :
PandaNode(copy),
_lens(copy._lens)
{
}
////////////////////////////////////////////////////////////////////
// Function: qpLensNode::Copy Assignment Operator
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE void qpLensNode::
operator = (const qpLensNode &copy) {
PandaNode::operator = (copy);
_lens = copy._lens;
}
////////////////////////////////////////////////////////////////////
// Function: copy_lens
// Access: Public
// Description: Sets up the qpLensNode using a copy of the
// indicated Lens. If the original Lens is
// changed or destroyed, this qpLensNode is not
// affected.
////////////////////////////////////////////////////////////////////
INLINE void qpLensNode::
copy_lens(const Lens &lens) {
_lens = lens.make_copy();
}
////////////////////////////////////////////////////////////////////
// Function: set_lens
// Access: Public
// Description: Sets up the qpLensNode using this particular Lens
// pointer. If the lens is subsequently modified, the
// qpLensNode properties immediately reflect the change.
////////////////////////////////////////////////////////////////////
INLINE void qpLensNode::
set_lens(Lens *lens) {
_lens = lens;
}
////////////////////////////////////////////////////////////////////
// Function: get_lens
// Access: Public
// Description: Returns a pointer to the particular Lens
// associated with this qpLensNode, or NULL if there is
// not yet a Lens associated.
////////////////////////////////////////////////////////////////////
INLINE Lens *qpLensNode::
get_lens() {
return _lens;
}

View File

@ -0,0 +1,82 @@
// Filename: qplensNode.cxx
// Created by: drose (26Feb02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#include "qplensNode.h"
#include "geometricBoundingVolume.h"
TypeHandle qpLensNode::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: qpLensNode::make_copy
// Access: Public, Virtual
// Description: Returns a newly-allocated Node that is a shallow copy
// of this one. It will be a different Node pointer,
// but its internal data may or may not be shared with
// that of the original Node.
////////////////////////////////////////////////////////////////////
PandaNode *qpLensNode::
make_copy() const {
return new qpLensNode(*this);
}
////////////////////////////////////////////////////////////////////
// Function: qpLensNode::is_in_view
// Access: Public
// Description: Returns true if the given point is within the bounds
// of the lens of the qpLensNode (i.e. if the camera can
// see the point).
////////////////////////////////////////////////////////////////////
bool qpLensNode::
is_in_view(const LPoint3f &pos) {
PT(BoundingVolume) bv = _lens->make_bounds();
if (bv == NULL) {
return false;
}
GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bv);
int ret = gbv->contains(pos);
return (ret != 0);
}
////////////////////////////////////////////////////////////////////
// Function: qpLensNode::output
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void qpLensNode::
output(ostream &out) const {
PandaNode::output(out);
if (_lens != (Lens *)NULL) {
out << " (";
_lens->output(out);
out << ")";
}
}
////////////////////////////////////////////////////////////////////
// Function: qpLensNode::write
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void qpLensNode::
write(ostream &out, int indent_level) const {
PandaNode::write(out, indent_level);
if (_lens != (Lens *)NULL) {
_lens->write(out, indent_level + 2);
}
}

View File

@ -0,0 +1,78 @@
// Filename: qplensNode.h
// Created by: drose (26Feb02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#ifndef QPLENSNODE_H
#define QPLENSNODE_H
#include "pandabase.h"
#include "pandaNode.h"
#include "lens.h"
#include "pointerTo.h"
////////////////////////////////////////////////////////////////////
// Class : qpLensNode
// Description : A node that contains a Lens. The most important
// example of this kind of node is a Camera, but other
// kinds of nodes also contain a lens (for instance, a
// Spotlight).
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA qpLensNode : public PandaNode {
PUBLISHED:
INLINE qpLensNode(const string &name);
public:
INLINE qpLensNode(const qpLensNode &copy);
INLINE void operator = (const qpLensNode &copy);
virtual void output(ostream &out) const;
virtual void write(ostream &out, int indent_level = 0) const;
virtual PandaNode *make_copy() const;
PUBLISHED:
INLINE void copy_lens(const Lens &lens);
INLINE void set_lens(Lens *lens);
INLINE Lens *get_lens();
bool is_in_view(const LPoint3f &pos);
protected:
PT(Lens) _lens;
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
PandaNode::init_type();
register_type(_type_handle, "qpLensNode",
PandaNode::get_class_type());
}
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
private:
static TypeHandle _type_handle;
};
#include "qplensNode.I"
#endif

View File

@ -173,7 +173,8 @@ compare_to_impl(const RenderAttrib *other) const {
// RenderAttrib (that is, a subsequent RenderAttrib
// completely replaces the preceding one). On the other
// hand, some kinds of RenderAttrib (for instance,
// TransformAttrib) might combine in meaningful ways.
// ColorTransformAttrib) might combine in meaningful
// ways.
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) RenderAttrib::
compose_impl(const RenderAttrib *other) const {

View File

@ -72,9 +72,6 @@ RenderState::
if (_saved_entry != _states.end()) {
_states.erase(_saved_entry);
_saved_entry = _states.end();
cerr << "Removing " << (void *)this << ", " << _states.size()
<< " remaining.\n";
}
// Now make sure we clean up all other floating pointers to the
@ -359,8 +356,6 @@ invert_compose(const RenderState *other) const {
// but we pretend that it is because it's only a cache which is
// transparent to the rest of the interface.
cerr << "invert composing " << *this << " with " << *other << "\n";
// We handle empty state (identity) as a trivial special case.
if (is_empty()) {
return other;
@ -384,7 +379,6 @@ invert_compose(const RenderState *other) const {
((Composition &)comp)._result = do_invert_compose(other);
}
// Here's the cache!
cerr << " returning cached result " << (void *)comp._result.p() << "\n";
return comp._result;
}
@ -401,7 +395,6 @@ invert_compose(const RenderState *other) const {
((RenderState *)other)->_invert_composition_cache[this]._result = NULL;
((RenderState *)this)->_invert_composition_cache[other]._result = result;
cerr << " returning new result " << (void *)result.p() << "\n";
return result;
}
@ -472,17 +465,19 @@ remove(TypeHandle type) const {
////////////////////////////////////////////////////////////////////
void RenderState::
output(ostream &out) const {
out << get_type() << ":";
if (_attributes.empty()) {
out << "empty";
out << "(empty)";
} else {
Attributes::const_iterator ai = _attributes.begin();
out << (*ai)._type;
out << "(" << (*ai)._type;
++ai;
while (ai != _attributes.end()) {
out << " " << (*ai)._type;
++ai;
}
out << ")";
}
}

View File

@ -126,7 +126,8 @@ private:
// should be).
const RenderState *_self_compose;
// This is the actual set of data within the RenderState: a set of
private:
// This is the actual data within the RenderState: a set of
// RenderAttribs.
class Attribute {
public:

View File

@ -17,9 +17,10 @@
////////////////////////////////////////////////////////////////////
#include "pandaNode.h"
#include "nodeChain.h"
#include "textureAttrib.h"
#include "colorAttrib.h"
#include "transformAttrib.h"
#include "transformState.h"
#include "texture.h"
#include "qpgeomNode.h"
#include "geomTristrip.h"
@ -68,19 +69,25 @@ main(int argc, char *argv[]) {
qpGeomNode *g2 = new qpGeomNode("g2");
b->add_child(g2);
g2->add_geom(geom1, b->get_state());
g2->set_attrib(TransformAttrib::make_mat(LMatrix4f::translate_mat(10, 0, 0)));
g2->set_transform(TransformState::make_mat(LMatrix4f::translate_mat(10, 0, 0)));
cerr << "\n";
list_hierarchy(root, 0);
qpCullTraverser trav;
CullHandler cull_handler;
trav.set_cull_handler(&cull_handler);
cerr << "\n";
trav.traverse(root);
NodeChain ch_g1(g1);
cerr << ch_g1 << "\n";
cerr << "\n";
trav.traverse(root);
NodeChain ch_g2(g2);
cerr << ch_g2 << "\n";
cerr << *ch_g1.get_rel_transform(ch_g2) << "\n";
cerr << *ch_g2.get_rel_transform(ch_g1) << "\n";
cerr << *ch_g1.get_rel_state(ch_g2) << "\n";
cerr << *ch_g2.get_rel_state(ch_g1) << "\n";
cerr << *ch_g2.get_rel_transform(ch_g2) << "\n";
cerr << *ch_g2.get_rel_state(ch_g2) << "\n";
cerr << "\n";
return 0;

View File

@ -1,308 +0,0 @@
// Filename: transformAttrib.cxx
// Created by: drose (23Feb02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#include "transformAttrib.h"
#include "bamReader.h"
#include "bamWriter.h"
#include "datagram.h"
#include "datagramIterator.h"
#include "compose_matrix.h"
TypeHandle TransformAttrib::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: TransformAttrib::make_identity
// Access: Published, Static
// Description: Constructs an identity transform.
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) TransformAttrib::
make_identity() {
TransformAttrib *attrib = new TransformAttrib;
return return_new(attrib);
}
////////////////////////////////////////////////////////////////////
// Function: TransformAttrib::make_components
// Access: Published, Static
// Description: Makes a new TransformAttrib with the specified
// components.
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) TransformAttrib::
make_components(const LVecBase3f &pos, const LVecBase3f &hpr,
const LVecBase3f &scale) {
// Make a special-case check for the identity transform.
if (pos == LVecBase3f(0.0f, 0.0f, 0.0f) &&
hpr == LVecBase3f(0.0f, 0.0f, 0.0f) &&
scale == LVecBase3f(1.0f, 1.0f, 1.0f)) {
return make_identity();
}
TransformAttrib *attrib = new TransformAttrib;
attrib->_pos = pos;
attrib->_hpr = hpr;
attrib->_scale = scale;
attrib->_flags = F_components_given | F_components_known | F_has_components;
return return_new(attrib);
}
////////////////////////////////////////////////////////////////////
// Function: TransformAttrib::make_components
// Access: Published, Static
// Description: Makes a new TransformAttrib with the specified
// transformation matrix.
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) TransformAttrib::
make_mat(const LMatrix4f &mat) {
// Make a special-case check for the identity matrix.
if (mat == LMatrix4f::ident_mat()) {
return make_identity();
}
TransformAttrib *attrib = new TransformAttrib;
attrib->_mat = mat;
attrib->_flags = F_mat_known;
return return_new(attrib);
}
////////////////////////////////////////////////////////////////////
// Function: TransformAttrib::output
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void TransformAttrib::
output(ostream &out) const {
out << get_type() << ":";
if (is_identity()) {
out << "(identity)";
} else if (has_components()) {
out << "(";
if (get_pos() != LVecBase3f(0.0f, 0.0f, 0.0f)) {
out << "pos " << get_pos();
}
if (get_hpr() != LVecBase3f(0.0f, 0.0f, 0.0f)) {
out << "hpr " << get_hpr();
}
if (get_scale() != LVecBase3f(1.0f, 1.0f, 1.0f)) {
out << "scale " << get_scale();
}
out << ")";
} else {
out << get_mat();
}
}
////////////////////////////////////////////////////////////////////
// Function: TransformAttrib::compare_to_impl
// Access: Protected, Virtual
// Description: Intended to be overridden by derived TransformAttrib
// types to return a unique number indicating whether
// this TransformAttrib is equivalent to the other one.
//
// This should return 0 if the two TransformAttrib objects
// are equivalent, a number less than zero if this one
// should be sorted before the other one, and a number
// greater than zero otherwise.
//
// This will only be called with two TransformAttrib
// objects whose get_type() functions return the same.
////////////////////////////////////////////////////////////////////
int TransformAttrib::
compare_to_impl(const RenderAttrib *other) const {
const TransformAttrib *ta;
DCAST_INTO_R(ta, other, 0);
if (is_identity() != ta->is_identity()) {
return is_identity() < ta->is_identity();
}
if (is_identity()) {
// All identity transforms are equivalent to each other.
return 0;
}
bool components_given = (_flags & F_components_given) != 0;
bool ta_components_given = (ta->_flags & F_components_given) != 0;
if (components_given != ta_components_given) {
return components_given < ta_components_given;
}
if (components_given) {
// If the transform was specified componentwise, compare them
// componentwise.
int c = _pos.compare_to(ta->_pos);
if (c != 0) {
return c;
}
c = _hpr.compare_to(ta->_hpr);
if (c != 0) {
return c;
}
c = _scale.compare_to(ta->_hpr);
return c;
}
// Otherwise, compare the matrices.
return get_mat().compare_to(ta->get_mat());
}
////////////////////////////////////////////////////////////////////
// Function: TransformAttrib::compose_impl
// Access: Protected, Virtual
// Description: Intended to be overridden by derived RenderAttrib
// types to specify how two consecutive RenderAttrib
// objects of the same type interact.
////////////////////////////////////////////////////////////////////
CPT(RenderAttrib) TransformAttrib::
compose_impl(const RenderAttrib *other) const {
const TransformAttrib *ta;
DCAST_INTO_R(ta, other, other);
// Identity times anything, duh.
if (is_identity()) {
return ta;
} else if (ta->is_identity()) {
return this;
}
// Perhaps we should be smarter here if both transforms are
// componentwise, and multiply them componentwise.
LMatrix4f new_mat = get_mat() * ta->get_mat();
return make_mat(new_mat);
}
////////////////////////////////////////////////////////////////////
// Function: TransformAttrib::make_default_impl
// Access: Protected, Virtual
// Description: Intended to be overridden by derived TransformAttrib
// types to specify what the default property for a
// TransformAttrib of this type should be.
//
// This should return a newly-allocated TransformAttrib of
// the same type that corresponds to whatever the
// standard default for this kind of TransformAttrib is.
////////////////////////////////////////////////////////////////////
RenderAttrib *TransformAttrib::
make_default_impl() const {
return new TransformAttrib;
}
////////////////////////////////////////////////////////////////////
// Function: TransformAttrib::calc_singular
// Access: Private
// Description: Determines whether the transform is singular (i.e. it
// scales to zero, and has no inverse).
////////////////////////////////////////////////////////////////////
void TransformAttrib::
calc_singular() {
bool singular = false;
if (has_components()) {
// The matrix is singular if any component of its scale is 0.
singular = (_scale[0] == 0.0f || _scale[1] == 0.0f || _scale[2] == 0.0f);
} else {
// The matrix is singular if its determinant is zero.
const LMatrix4f &mat = get_mat();
singular = (mat.get_upper_3().determinant() == 0.0f);
}
if (singular) {
_flags |= F_is_singular;
}
_flags |= F_singular_known;
}
////////////////////////////////////////////////////////////////////
// Function: TransformAttrib::calc_components
// Access: Private
// Description: Derives the components from the matrix, if possible.
////////////////////////////////////////////////////////////////////
void TransformAttrib::
calc_components() {
bool possible = decompose_matrix(get_mat(), _scale, _hpr, _pos);
if (possible) {
// Some matrices can't be decomposed into scale, hpr, pos.
_flags |= F_has_components;
}
_flags |= F_components_known;
}
////////////////////////////////////////////////////////////////////
// Function: TransformAttrib::calc_mat
// Access: Private
// Description: Computes the matrix from the components.
////////////////////////////////////////////////////////////////////
void TransformAttrib::
calc_mat() {
compose_matrix(_mat, _scale, _hpr, _pos);
_flags |= F_mat_known;
}
////////////////////////////////////////////////////////////////////
// Function: TransformAttrib::register_with_read_factory
// Access: Public, Static
// Description: Tells the BamReader how to create objects of type
// TransformAttrib.
////////////////////////////////////////////////////////////////////
void TransformAttrib::
register_with_read_factory() {
BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
}
////////////////////////////////////////////////////////////////////
// Function: TransformAttrib::write_datagram
// Access: Public, Virtual
// Description: Writes the contents of this object to the datagram
// for shipping out to a Bam file.
////////////////////////////////////////////////////////////////////
void TransformAttrib::
write_datagram(BamWriter *manager, Datagram &dg) {
RenderAttrib::write_datagram(manager, dg);
}
////////////////////////////////////////////////////////////////////
// Function: TransformAttrib::make_from_bam
// Access: Protected, Static
// Description: This function is called by the BamReader's factory
// when a new object of type TransformAttrib is encountered
// in the Bam file. It should create the TransformAttrib
// and extract its information from the file.
////////////////////////////////////////////////////////////////////
TypedWritable *TransformAttrib::
make_from_bam(const FactoryParams &params) {
TransformAttrib *attrib = new TransformAttrib;
DatagramIterator scan;
BamReader *manager;
parse_params(params, scan, manager);
attrib->fillin(scan, manager);
return new_from_bam(attrib, manager);
}
////////////////////////////////////////////////////////////////////
// Function: TransformAttrib::fillin
// Access: Protected
// Description: This internal function is called by make_from_bam to
// read in all of the relevant data from the BamFile for
// the new TransformAttrib.
////////////////////////////////////////////////////////////////////
void TransformAttrib::
fillin(DatagramIterator &scan, BamReader *manager) {
RenderAttrib::fillin(scan, manager);
}

View File

@ -1,117 +0,0 @@
// Filename: transformAttrib.h
// Created by: drose (23Feb02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#ifndef TRANSFORMATTRIB_H
#define TRANSFORMATTRIB_H
#include "pandabase.h"
#include "renderAttrib.h"
#include "luse.h"
////////////////////////////////////////////////////////////////////
// Class : TransformAttrib
// Description : Indicates a coordinate-system transform on vertices.
// TransformAttribs are the primary means for storing
// transformations on the scene graph.
//
// Transforms may be specified in one of two ways:
// componentwise, with a pos-hpr-scale, or with an
// arbitrary transform matrix. If you specify a
// transform componentwise, it will remember its
// original components.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA TransformAttrib : public RenderAttrib {
private:
INLINE TransformAttrib();
PUBLISHED:
static CPT(RenderAttrib) make_identity();
static CPT(RenderAttrib) make_components(const LVecBase3f &pos,
const LVecBase3f &hpr,
const LVecBase3f &scale);
static CPT(RenderAttrib) make_mat(const LMatrix4f &mat);
INLINE bool is_identity() const;
INLINE bool is_singular() const;
INLINE bool has_components() const;
INLINE const LVecBase3f &get_pos() const;
INLINE const LVecBase3f &get_hpr() const;
INLINE const LVecBase3f &get_scale() const;
INLINE const LMatrix4f &get_mat() const;
public:
virtual void output(ostream &out) const;
protected:
virtual int compare_to_impl(const RenderAttrib *other) const;
virtual CPT(RenderAttrib) compose_impl(const RenderAttrib *other) const;
virtual RenderAttrib *make_default_impl() const;
private:
INLINE void check_singular() const;
INLINE void check_components() const;
INLINE void check_mat() const;
void calc_singular();
void calc_components();
void calc_mat();
enum Flags {
F_is_identity = 0x0001,
F_is_singular = 0x0002,
F_singular_known = 0x0004,
F_components_given = 0x0008,
F_components_known = 0x0010,
F_has_components = 0x0020,
F_mat_known = 0x0040,
};
LVecBase3f _pos, _hpr, _scale;
LMatrix4f _mat;
short _flags;
public:
static void register_with_read_factory();
virtual void write_datagram(BamWriter *manager, Datagram &dg);
protected:
static TypedWritable *make_from_bam(const FactoryParams &params);
void fillin(DatagramIterator &scan, BamReader *manager);
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
RenderAttrib::init_type();
register_type(_type_handle, "TransformAttrib",
RenderAttrib::get_class_type());
}
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
private:
static TypeHandle _type_handle;
};
#include "transformAttrib.I"
#endif

View File

@ -1,5 +1,5 @@
// Filename: transformAttrib.I
// Created by: drose (23Feb02)
// Filename: transformState.I
// Created by: drose (25Feb02)
//
////////////////////////////////////////////////////////////////////
//
@ -18,42 +18,95 @@
////////////////////////////////////////////////////////////////////
// Function: TransformAttrib::Constructor
// Access: Private
// Description: Use TransformAttrib::make() to construct a new
// TransformAttrib object.
// Function: TransformState::make_pos
// Access: Published, Static
// Description: Makes a new TransformState with the specified
// components.
////////////////////////////////////////////////////////////////////
INLINE TransformAttrib::
TransformAttrib() {
_flags = F_is_identity | F_singular_known;
INLINE CPT(TransformState) TransformState::
make_pos(const LVecBase3f &pos) {
return make_pos_hpr_scale(pos,
LVecBase3f(0.0f, 0.0f, 0.0f),
LVecBase3f(1.0f, 1.0f, 1.0f));
}
////////////////////////////////////////////////////////////////////
// Function: TransformAttrib::is_identity
// Function: TransformState::make_hpr
// Access: Published, Static
// Description: Makes a new TransformState with the specified
// components.
////////////////////////////////////////////////////////////////////
INLINE CPT(TransformState) TransformState::
make_hpr(const LVecBase3f &hpr) {
return make_pos_hpr_scale(LVecBase3f(0.0f, 0.0f, 0.0f),
hpr,
LVecBase3f(1.0f, 1.0f, 1.0f));
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::make_pos_hpr
// Access: Published, Static
// Description: Makes a new TransformState with the specified
// components.
////////////////////////////////////////////////////////////////////
INLINE CPT(TransformState) TransformState::
make_pos_hpr(const LVecBase3f &pos, const LVecBase3f &hpr) {
return make_pos_hpr_scale(pos, hpr,
LVecBase3f(1.0, 1.0f, 1.0f));
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::make_scale
// Access: Published, Static
// Description: Makes a new TransformState with the specified
// components.
////////////////////////////////////////////////////////////////////
INLINE CPT(TransformState) TransformState::
make_scale(float scale) {
return make_pos_hpr_scale(LVecBase3f(0.0f, 0.0f, 0.0f),
LVecBase3f(0.0f, 0.0f, 0.0f),
LVecBase3f(scale, scale, scale));
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::make_scale
// Access: Published, Static
// Description: Makes a new TransformState with the specified
// components.
////////////////////////////////////////////////////////////////////
INLINE CPT(TransformState) TransformState::
make_scale(const LVecBase3f &scale) {
return make_pos_hpr_scale(LVecBase3f(0.0f, 0.0f, 0.0f),
LVecBase3f(0.0f, 0.0f, 0.0f),
scale);
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::is_identity
// Access: Published
// Description: Returns true if the transform represents the identity
// matrix, false otherwise.
////////////////////////////////////////////////////////////////////
INLINE bool TransformAttrib::
INLINE bool TransformState::
is_identity() const {
return ((_flags & F_is_identity) != 0);
}
////////////////////////////////////////////////////////////////////
// Function: TransformAttrib::is_singular
// Function: TransformState::is_singular
// Access: Published
// Description: Returns true if the transform represents a singular
// transform (that is, it has a zero scale, and it
// cannot be inverted), or false otherwise.
////////////////////////////////////////////////////////////////////
INLINE bool TransformAttrib::
INLINE bool TransformState::
is_singular() const {
check_singular();
return ((_flags & F_is_singular) != 0);
}
////////////////////////////////////////////////////////////////////
// Function: TransformAttrib::has_components
// Function: TransformState::has_components
// Access: Published
// Description: Returns true if the transform can be described by
// separate pos, hpr, and scale components. Most
@ -69,20 +122,20 @@ is_singular() const {
// If this returns true, you may safely call get_pos(),
// etc., to retrieve the components.
////////////////////////////////////////////////////////////////////
INLINE bool TransformAttrib::
INLINE bool TransformState::
has_components() const {
check_components();
return ((_flags & F_has_components) != 0);
}
////////////////////////////////////////////////////////////////////
// Function: TransformAttrib::get_pos
// Function: TransformState::get_pos
// Access: Published
// Description: Returns the pos component of the transform. It is an
// error to call this if has_components() returned
// false.
////////////////////////////////////////////////////////////////////
INLINE const LVecBase3f &TransformAttrib::
INLINE const LVecBase3f &TransformState::
get_pos() const {
check_components();
nassertr(has_components(), _pos);
@ -90,13 +143,13 @@ get_pos() const {
}
////////////////////////////////////////////////////////////////////
// Function: TransformAttrib::get_hpr
// Function: TransformState::get_hpr
// Access: Published
// Description: Returns the hpr component of the transform. It is an
// error to call this if has_components() returned
// false.
////////////////////////////////////////////////////////////////////
INLINE const LVecBase3f &TransformAttrib::
INLINE const LVecBase3f &TransformState::
get_hpr() const {
check_components();
nassertr(has_components(), _hpr);
@ -104,13 +157,13 @@ get_hpr() const {
}
////////////////////////////////////////////////////////////////////
// Function: TransformAttrib::get_scale
// Function: TransformState::get_scale
// Access: Published
// Description: Returns the scale component of the transform. It is an
// error to call this if has_components() returned
// false.
////////////////////////////////////////////////////////////////////
INLINE const LVecBase3f &TransformAttrib::
INLINE const LVecBase3f &TransformState::
get_scale() const {
check_components();
nassertr(has_components(), _scale);
@ -118,55 +171,55 @@ get_scale() const {
}
////////////////////////////////////////////////////////////////////
// Function: TransformAttrib::get_mat
// Function: TransformState::get_mat
// Access: Published
// Description: Returns the matrix that describes the transform.
////////////////////////////////////////////////////////////////////
INLINE const LMatrix4f &TransformAttrib::
INLINE const LMatrix4f &TransformState::
get_mat() const {
check_mat();
return _mat;
}
////////////////////////////////////////////////////////////////////
// Function: TransformAttrib::check_singular
// Function: TransformState::check_singular
// Access: Private
// Description: Ensures that we know whether the matrix is singular.
////////////////////////////////////////////////////////////////////
INLINE void TransformAttrib::
INLINE void TransformState::
check_singular() const {
// This pretends to be a const function, even though it's not,
// because it only updates a transparent cache value.
if ((_flags & F_singular_known) == 0) {
((TransformAttrib *)this)->calc_singular();
((TransformState *)this)->calc_singular();
}
}
////////////////////////////////////////////////////////////////////
// Function: TransformAttrib::check_components
// Function: TransformState::check_components
// Access: Private
// Description: Ensures that we know the components of the transform
// (or that we know they cannot be derived).
////////////////////////////////////////////////////////////////////
INLINE void TransformAttrib::
INLINE void TransformState::
check_components() const {
// This pretends to be a const function, even though it's not,
// because it only updates a transparent cache value.
if ((_flags & F_components_known) == 0) {
((TransformAttrib *)this)->calc_components();
((TransformState *)this)->calc_components();
}
}
////////////////////////////////////////////////////////////////////
// Function: TransformAttrib::check_mat
// Function: TransformState::check_mat
// Access: Private
// Description: Ensures that we know the overall matrix.
////////////////////////////////////////////////////////////////////
INLINE void TransformAttrib::
INLINE void TransformState::
check_mat() const {
// This pretends to be a const function, even though it's not,
// because it only updates a transparent cache value.
if ((_flags & F_mat_known) == 0) {
((TransformAttrib *)this)->calc_mat();
((TransformState *)this)->calc_mat();
}
}

View File

@ -0,0 +1,650 @@
// Filename: transformState.cxx
// Created by: drose (25Feb02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#include "transformState.h"
#include "compose_matrix.h"
#include "bamReader.h"
#include "bamWriter.h"
#include "datagramIterator.h"
#include "indent.h"
#include "compareTo.h"
TransformState::States TransformState::_states;
CPT(TransformState) TransformState::_identity_state;
TypeHandle TransformState::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: TransformState::Constructor
// Access: Protected
// Description: Actually, this could be a private constructor, since
// no one inherits from TransformState, but gcc gives us a
// spurious warning if all constructors are private.
////////////////////////////////////////////////////////////////////
TransformState::
TransformState() {
_saved_entry = _states.end();
_self_compose = (TransformState *)NULL;
_flags = F_is_identity | F_singular_known;
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::Copy Constructor
// Access: Private
// Description: TransformStates are not meant to be copied.
////////////////////////////////////////////////////////////////////
TransformState::
TransformState(const TransformState &) {
nassertv(false);
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::Copy Assignment Operator
// Access: Private
// Description: TransformStates are not meant to be copied.
////////////////////////////////////////////////////////////////////
void TransformState::
operator = (const TransformState &) {
nassertv(false);
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::Destructor
// Access: Public, Virtual
// Description: The destructor is responsible for removing the
// TransformState from the global set if it is there.
////////////////////////////////////////////////////////////////////
TransformState::
~TransformState() {
// Remove the deleted TransformState object from the global pool.
if (_saved_entry != _states.end()) {
_states.erase(_saved_entry);
_saved_entry = _states.end();
}
// Now make sure we clean up all other floating pointers to the
// TransformState. These may be scattered around in the various
// CompositionCaches from other TransformState objects.
// Fortunately, since we added CompositionCache records in pairs, we
// know exactly the set of TransformState objects that have us in their
// cache: it's the same set of TransformState objects that we have in
// our own cache.
// We do need to put some thought into this loop, because as we
// clear out cache entries we'll cause other TransformState objects to
// destruct, which could cause things to get pulled out of our own
// _composition_cache map. We don't want to get bitten by this
// cascading effect.
CompositionCache::iterator ci;
ci = _composition_cache.begin();
while (ci != _composition_cache.end()) {
{
PT(TransformState) other = (TransformState *)(*ci).first;
Composition comp = (*ci).second;
// We should never have a reflexive entry in this map. If we
// do, something got screwed up elsewhere.
nassertv(other != (const TransformState *)this);
// Now we're holding a reference count to the other state, as well
// as to the computed result (if any), so neither object will be
// tempted to destruct. Go ahead and remove ourselves from the
// other cache.
other->_composition_cache.erase(this);
// It's all right if the other state destructs now, since it
// won't try to remove itself from our own composition cache any
// more. Someone might conceivably delete the *next* entry,
// though, so we should be sure to let all that deleting finish
// up before we attempt to increment ci, by closing the scope
// here.
}
// Now it's safe to increment ci, because the current cache entry
// has not gone away, and if the next one has, by now it's safely
// gone.
++ci;
}
// A similar bit of code for the invert cache.
ci = _invert_composition_cache.begin();
while (ci != _invert_composition_cache.end()) {
{
PT(TransformState) other = (TransformState *)(*ci).first;
Composition comp = (*ci).second;
nassertv(other != (const TransformState *)this);
other->_invert_composition_cache.erase(this);
}
++ci;
}
// Also, if we called compose(this) at some point and the return
// value was something other than this, we need to decrement the
// associated reference count.
if (_self_compose != (TransformState *)NULL && _self_compose != this) {
unref_delete((TransformState *)_self_compose);
}
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::operator <
// Access: Public
// Description: Provides an arbitrary ordering among all unique
// TransformStates, so we can store the essentially
// different ones in a big set and throw away the rest.
//
// This method is not needed outside of the TransformState
// class because all equivalent TransformState objects are
// guaranteed to share the same pointer; thus, a pointer
// comparison is always sufficient.
////////////////////////////////////////////////////////////////////
bool TransformState::
operator < (const TransformState &other) const {
bool components_given = (_flags & F_components_given) != 0;
bool other_components_given = (other._flags & F_components_given) != 0;
if (components_given != other_components_given) {
return components_given < other_components_given;
}
if (components_given) {
// If the transform was specified componentwise, compare them
// componentwise.
int c = _pos.compare_to(other._pos);
if (c != 0) {
return c < 0;
}
c = _hpr.compare_to(other._hpr);
if (c != 0) {
return c < 0;
}
c = _scale.compare_to(other._hpr);
return c < 0;
}
// Otherwise, compare the matrices.
return get_mat() < other.get_mat();
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::make_identity
// Access: Published, Static
// Description: Constructs an identity transform.
////////////////////////////////////////////////////////////////////
CPT(TransformState) TransformState::
make_identity() {
// The identity state is asked for so often, we make it a special case
// and store a pointer forever once we find it the first time.
if (_identity_state == (TransformState *)NULL) {
TransformState *state = new TransformState;
_identity_state = return_new(state);
}
return _identity_state;
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::make_pos_hpr_scale
// Access: Published, Static
// Description: Makes a new TransformState with the specified
// components.
////////////////////////////////////////////////////////////////////
CPT(TransformState) TransformState::
make_pos_hpr_scale(const LVecBase3f &pos, const LVecBase3f &hpr,
const LVecBase3f &scale) {
// Make a special-case check for the identity transform.
if (pos == LVecBase3f(0.0f, 0.0f, 0.0f) &&
hpr == LVecBase3f(0.0f, 0.0f, 0.0f) &&
scale == LVecBase3f(1.0f, 1.0f, 1.0f)) {
return make_identity();
}
TransformState *attrib = new TransformState;
attrib->_pos = pos;
attrib->_hpr = hpr;
attrib->_scale = scale;
attrib->_flags = F_components_given | F_components_known | F_has_components;
return return_new(attrib);
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::make_mat
// Access: Published, Static
// Description: Makes a new TransformState with the specified
// transformation matrix.
////////////////////////////////////////////////////////////////////
CPT(TransformState) TransformState::
make_mat(const LMatrix4f &mat) {
// Make a special-case check for the identity matrix.
if (mat == LMatrix4f::ident_mat()) {
return make_identity();
}
TransformState *attrib = new TransformState;
attrib->_mat = mat;
attrib->_flags = F_mat_known;
return return_new(attrib);
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::compose
// Access: Published
// Description: Returns a new TransformState object that represents the
// composition of this state with the other state.
//
// The result of this operation is cached, and will be
// retained as long as both this TransformState object and
// the other TransformState object continue to exist.
// Should one of them destruct, the cached entry will be
// removed, and its pointer will be allowed to destruct
// as well.
////////////////////////////////////////////////////////////////////
CPT(TransformState) TransformState::
compose(const TransformState *other) const {
// This method isn't strictly const, because it updates the cache,
// but we pretend that it is because it's only a cache which is
// transparent to the rest of the interface.
// We handle identity as a trivial special case.
if (is_identity()) {
return other;
}
if (other->is_identity()) {
return this;
}
if (other == this) {
// compose(this) has to be handled as a special case, because the
// caching problem is so different.
if (_self_compose != (TransformState *)NULL) {
return _self_compose;
}
CPT(TransformState) result = do_compose(this);
((TransformState *)this)->_self_compose = result;
if (result != (const TransformState *)this) {
// If the result of compose(this) is something other than this,
// explicitly increment the reference count. We have to be sure
// to decrement it again later, in our destructor.
_self_compose->ref();
// (If the result was just this again, we still store the
// result, but we don't increment the reference count, since
// that would be a self-referential leak. What a mess this is.)
}
return _self_compose;
}
// Is this composition already cached?
CompositionCache::const_iterator ci = _composition_cache.find(other);
if (ci != _composition_cache.end()) {
const Composition &comp = (*ci).second;
if (comp._result == (const TransformState *)NULL) {
// Well, it wasn't cached already, but we already had an entry
// (probably created for the reverse direction), so use the same
// entry to store the new result.
((Composition &)comp)._result = do_compose(other);
}
// Here's the cache!
return comp._result;
}
// We need to make a new cache entry, both in this object and in the
// other object. We make both records so the other TransformState
// object will know to delete the entry from this object when it
// destructs, and vice-versa.
// The cache entry in this object is the only one that indicates the
// result; the other will be NULL for now.
CPT(TransformState) result = do_compose(other);
// We store them in this order, on the off-chance that other is the
// same as this, a degenerate case which is still worth supporting.
((TransformState *)other)->_composition_cache[this]._result = NULL;
((TransformState *)this)->_composition_cache[other]._result = result;
return result;
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::invert_compose
// Access: Published
// Description: Returns a new TransformState object that represents the
// composition of this state's inverse with the other
// state.
//
// This is similar to compose(), but is particularly
// useful for computing the relative state of a node as
// viewed from some other node.
////////////////////////////////////////////////////////////////////
CPT(TransformState) TransformState::
invert_compose(const TransformState *other) const {
// This method isn't strictly const, because it updates the cache,
// but we pretend that it is because it's only a cache which is
// transparent to the rest of the interface.
// We handle identity as a trivial special case.
if (is_identity()) {
return other;
}
// Unlike compose(), the case of other->is_identity() is not quite as
// trivial for invert_compose().
if (other == this) {
// a->invert_compose(a) always produces identity.
return make_identity();
}
// Is this composition already cached?
CompositionCache::const_iterator ci = _invert_composition_cache.find(other);
if (ci != _invert_composition_cache.end()) {
const Composition &comp = (*ci).second;
if (comp._result == (const TransformState *)NULL) {
// Well, it wasn't cached already, but we already had an entry
// (probably created for the reverse direction), so use the same
// entry to store the new result.
((Composition &)comp)._result = do_invert_compose(other);
}
// Here's the cache!
return comp._result;
}
// We need to make a new cache entry, both in this object and in the
// other object. We make both records so the other TransformState
// object will know to delete the entry from this object when it
// destructs, and vice-versa.
// The cache entry in this object is the only one that indicates the
// result; the other will be NULL for now.
CPT(TransformState) result = do_invert_compose(other);
// We store them in this order, on the off-chance that other is the
// same as this, a degenerate case which is still worth supporting.
((TransformState *)other)->_invert_composition_cache[this]._result = NULL;
((TransformState *)this)->_invert_composition_cache[other]._result = result;
return result;
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::output
// Access: Published, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void TransformState::
output(ostream &out) const {
out << get_type() << ":";
if (is_identity()) {
out << "(identity)";
} else if (has_components()) {
out << "(";
if (get_pos() != LVecBase3f(0.0f, 0.0f, 0.0f)) {
out << "pos " << get_pos();
}
if (get_hpr() != LVecBase3f(0.0f, 0.0f, 0.0f)) {
out << "hpr " << get_hpr();
}
if (get_scale() != LVecBase3f(1.0f, 1.0f, 1.0f)) {
out << "scale " << get_scale();
}
out << ")";
} else {
out << get_mat();
}
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::write
// Access: Published, Virtual
// Description:
////////////////////////////////////////////////////////////////////
void TransformState::
write(ostream &out, int indent_level) const {
indent(out, indent_level) << *this << "\n";
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::return_new
// Access: Private, Static
// Description: This function is used to share a common TransformState
// pointer for all equivalent TransformState objects.
//
// See the similar logic in RenderAttrib. The idea is
// to create a new TransformState object and pass it
// through this function, which will share the pointer
// with a previously-created TransformState object if it is
// equivalent.
////////////////////////////////////////////////////////////////////
CPT(TransformState) TransformState::
return_new(TransformState *state) {
nassertr(state != (TransformState *)NULL, state);
// This should be a newly allocated pointer, not one that was used
// for anything else.
nassertr(state->_saved_entry == _states.end(), state);
// Save the state in a local PointerTo so that it will be freed at
// the end of this function if no one else uses it.
CPT(TransformState) pt_state = state;
pair<States::iterator, bool> result = _states.insert(state);
if (result.second) {
// The state was inserted; save the iterator and return the
// input state.
state->_saved_entry = result.first;
return pt_state;
}
// The state was not inserted; there must be an equivalent one
// already in the set. Return that one.
return *(result.first);
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::do_compose
// Access: Private
// Description: The private implemention of compose(); this actually
// composes two TransformStates, without bothering with the
// cache.
////////////////////////////////////////////////////////////////////
CPT(TransformState) TransformState::
do_compose(const TransformState *other) const {
LMatrix4f new_mat = get_mat() * other->get_mat();
return make_mat(new_mat);
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::do_invert_compose
// Access: Private
// Description: The private implemention of invert_compose().
////////////////////////////////////////////////////////////////////
CPT(TransformState) TransformState::
do_invert_compose(const TransformState *other) const {
LMatrix4f new_mat;
new_mat.invert_from(get_mat());
new_mat = new_mat * other->get_mat();
return make_mat(new_mat);
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::calc_singular
// Access: Private
// Description: Determines whether the transform is singular (i.e. it
// scales to zero, and has no inverse).
////////////////////////////////////////////////////////////////////
void TransformState::
calc_singular() {
bool singular = false;
if (has_components()) {
// The matrix is singular if any component of its scale is 0.
singular = (_scale[0] == 0.0f || _scale[1] == 0.0f || _scale[2] == 0.0f);
} else {
// The matrix is singular if its determinant is zero.
const LMatrix4f &mat = get_mat();
singular = (mat.get_upper_3().determinant() == 0.0f);
}
if (singular) {
_flags |= F_is_singular;
}
_flags |= F_singular_known;
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::calc_components
// Access: Private
// Description: Derives the components from the matrix, if possible.
////////////////////////////////////////////////////////////////////
void TransformState::
calc_components() {
if ((_flags & F_is_identity) != 0) {
_scale.set(1.0f, 1.0f, 1.0f);
_hpr.set(0.0f, 0.0f, 0.0f);
_pos.set(0.0f, 0.0f, 0.0f);
_flags |= F_has_components;
} else {
// If we don't have components and we're not identity, the only
// other explanation is that we were constructed via a matrix.
nassertv((_flags & F_mat_known) != 0);
bool possible = decompose_matrix(get_mat(), _scale, _hpr, _pos);
if (possible) {
// Some matrices can't be decomposed into scale, hpr, pos.
_flags |= F_has_components;
}
}
_flags |= F_components_known;
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::calc_mat
// Access: Private
// Description: Computes the matrix from the components.
////////////////////////////////////////////////////////////////////
void TransformState::
calc_mat() {
if ((_flags & F_is_identity) != 0) {
_mat = LMatrix4f::ident_mat();
} else {
// If we don't have a matrix and we're not identity, the only
// other explanation is that we were constructed via components.
nassertv((_flags & F_components_known) != 0);
compose_matrix(_mat, _scale, _hpr, _pos);
}
_flags |= F_mat_known;
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::register_with_read_factory
// Access: Public, Static
// Description: Tells the BamReader how to create objects of type
// TransformState.
////////////////////////////////////////////////////////////////////
void TransformState::
register_with_read_factory() {
BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::write_datagram
// Access: Public, Virtual
// Description: Writes the contents of this object to the datagram
// for shipping out to a Bam file.
////////////////////////////////////////////////////////////////////
void TransformState::
write_datagram(BamWriter *manager, Datagram &dg) {
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::finalize
// Access: Public, Virtual
// Description: Method to ensure that any necessary clean up tasks
// that have to be performed by this object are performed
////////////////////////////////////////////////////////////////////
void TransformState::
finalize() {
// Unref the pointer that we explicitly reffed in make_from_bam().
unref();
// We should never get back to zero after unreffing our own count,
// because we expect to have been stored in a pointer somewhere. If
// we do get to zero, it's a memory leak; the way to avoid this is
// to call unref_delete() above instead of unref(), but this is
// dangerous to do from within a virtual function.
nassertv(get_ref_count() != 0);
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::make_from_bam
// Access: Protected, Static
// Description: This function is called by the BamReader's factory
// when a new object of type TransformState is encountered
// in the Bam file. It should create the TransformState
// and extract its information from the file.
////////////////////////////////////////////////////////////////////
TypedWritable *TransformState::
make_from_bam(const FactoryParams &params) {
TransformState *state = new TransformState;
DatagramIterator scan;
BamReader *manager;
parse_params(params, scan, manager);
state->fillin(scan, manager);
return new_from_bam(state, manager);
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::new_from_bam
// Access: Protected, Static
// Description: Uniquifies the pointer for a TransformState object just
// created from a bam file, and preserves its reference
// count correctly.
////////////////////////////////////////////////////////////////////
TypedWritable *TransformState::
new_from_bam(TransformState *state, BamReader *manager) {
// First, uniquify the pointer.
CPT(TransformState) pointer = return_new(state);
// But now we have a problem, since we have to hold the reference
// count and there's no way to return a TypedWritable while still
// holding the reference count! We work around this by explicitly
// upping the count, and also setting a finalize() callback to down
// it later.
if (pointer == state) {
pointer->ref();
manager->register_finalize(state);
}
// We have to cast the pointer back to non-const, because the bam
// reader expects that.
return (TransformState *)pointer.p();
}
////////////////////////////////////////////////////////////////////
// Function: TransformState::fillin
// Access: Protected
// Description: This internal function is called by make_from_bam to
// read in all of the relevant data from the BamFile for
// the new TransformState.
////////////////////////////////////////////////////////////////////
void TransformState::
fillin(DatagramIterator &scan, BamReader *manager) {
}

View File

@ -0,0 +1,187 @@
// Filename: transformState.h
// Created by: drose (25Feb02)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://www.panda3d.org/license.txt .
//
// To contact the maintainers of this program write to
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#ifndef TRANSFORMSTATE_H
#define TRANSFORMSTATE_H
#include "pandabase.h"
#include "typedWritableReferenceCount.h"
#include "pointerTo.h"
#include "indirectLess.h"
#include "luse.h"
#include "pset.h"
class GraphicsStateGuardianBase;
////////////////////////////////////////////////////////////////////
// Class : TransformState
// Description : Indicates a coordinate-system transform on vertices.
// TransformStates are the primary means for storing
// transformations on the scene graph.
//
// Transforms may be specified in one of two ways:
// componentwise, with a pos-hpr-scale, or with an
// arbitrary transform matrix. If you specify a
// transform componentwise, it will remember its
// original components.
//
// TransformState objects are managed very much like
// RenderState objects. They are immutable and
// reference-counted automatically.
//
// You should not attempt to create or modify a
// TransformState object directly. Instead, call one of
// the make() functions to create one for you. And
// instead of modifying a TransformState object, create a
// new one.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA TransformState : public TypedWritableReferenceCount {
protected:
TransformState();
private:
TransformState(const TransformState &copy);
void operator = (const TransformState &copy);
public:
virtual ~TransformState();
bool operator < (const TransformState &other) const;
PUBLISHED:
static CPT(TransformState) make_identity();
INLINE static CPT(TransformState) make_pos(const LVecBase3f &pos);
INLINE static CPT(TransformState) make_hpr(const LVecBase3f &hpr);
INLINE static CPT(TransformState) make_pos_hpr(const LVecBase3f &pos,
const LVecBase3f &hpr);
INLINE static CPT(TransformState) make_scale(float scale);
INLINE static CPT(TransformState) make_scale(const LVecBase3f &scale);
static CPT(TransformState) make_pos_hpr_scale(const LVecBase3f &pos,
const LVecBase3f &hpr,
const LVecBase3f &scale);
static CPT(TransformState) make_mat(const LMatrix4f &mat);
INLINE bool is_identity() const;
INLINE bool is_singular() const;
INLINE bool has_components() const;
INLINE const LVecBase3f &get_pos() const;
INLINE const LVecBase3f &get_hpr() const;
INLINE const LVecBase3f &get_scale() const;
INLINE const LMatrix4f &get_mat() const;
CPT(TransformState) compose(const TransformState *other) const;
CPT(TransformState) invert_compose(const TransformState *other) const;
void output(ostream &out) const;
void write(ostream &out, int indent_level) const;
private:
static CPT(TransformState) return_new(TransformState *state);
CPT(TransformState) do_compose(const TransformState *other) const;
CPT(TransformState) do_invert_compose(const TransformState *other) const;
private:
typedef pset<const TransformState *, IndirectLess<TransformState> > States;
static States _states;
static CPT(TransformState) _identity_state;
// This iterator records the entry corresponding to this TransformState
// object in the above global set. We keep the iterator around so
// we can remove it when the TransformState destructs.
States::iterator _saved_entry;
// This data structure manages the job of caching the composition of
// two TransformStates. It's complicated because we have to be sure to
// remove the entry if *either* of the input TransformStates destructs.
// To implement this, we always record Composition entries in pairs,
// one in each of the two involved TransformState objects.
class Composition {
public:
CPT(TransformState) _result;
};
typedef pmap<const TransformState *, Composition> CompositionCache;
CompositionCache _composition_cache;
CompositionCache _invert_composition_cache;
// Thise pointer is used to cache the result of compose(this). This
// has to be a special case, because we have to handle the reference
// counts carefully so that we don't leak.
const TransformState *_self_compose;
private:
// This is the actual data within the TransformState.
INLINE void check_singular() const;
INLINE void check_components() const;
INLINE void check_mat() const;
void calc_singular();
void calc_components();
void calc_mat();
enum Flags {
F_is_identity = 0x0001,
F_is_singular = 0x0002,
F_singular_known = 0x0004,
F_components_given = 0x0008,
F_components_known = 0x0010,
F_has_components = 0x0020,
F_mat_known = 0x0040,
};
LVecBase3f _pos, _hpr, _scale;
LMatrix4f _mat;
short _flags;
public:
static void register_with_read_factory();
virtual void write_datagram(BamWriter *manager, Datagram &dg);
virtual void finalize();
protected:
static TypedWritable *make_from_bam(const FactoryParams &params);
static TypedWritable *new_from_bam(TransformState *state, BamReader *manager);
void fillin(DatagramIterator &scan, BamReader *manager);
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
TypedWritableReferenceCount::init_type();
register_type(_type_handle, "TransformState",
TypedWritableReferenceCount::get_class_type());
}
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
private:
static TypeHandle _type_handle;
};
INLINE ostream &operator << (ostream &out, const TransformState &state) {
state.output(out);
return out;
}
#include "transformState.I"
#endif

View File

@ -23,7 +23,7 @@
#include "graphicsPipe.h"
#include "interactiveGraphicsPipe.h"
#include "displayRegion.h"
#include "camera.h"
#include "qpcamera.h"
#include "perspectiveLens.h"
#include "pandaNode.h"
#include "textureAttrib.h"
@ -33,6 +33,32 @@
#include "texture.h"
#include "texturePool.h"
// These are in support of legacy data graph operations.
#include "namedNode.h"
#include "mouse.h"
#include "mouseWatcher.h"
#include "trackball.h"
#include "transform2sg.h"
#include "dataRelation.h"
#include "dataGraphTraversal.h"
#include "buttonThrower.h"
#include "modifierButtons.h"
#include "keyboardButton.h"
#include "event.h"
#include "eventQueue.h"
#include "eventHandler.h"
// Use dconfig to read a few Configrc variables.
Configure(config_pview);
ConfigureFn(config_pview) {
}
static const int win_width = config_pview.GetInt("win-width", 640);
static const int win_height = config_pview.GetInt("win-height", 480);
// As long as this is true, the main loop will continue running.
bool run_flag = true;
PT(GraphicsPipe)
make_pipe() {
// We use the GraphicsPipe factory to make us a renderable pipe
@ -59,20 +85,25 @@ make_pipe() {
return pipe;
}
PT(Camera)
setup_window(GraphicsPipe *pipe, GraphicsEngine *engine) {
PT(GraphicsWindow)
make_window(GraphicsPipe *pipe, GraphicsEngine *engine) {
// Now create a window on that pipe.
GraphicsWindow::Properties window_prop;
window_prop._xorg = 0;
window_prop._yorg = 0;
window_prop._xsize = 640;
window_prop._ysize = 480;
window_prop._xsize = win_width;
window_prop._ysize = win_height;
window_prop._title = "Panda Viewer";
// window_prop._fullscreen = true;
PT(GraphicsWindow) window = pipe->make_window(window_prop);
engine->add_window(window);
return window;
}
PT(qpCamera)
make_camera(GraphicsWindow *window) {
// Get the first channel on the window. This will be the only
// channel on non-SGI hardware.
PT(GraphicsChannel) channel = window->get_channel(0);
@ -84,11 +115,11 @@ setup_window(GraphicsPipe *pipe, GraphicsEngine *engine) {
PT(DisplayRegion) dr = layer->make_display_region();
// Finally, we need a camera to associate with the display region.
PT(Camera) camera = new Camera;
PT(qpCamera) camera = new qpCamera("camera");
PT(Lens) lens = new PerspectiveLens;
lens->set_film_size(640, 480);
lens->set_film_size(win_width, win_height);
camera->set_lens(lens);
dr->set_camera(camera);
dr->set_qpcamera(NodeChain(camera));
return camera;
}
@ -102,9 +133,9 @@ make_default_geometry(PandaNode *parent) {
PTA_Colorf colors;
PTA_ushort cindex;
coords.push_back(Vertexf::rfu(0.0, 20.0, 0.0));
coords.push_back(Vertexf::rfu(1.0, 20.0, 0.0));
coords.push_back(Vertexf::rfu(0.0, 20.0, 1.0));
coords.push_back(Vertexf::rfu(0.0, 0.0, 0.0));
coords.push_back(Vertexf::rfu(1.0, 0.0, 0.0));
coords.push_back(Vertexf::rfu(0.0, 0.0, 1.0));
uvs.push_back(TexCoordf(0.0, 0.0));
uvs.push_back(TexCoordf(1.0, 0.0));
uvs.push_back(TexCoordf(0.0, 1.0));
@ -153,6 +184,39 @@ get_models(PandaNode *parent, int argc, char *argv[]) {
*/
}
NamedNode *
setup_mouse(NamedNode *data_root, GraphicsWindow *window) {
MouseAndKeyboard *mouse = new MouseAndKeyboard(window, 0);
new DataRelation(data_root, mouse);
// Create a ButtonThrower to throw events from the keyboard.
PT(ButtonThrower) bt = new ButtonThrower("kb-events");
ModifierButtons mods;
mods.add_button(KeyboardButton::shift());
mods.add_button(KeyboardButton::control());
mods.add_button(KeyboardButton::alt());
bt->set_modifier_buttons(mods);
new DataRelation(mouse, bt);
return mouse;
}
void
setup_trackball(NamedNode *mouse, qpCamera *camera) {
PT(Trackball) trackball = new Trackball("trackball");
trackball->set_pos(LVector3f::forward() * 50.0);
new DataRelation(mouse, trackball);
PT(Transform2SG) tball2cam = new Transform2SG("tball2cam");
tball2cam->set_node(camera);
new DataRelation(trackball, tball2cam);
}
void
event_esc(CPT_Event) {
// The Escape or q key was pressed. Exit the application.
run_flag = false;
}
int
main(int argc, char *argv[]) {
@ -163,33 +227,40 @@ main(int argc, char *argv[]) {
GraphicsEngine *engine = new GraphicsEngine;
// Now open a window and get a camera.
PT(Camera) camera = setup_window(pipe, engine);
PT(GraphicsWindow) window = make_window(pipe, engine);
PT(qpCamera) camera = make_camera(window);
// Now we just need to make a scene graph for the camera to render.
PT(PandaNode) render = new PandaNode("render");
camera->set_qpscene(render);
render->add_child(camera);
camera->set_scene(render);
// Set up a data graph for tracking user input. For now, this uses
// the old-style graph interface.
PT(NamedNode) data_root = new NamedNode("data_root");
NamedNode *mouse = setup_mouse(data_root, window);
setup_trackball(mouse, camera);
// Use an event handler to manage keyboard events.
EventHandler event_handler(EventQueue::get_global_event_queue());
event_handler.add_hook("escape", event_esc);
event_handler.add_hook("q", event_esc);
// Put something in the scene graph to look at.
get_models(render, argc, argv);
/*
// Put the scene a distance in front of our face so we can see it.
LMatrix4f mat = LMatrix4f::translate_mat(0, 20, 0);
TransformTransition *tt = new TransformTransition(mat);
render_arc->set_transition(tt);
*/
// This is our main update loop. Run for 5 seconds, then shut down.
// This is our main update loop. Loop here until someone
// (e.g. event_esc) sets run_flag to false.
ClockObject *clock = ClockObject::get_global_clock();
clock->tick();
double now = clock->get_frame_time();
while (clock->get_frame_time() - now < 5.0) {
while (run_flag) {
clock->tick();
traverse_data_graph(data_root);
event_handler.process_events();
engine->render_frame();
}
cerr << "Exiting.\n";
delete engine;
return (0);
}

View File

@ -4,7 +4,7 @@
#begin lib_target
#define TARGET tform
#define LOCAL_LIBS \
dgraph graph linmath sgattrib display event putil gobj gsgbase \
dgraph pgraph graph linmath sgattrib display event putil gobj gsgbase \
mathutil sgraph device sgraphutil
#define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx

View File

@ -20,6 +20,7 @@
#include "nodeRelation.h"
#include "transformTransition.h"
#include "transformState.h"
#include "matrixDataTransition.h"
#include "allTransitionsWrapper.h"
@ -36,6 +37,7 @@ TypeHandle Transform2SG::_transform_type;
Transform2SG::
Transform2SG(const string &name) : DataNode(name) {
_arc = NULL;
_node = NULL;
}
@ -60,6 +62,27 @@ get_arc() const {
return _arc;
}
////////////////////////////////////////////////////////////////////
// Function: Transform2SG::set_node
// Access: Public
// Description: Sets the node that this node will adjust.
////////////////////////////////////////////////////////////////////
void Transform2SG::
set_node(PandaNode *node) {
_node = node;
}
////////////////////////////////////////////////////////////////////
// Function: Transform2SG::get_node
// Access: Public
// Description: Returns the node that this node will adjust, or NULL
// if the node has not yet been set.
////////////////////////////////////////////////////////////////////
PandaNode *Transform2SG::
get_node() const {
return _node;
}
////////////////////////////////////////////////////////////////////
// Function: Transform2SG::transmit_data
@ -70,9 +93,14 @@ void Transform2SG::
transmit_data(AllTransitionsWrapper &data) {
const NodeTransition *transform = data.get_transition(_transform_type);
if (transform != (NodeTransition *)NULL && _arc != (NodeRelation *)NULL) {
if (transform != (NodeTransition *)NULL) {
const LMatrix4f &mat = DCAST(MatrixDataTransition, transform)->get_value();
_arc->set_transition(new TransformTransition(mat));
if (_arc != (NodeRelation *)NULL) {
_arc->set_transition(new TransformTransition(mat));
}
if (_node != (PandaNode *)NULL) {
_node->set_transform(TransformState::make_mat(mat));
}
}
// Clear the data below us.

View File

@ -22,6 +22,7 @@
#include "pandabase.h"
#include "dataNode.h"
#include "pandaNode.h"
class NodeRelation;
@ -39,8 +40,12 @@ PUBLISHED:
void set_arc(NodeRelation *arc);
NodeRelation *get_arc() const;
void set_node(PandaNode *node);
PandaNode *get_node() const;
private:
NodeRelation *_arc;
PandaNode *_node;
////////////////////////////////////////////////////////////////////
// From parent class DataNode