mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 02:42:49 -04:00
846 lines
30 KiB
C++
846 lines
30 KiB
C++
// Filename: qpgeom.cxx
|
|
// Created by: drose (06Mar05)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PANDA 3D SOFTWARE
|
|
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
|
|
//
|
|
// All use of this software is subject to the terms of the Panda 3d
|
|
// Software license. You should have received a copy of this license
|
|
// along with this source code; you will also find a current copy of
|
|
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
|
|
//
|
|
// To contact the maintainers of this program write to
|
|
// panda3d-general@lists.sourceforge.net .
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#include "qpgeom.h"
|
|
#include "qpgeomVertexReader.h"
|
|
#include "qpgeomVertexRewriter.h"
|
|
#include "pStatTimer.h"
|
|
#include "bamReader.h"
|
|
#include "bamWriter.h"
|
|
|
|
UpdateSeq qpGeom::_next_modified;
|
|
TypeHandle qpGeom::_type_handle;
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::Constructor
|
|
// Access: Published
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
qpGeom::
|
|
qpGeom() {
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::Copy Constructor
|
|
// Access: Published
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
qpGeom::
|
|
qpGeom(const qpGeom ©) :
|
|
/*
|
|
TypedWritableReferenceCount(copy),
|
|
BoundedObject(copy),
|
|
*/
|
|
Geom(copy),
|
|
_cycler(copy._cycler)
|
|
{
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::Copy Assignment Operator
|
|
// Access: Published
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
void qpGeom::
|
|
operator = (const qpGeom ©) {
|
|
/*
|
|
TypedWritableReferenceCount::operator = (copy);
|
|
BoundedObject::operator = (copy);
|
|
*/
|
|
clear_cache();
|
|
Geom::operator = (copy);
|
|
_cycler = copy._cycler;
|
|
mark_bound_stale();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::Destructor
|
|
// Access: Published, Virtual
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
qpGeom::
|
|
~qpGeom() {
|
|
// When we destruct, we should ensure that all of our cached
|
|
// entries, across all pipeline stages, are properly removed from
|
|
// the cache manager.
|
|
int num_stages = _cycler.get_num_stages();
|
|
for (int i = 0; i < num_stages; i++) {
|
|
if (_cycler.is_stage_unique(i)) {
|
|
CData *cdata = _cycler.write_stage(i);
|
|
for (Cache::iterator ci = cdata->_cache.begin();
|
|
ci != cdata->_cache.end();
|
|
++ci) {
|
|
CacheEntry *entry = (*ci);
|
|
entry->erase();
|
|
}
|
|
cdata->_cache.clear();
|
|
_cycler.release_write_stage(i, cdata);
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::make_copy
|
|
// Access: Published, Virtual
|
|
// Description: Temporarily redefined from Geom base class.
|
|
////////////////////////////////////////////////////////////////////
|
|
Geom *qpGeom::
|
|
make_copy() const {
|
|
return new qpGeom(*this);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::modify_vertex_data
|
|
// Access: Published
|
|
// Description: Returns a modifiable pointer to the GeomVertexData,
|
|
// so that application code may directly maniuplate the
|
|
// geom's underlying data.
|
|
////////////////////////////////////////////////////////////////////
|
|
PT(qpGeomVertexData) qpGeom::
|
|
modify_vertex_data() {
|
|
// Perform copy-on-write: if the reference count on the vertex data
|
|
// is greater than 1, assume some other Geom has the same pointer,
|
|
// so make a copy of it first.
|
|
clear_cache();
|
|
CDWriter cdata(_cycler);
|
|
if (cdata->_data->get_ref_count() > 1) {
|
|
cdata->_data = new qpGeomVertexData(*cdata->_data);
|
|
}
|
|
mark_bound_stale();
|
|
return cdata->_data;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::set_vertex_data
|
|
// Access: Published
|
|
// Description: Replaces the Geom's underlying vertex data table with
|
|
// a completely new table.
|
|
////////////////////////////////////////////////////////////////////
|
|
void qpGeom::
|
|
set_vertex_data(const qpGeomVertexData *data) {
|
|
nassertv(check_will_be_valid(data));
|
|
clear_cache();
|
|
CDWriter cdata(_cycler);
|
|
cdata->_data = (qpGeomVertexData *)data;
|
|
mark_bound_stale();
|
|
reset_point_rendering(cdata);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::set_primitive
|
|
// Access: Published
|
|
// Description: Replaces the ith GeomPrimitive object stored within
|
|
// the Geom with the new object.
|
|
////////////////////////////////////////////////////////////////////
|
|
void qpGeom::
|
|
set_primitive(int i, const qpGeomPrimitive *primitive) {
|
|
clear_cache();
|
|
CDWriter cdata(_cycler);
|
|
nassertv(i >= 0 && i < (int)cdata->_primitives.size());
|
|
nassertv(primitive->check_valid(cdata->_data));
|
|
|
|
// All primitives within a particular Geom must have the same
|
|
// fundamental primitive type (triangles, points, or lines).
|
|
nassertv(cdata->_primitive_type == qpGeomPrimitive::PT_none ||
|
|
cdata->_primitive_type == primitive->get_primitive_type());
|
|
|
|
if (cdata->_got_usage_hint &&
|
|
cdata->_primitives[i]->get_usage_hint() != primitive->get_usage_hint()) {
|
|
if (cdata->_primitives[i]->get_usage_hint() < primitive->get_usage_hint()) {
|
|
// If we're reducing the usage hint, we might also be reducing
|
|
// the minimum usage hit.
|
|
cdata->_usage_hint = min(cdata->_usage_hint, primitive->get_usage_hint());
|
|
} else { // (cdata->_primitives[i]->get_usage_hint() > primitive->get_usage_hint())
|
|
// If we're increasing it, we might have to rederive the minimum.
|
|
if (cdata->_usage_hint == cdata->_primitives[i]->get_usage_hint()) {
|
|
cdata->_got_usage_hint = false;
|
|
}
|
|
}
|
|
}
|
|
cdata->_primitives[i] = (qpGeomPrimitive *)primitive;
|
|
qpGeomPrimitive::PrimitiveType new_primitive_type = primitive->get_primitive_type();
|
|
if (new_primitive_type != cdata->_primitive_type) {
|
|
cdata->_primitive_type = new_primitive_type;
|
|
reset_point_rendering(cdata);
|
|
}
|
|
cdata->_modified = qpGeom::get_next_modified();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::add_primitive
|
|
// Access: Published
|
|
// Description: Adds a new GeomPrimitive structure to the Geom
|
|
// object. This specifies a particular subset of
|
|
// vertices that are used to define geometric primitives
|
|
// of the indicated type.
|
|
////////////////////////////////////////////////////////////////////
|
|
void qpGeom::
|
|
add_primitive(const qpGeomPrimitive *primitive) {
|
|
clear_cache();
|
|
CDWriter cdata(_cycler);
|
|
|
|
nassertv(primitive->check_valid(cdata->_data));
|
|
|
|
// All primitives within a particular Geom must have the same
|
|
// fundamental primitive type (triangles, points, or lines).
|
|
nassertv(cdata->_primitive_type == qpGeomPrimitive::PT_none ||
|
|
cdata->_primitive_type == primitive->get_primitive_type());
|
|
|
|
cdata->_primitives.push_back((qpGeomPrimitive *)primitive);
|
|
qpGeomPrimitive::PrimitiveType new_primitive_type = primitive->get_primitive_type();
|
|
if (new_primitive_type != cdata->_primitive_type) {
|
|
cdata->_primitive_type = new_primitive_type;
|
|
reset_point_rendering(cdata);
|
|
}
|
|
|
|
if (cdata->_got_usage_hint) {
|
|
cdata->_usage_hint = min(cdata->_usage_hint, primitive->get_usage_hint());
|
|
}
|
|
cdata->_modified = qpGeom::get_next_modified();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::remove_primitive
|
|
// Access: Published
|
|
// Description: Removes the ith primitive from the list.
|
|
////////////////////////////////////////////////////////////////////
|
|
void qpGeom::
|
|
remove_primitive(int i) {
|
|
clear_cache();
|
|
CDWriter cdata(_cycler);
|
|
nassertv(i >= 0 && i < (int)cdata->_primitives.size());
|
|
if (cdata->_got_usage_hint &&
|
|
cdata->_usage_hint == cdata->_primitives[i]->get_usage_hint()) {
|
|
// Maybe we're raising the minimum usage_hint; we have to rederive
|
|
// the usage_hint later.
|
|
cdata->_got_usage_hint = false;
|
|
}
|
|
cdata->_primitives.erase(cdata->_primitives.begin() + i);
|
|
if (cdata->_primitives.empty()) {
|
|
cdata->_primitive_type = qpGeomPrimitive::PT_none;
|
|
reset_point_rendering(cdata);
|
|
}
|
|
cdata->_modified = qpGeom::get_next_modified();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::clear_primitives
|
|
// Access: Published
|
|
// Description: Removes all the primitives from the Geom object (but
|
|
// keeps the same table of vertices). You may then
|
|
// re-add primitives one at a time via calls to
|
|
// add_primitive().
|
|
////////////////////////////////////////////////////////////////////
|
|
void qpGeom::
|
|
clear_primitives() {
|
|
clear_cache();
|
|
CDWriter cdata(_cycler);
|
|
cdata->_primitives.clear();
|
|
cdata->_primitive_type = qpGeomPrimitive::PT_none;
|
|
reset_point_rendering(cdata);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::get_num_bytes
|
|
// Access: Published
|
|
// Description: Returns the number of bytes consumed by the geom and
|
|
// its primitives (but not including its vertex table).
|
|
////////////////////////////////////////////////////////////////////
|
|
int qpGeom::
|
|
get_num_bytes() const {
|
|
CDReader cdata(_cycler);
|
|
|
|
int num_bytes = sizeof(qpGeom);
|
|
Primitives::const_iterator pi;
|
|
for (pi = cdata->_primitives.begin();
|
|
pi != cdata->_primitives.end();
|
|
++pi) {
|
|
num_bytes += (*pi)->get_num_bytes();
|
|
}
|
|
|
|
return num_bytes;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::transform_vertices
|
|
// Access: Published, Virtual
|
|
// Description: Applies the indicated transform to all of the
|
|
// vertices in the Geom. If the Geom happens to share a
|
|
// vertex table with another Geom, this operation will
|
|
// duplicate the vertex table instead of breaking the
|
|
// other Geom; however, if multiple Geoms with shared
|
|
// tables are transformed by the same matrix, they will
|
|
// no longer share tables after the operation. Consider
|
|
// using the GeomTransformer if you will be applying the
|
|
// same transform to multiple Geoms.
|
|
////////////////////////////////////////////////////////////////////
|
|
void qpGeom::
|
|
transform_vertices(const LMatrix4f &mat) {
|
|
PT(qpGeomVertexData) new_data = modify_vertex_data();
|
|
CPT(qpGeomVertexFormat) format = new_data->get_format();
|
|
|
|
int ci;
|
|
for (ci = 0; ci < format->get_num_points(); ci++) {
|
|
qpGeomVertexRewriter data(new_data, format->get_point(ci));
|
|
|
|
while (!data.is_at_end()) {
|
|
const LPoint3f &point = data.get_data3f();
|
|
data.set_data3f(point * mat);
|
|
}
|
|
}
|
|
for (ci = 0; ci < format->get_num_vectors(); ci++) {
|
|
qpGeomVertexRewriter data(new_data, format->get_vector(ci));
|
|
|
|
while (!data.is_at_end()) {
|
|
const LVector3f &vector = data.get_data3f();
|
|
data.set_data3f(normalize(vector * mat));
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::munge_geom
|
|
// Access: Published
|
|
// Description: Applies the indicated munger to the geom and its
|
|
// data, and returns a (possibly different) geom and
|
|
// data, according to the munger's whim.
|
|
//
|
|
// The assumption is that for a particular geom and a
|
|
// particular munger, the result will always be the
|
|
// same; so this result may be cached.
|
|
////////////////////////////////////////////////////////////////////
|
|
void qpGeom::
|
|
munge_geom(const qpGeomMunger *munger,
|
|
CPT(qpGeom) &result, CPT(qpGeomVertexData) &data) const {
|
|
CPT(qpGeomVertexData) source_data = data;
|
|
|
|
// Look up the munger in our cache--maybe we've recently applied it.
|
|
{
|
|
CDReader cdata(_cycler);
|
|
CacheEntry temp_entry(source_data, munger);
|
|
temp_entry.ref(); // big ugly hack to allow a stack-allocated ReferenceCount object.
|
|
Cache::const_iterator ci = cdata->_cache.find(&temp_entry);
|
|
if (ci != cdata->_cache.end()) {
|
|
CacheEntry *entry = (*ci);
|
|
|
|
if (get_modified() <= entry->_geom_result->get_modified() &&
|
|
data->get_modified() <= entry->_data_result->get_modified()) {
|
|
// The cache entry is still good; use it.
|
|
|
|
// Record a cache hit, so this element will stay in the cache a
|
|
// while longer.
|
|
entry->refresh();
|
|
result = entry->_geom_result;
|
|
data = entry->_data_result;
|
|
temp_entry.unref();
|
|
return;
|
|
}
|
|
|
|
// The cache entry is stale, remove it.
|
|
if (gobj_cat.is_debug()) {
|
|
gobj_cat.debug()
|
|
<< "Cache entry " << *entry << " is stale, removing.\n";
|
|
}
|
|
entry->erase();
|
|
CDWriter cdataw(((qpGeom *)this)->_cycler, cdata);
|
|
cdataw->_cache.erase(entry);
|
|
}
|
|
temp_entry.unref();
|
|
}
|
|
|
|
// Ok, invoke the munger.
|
|
PStatTimer timer(qpGeomMunger::_munge_pcollector);
|
|
|
|
result = this;
|
|
if (munger != (qpGeomMunger *)NULL) {
|
|
data = munger->munge_data(data);
|
|
((qpGeomMunger *)munger)->munge_geom_impl(result, data);
|
|
}
|
|
|
|
{
|
|
// Record the new result in the cache.
|
|
CacheEntry *entry;
|
|
{
|
|
CDWriter cdata(((qpGeom *)this)->_cycler);
|
|
entry = new CacheEntry(source_data, munger);
|
|
entry->_source = (qpGeom *)this;
|
|
entry->_geom_result = result;
|
|
entry->_data_result = data;
|
|
bool inserted = cdata->_cache.insert(entry).second;
|
|
nassertv(inserted);
|
|
}
|
|
|
|
// And tell the cache manager about the new entry. (It might
|
|
// immediately request a delete from the cache of the thing we
|
|
// just added.)
|
|
entry->record();
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::check_valid
|
|
// Access: Published
|
|
// Description: Verifies that the all of the primitives within the
|
|
// geom reference vertices that actually exist within
|
|
// the geom's GeomVertexData. Returns true if the geom
|
|
// appears to be valid, false otherwise.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool qpGeom::
|
|
check_valid() const {
|
|
CDReader cdata(_cycler);
|
|
|
|
Primitives::const_iterator pi;
|
|
for (pi = cdata->_primitives.begin();
|
|
pi != cdata->_primitives.end();
|
|
++pi) {
|
|
if (!(*pi)->check_valid(cdata->_data)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::output
|
|
// Access: Published
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
void qpGeom::
|
|
output(ostream &out) const {
|
|
CDReader cdata(_cycler);
|
|
|
|
// Get a list of the primitive types contained within this object.
|
|
pset<TypeHandle> types;
|
|
Primitives::const_iterator pi;
|
|
for (pi = cdata->_primitives.begin();
|
|
pi != cdata->_primitives.end();
|
|
++pi) {
|
|
types.insert((*pi)->get_type());
|
|
}
|
|
|
|
out << "Geom [";
|
|
pset<TypeHandle>::iterator ti;
|
|
for (ti = types.begin(); ti != types.end(); ++ti) {
|
|
out << " " << (*ti);
|
|
}
|
|
out << " ], " << cdata->_data->get_num_vertices() << " vertices";
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::write
|
|
// Access: Published
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
void qpGeom::
|
|
write(ostream &out, int indent_level) const {
|
|
CDReader cdata(_cycler);
|
|
|
|
// Get a list of the primitive types contained within this object.
|
|
Primitives::const_iterator pi;
|
|
for (pi = cdata->_primitives.begin();
|
|
pi != cdata->_primitives.end();
|
|
++pi) {
|
|
(*pi)->write(out, indent_level);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::clear_cache
|
|
// Access: Published
|
|
// Description: Removes all of the previously-cached results of
|
|
// munge_geom().
|
|
////////////////////////////////////////////////////////////////////
|
|
void qpGeom::
|
|
clear_cache() {
|
|
// Probably we shouldn't do anything at all here unless we are
|
|
// running in pipeline stage 0.
|
|
CData *cdata = CDWriter(_cycler);
|
|
for (Cache::iterator ci = cdata->_cache.begin();
|
|
ci != cdata->_cache.end();
|
|
++ci) {
|
|
CacheEntry *entry = (*ci);
|
|
entry->erase();
|
|
}
|
|
cdata->_cache.clear();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::draw
|
|
// Access: Public
|
|
// Description: Actually draws the Geom with the indicated GSG, using
|
|
// the indicated vertex data (which might have been
|
|
// pre-munged to support the GSG's needs).
|
|
////////////////////////////////////////////////////////////////////
|
|
void qpGeom::
|
|
draw(GraphicsStateGuardianBase *gsg, const qpGeomMunger *munger,
|
|
const qpGeomVertexData *vertex_data) const {
|
|
#ifdef DO_PIPELINING
|
|
// Make sure the usage_hint is already updated before we start to
|
|
// draw, so we don't end up with a circular lock if the GSG asks us
|
|
// to update this while we're holding the read lock.
|
|
{
|
|
CDReader cdata(_cycler);
|
|
if (!cdata->_got_usage_hint) {
|
|
CDWriter cdataw(((qpGeom *)this)->_cycler, cdata);
|
|
((qpGeom *)this)->reset_usage_hint(cdataw);
|
|
}
|
|
}
|
|
// TODO: fix up the race condition between this line and the next.
|
|
// Maybe CDWriter's elevate-to-write should return the read lock to
|
|
// its original locked state when it's done.
|
|
#endif // DO_PIPELINING
|
|
|
|
CDReader cdata(_cycler);
|
|
|
|
if (gsg->begin_draw_primitives(this, munger, vertex_data)) {
|
|
Primitives::const_iterator pi;
|
|
for (pi = cdata->_primitives.begin();
|
|
pi != cdata->_primitives.end();
|
|
++pi) {
|
|
const qpGeomPrimitive *primitive = (*pi);
|
|
if (primitive->get_num_vertices() != 0) {
|
|
(*pi)->draw(gsg);
|
|
}
|
|
}
|
|
gsg->end_draw_primitives();
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::calc_tight_bounds
|
|
// Access: Public, Virtual
|
|
// Description: Expands min_point and max_point to include all of the
|
|
// vertices in the Geom, if any. found_any is set true
|
|
// if any points are found. It is the caller's
|
|
// responsibility to initialize min_point, max_point,
|
|
// and found_any before calling this function.
|
|
////////////////////////////////////////////////////////////////////
|
|
void qpGeom::
|
|
calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
|
|
bool &found_any,
|
|
const qpGeomVertexData *vertex_data,
|
|
bool got_mat, const LMatrix4f &mat) const {
|
|
|
|
CDReader cdata(_cycler);
|
|
|
|
Primitives::const_iterator pi;
|
|
for (pi = cdata->_primitives.begin();
|
|
pi != cdata->_primitives.end();
|
|
++pi) {
|
|
(*pi)->calc_tight_bounds(min_point, max_point, found_any, vertex_data,
|
|
got_mat, mat);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::get_next_modified
|
|
// Access: Public, Static
|
|
// Description: Returns a monotonically increasing sequence. Each
|
|
// time this is called, a new sequence number is
|
|
// returned, higher than the previous value.
|
|
//
|
|
// This is used to ensure that
|
|
// GeomVertexArrayData::get_modified() and
|
|
// GeomPrimitive::get_modified() update from the same
|
|
// space, so that Geom::get_modified() returns a
|
|
// meaningful value.
|
|
////////////////////////////////////////////////////////////////////
|
|
UpdateSeq qpGeom::
|
|
get_next_modified() {
|
|
++_next_modified;
|
|
return _next_modified;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::recompute_bound
|
|
// Access: Protected, Virtual
|
|
// Description: Recomputes the dynamic bounding volume for this Geom.
|
|
// This includes all of the vertices.
|
|
////////////////////////////////////////////////////////////////////
|
|
BoundingVolume *qpGeom::
|
|
recompute_bound() {
|
|
// First, get ourselves a fresh, empty bounding volume.
|
|
BoundingVolume *bound = BoundedObject::recompute_bound();
|
|
nassertr(bound != (BoundingVolume*)0L, bound);
|
|
|
|
GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound);
|
|
|
|
// Now actually compute the bounding volume. We do this by using
|
|
// calc_tight_bounds to determine our minmax first.
|
|
LPoint3f points[2];
|
|
bool found_any = false;
|
|
calc_tight_bounds(points[0], points[1], found_any, get_vertex_data(),
|
|
false, LMatrix4f::ident_mat());
|
|
if (found_any) {
|
|
// Then we put the bounding volume around both of those points.
|
|
// Technically, we should put it around the eight points at the
|
|
// corners of the rectangular solid, but we happen to know that
|
|
// the two diagonally opposite points is good enough to define any
|
|
// of our bound volume types.
|
|
|
|
const LPoint3f *points_begin = &points[0];
|
|
const LPoint3f *points_end = points_begin + 2;
|
|
gbv->around(points_begin, points_end);
|
|
}
|
|
|
|
return bound;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::check_will_be_valid
|
|
// Access: Private
|
|
// Description: Verifies that the all of the primitives within the
|
|
// geom reference vertices that actually exist within
|
|
// the indicated GeomVertexData (presumably in
|
|
// preparation for assigning the geom to use this data).
|
|
// Returns true if the data appears to be valid, false
|
|
// otherwise.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool qpGeom::
|
|
check_will_be_valid(const qpGeomVertexData *vertex_data) const {
|
|
CDReader cdata(_cycler);
|
|
|
|
Primitives::const_iterator pi;
|
|
for (pi = cdata->_primitives.begin();
|
|
pi != cdata->_primitives.end();
|
|
++pi) {
|
|
if (!(*pi)->check_valid(vertex_data)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::reset_usage_hint
|
|
// Access: Private
|
|
// Description: Recomputes the minimum usage_hint.
|
|
////////////////////////////////////////////////////////////////////
|
|
void qpGeom::
|
|
reset_usage_hint(qpGeom::CDWriter &cdata) {
|
|
cdata->_usage_hint = qpGeomUsageHint::UH_static;
|
|
Primitives::const_iterator pi;
|
|
for (pi = cdata->_primitives.begin();
|
|
pi != cdata->_primitives.end();
|
|
++pi) {
|
|
cdata->_usage_hint = min(cdata->_usage_hint, (*pi)->get_usage_hint());
|
|
}
|
|
cdata->_got_usage_hint = true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::reset_point_rendering
|
|
// Access: Private
|
|
// Description: Rederives the _point_rendering member.
|
|
////////////////////////////////////////////////////////////////////
|
|
void qpGeom::
|
|
reset_point_rendering(qpGeom::CDWriter &cdata) {
|
|
cdata->_point_rendering = 0;
|
|
if (cdata->_primitive_type == qpGeomPrimitive::PT_points) {
|
|
cdata->_point_rendering |= PR_point;
|
|
|
|
if (cdata->_data->has_column(InternalName::get_size())) {
|
|
cdata->_point_rendering |= PR_per_point_size;
|
|
}
|
|
if (cdata->_data->has_column(InternalName::get_aspect_ratio())) {
|
|
cdata->_point_rendering |= PR_aspect_ratio;
|
|
}
|
|
if (cdata->_data->has_column(InternalName::get_rotate())) {
|
|
cdata->_point_rendering |= PR_rotate;
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::register_with_read_factory
|
|
// Access: Public, Static
|
|
// Description: Tells the BamReader how to create objects of type
|
|
// qpGeom.
|
|
////////////////////////////////////////////////////////////////////
|
|
void qpGeom::
|
|
register_with_read_factory() {
|
|
BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::write_datagram
|
|
// Access: Public, Virtual
|
|
// Description: Writes the contents of this object to the datagram
|
|
// for shipping out to a Bam file.
|
|
////////////////////////////////////////////////////////////////////
|
|
void qpGeom::
|
|
write_datagram(BamWriter *manager, Datagram &dg) {
|
|
TypedWritable::write_datagram(manager, dg);
|
|
|
|
manager->write_cdata(dg, _cycler);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::make_from_bam
|
|
// Access: Protected, Static
|
|
// Description: This function is called by the BamReader's factory
|
|
// when a new object of type qpGeom is encountered
|
|
// in the Bam file. It should create the qpGeom
|
|
// and extract its information from the file.
|
|
////////////////////////////////////////////////////////////////////
|
|
TypedWritable *qpGeom::
|
|
make_from_bam(const FactoryParams ¶ms) {
|
|
qpGeom *object = new qpGeom;
|
|
DatagramIterator scan;
|
|
BamReader *manager;
|
|
|
|
parse_params(params, scan, manager);
|
|
object->fillin(scan, manager);
|
|
|
|
return object;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::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 qpGeom.
|
|
////////////////////////////////////////////////////////////////////
|
|
void qpGeom::
|
|
fillin(DatagramIterator &scan, BamReader *manager) {
|
|
TypedWritable::fillin(scan, manager);
|
|
|
|
manager->read_cdata(scan, _cycler);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::CacheEntry::evict_callback
|
|
// Access: Public, Virtual
|
|
// Description: Called when the entry is evicted from the cache, this
|
|
// should clean up the owning object appropriately.
|
|
////////////////////////////////////////////////////////////////////
|
|
void qpGeom::CacheEntry::
|
|
evict_callback() {
|
|
// We have to operate on stage 0 of the pipeline, since that's where
|
|
// the cache really counts. Because of the multistage pipeline, we
|
|
// might not actually have a cache entry there (it might have been
|
|
// added to stage 1 instead). No big deal if we don't.
|
|
CData *cdata = _source->_cycler.write_stage(0);
|
|
Cache::iterator ci = cdata->_cache.find(this);
|
|
if (ci != cdata->_cache.end()) {
|
|
cdata->_cache.erase(ci);
|
|
}
|
|
_source->_cycler.release_write_stage(0, cdata);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::CacheEntry::get_result_size
|
|
// Access: Public, Virtual
|
|
// Description: Returns the approximate number of bytes represented
|
|
// by the computed result.
|
|
////////////////////////////////////////////////////////////////////
|
|
int qpGeom::CacheEntry::
|
|
get_result_size() const {
|
|
return _geom_result->get_num_bytes() + _data_result->get_num_bytes();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::CacheEntry::output
|
|
// Access: Public, Virtual
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
void qpGeom::CacheEntry::
|
|
output(ostream &out) const {
|
|
out << "geom " << (void *)_source << ", "
|
|
<< (const void *)_modifier;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::CData::make_copy
|
|
// Access: Public, Virtual
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
CycleData *qpGeom::CData::
|
|
make_copy() const {
|
|
return new CData(*this);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::CData::write_datagram
|
|
// Access: Public, Virtual
|
|
// Description: Writes the contents of this object to the datagram
|
|
// for shipping out to a Bam file.
|
|
////////////////////////////////////////////////////////////////////
|
|
void qpGeom::CData::
|
|
write_datagram(BamWriter *manager, Datagram &dg) const {
|
|
manager->write_pointer(dg, _data);
|
|
|
|
dg.add_uint16(_primitives.size());
|
|
Primitives::const_iterator pi;
|
|
for (pi = _primitives.begin(); pi != _primitives.end(); ++pi) {
|
|
manager->write_pointer(dg, *pi);
|
|
}
|
|
|
|
dg.add_uint8(_primitive_type);
|
|
dg.add_uint16(_point_rendering);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::CData::complete_pointers
|
|
// Access: Public, Virtual
|
|
// Description: Receives an array of pointers, one for each time
|
|
// manager->read_pointer() was called in fillin().
|
|
// Returns the number of pointers processed.
|
|
////////////////////////////////////////////////////////////////////
|
|
int qpGeom::CData::
|
|
complete_pointers(TypedWritable **p_list, BamReader *manager) {
|
|
int pi = CycleData::complete_pointers(p_list, manager);
|
|
|
|
_data = DCAST(qpGeomVertexData, p_list[pi++]);
|
|
|
|
Primitives::iterator pri;
|
|
for (pri = _primitives.begin(); pri != _primitives.end(); ++pri) {
|
|
(*pri) = DCAST(qpGeomPrimitive, p_list[pi++]);
|
|
}
|
|
|
|
return pi;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: qpGeom::CData::fillin
|
|
// Access: Public, Virtual
|
|
// Description: This internal function is called by make_from_bam to
|
|
// read in all of the relevant data from the BamFile for
|
|
// the new qpGeom.
|
|
////////////////////////////////////////////////////////////////////
|
|
void qpGeom::CData::
|
|
fillin(DatagramIterator &scan, BamReader *manager) {
|
|
manager->read_pointer(scan);
|
|
|
|
int num_primitives = scan.get_uint16();
|
|
_primitives.reserve(num_primitives);
|
|
for (int i = 0; i < num_primitives; ++i) {
|
|
manager->read_pointer(scan);
|
|
_primitives.push_back(NULL);
|
|
}
|
|
|
|
_primitive_type = (qpGeomPrimitive::PrimitiveType)scan.get_uint8();
|
|
_point_rendering = scan.get_uint16();
|
|
_got_usage_hint = false;
|
|
_modified = qpGeom::get_next_modified();
|
|
}
|