allow-collider-bitarray -> allow-collider-multiple

This commit is contained in:
David Rose 2007-04-06 15:43:44 +00:00
parent ceace684a2
commit 6381866a56
5 changed files with 372 additions and 95 deletions

View File

@ -24,7 +24,7 @@
#include "collisionLevelStateBase.h"
#include "collisionNode.h"
#include "bitMask.h"
#include "bitArray.h"
#include "doubleBitMask.h"
////////////////////////////////////////////////////////////////////
// Class : CollisionLevelState
@ -74,14 +74,13 @@ private:
#include "collisionLevelState.I"
// Now instantiate a pair of implementations of CollisionLevelState:
// Now instantiate a handful of implementations of CollisionLevelState:
// one that uses a word-at-a-time bitmask to track the active
// colliders (and thus is limited to handling 32 or 64 colliders in a
// given pass), and another that uses an infinite BitArray to track
// these colliders (and thus has no particular limit).
// colliders, and a couple that use more words at a time.
typedef CollisionLevelState<BitMaskNative> CollisionLevelStateWord;
typedef CollisionLevelState<BitArray> CollisionLevelStateArray;
typedef CollisionLevelState<BitMaskNative> CollisionLevelStateSingle;
typedef CollisionLevelState<DoubleBitMaskNative> CollisionLevelStateDouble;
typedef CollisionLevelState<QuadBitMaskNative> CollisionLevelStateQuad;
#endif

View File

@ -51,10 +51,18 @@ PStatCollector CollisionTraverser::_geom_volume_pcollector("Collision Volumes:Ge
// This function object class is used in prepare_colliders(), below.
class SortByColliderSort {
public:
inline bool operator () (const CollisionTraverser::OrderedColliderDef &a,
const CollisionTraverser::OrderedColliderDef &b) const {
return DCAST(CollisionNode, a._node_path.node())->get_collider_sort() < DCAST(CollisionNode, b._node_path.node())->get_collider_sort();
SortByColliderSort(const CollisionTraverser &trav) :
_trav(trav)
{
}
inline bool operator () (int a, int b) const {
const CollisionTraverser::OrderedColliderDef &ocd_a = _trav._ordered_colliders[a];
const CollisionTraverser::OrderedColliderDef &ocd_b = _trav._ordered_colliders[b];
return DCAST(CollisionNode, ocd_a._node_path.node())->get_collider_sort() < DCAST(CollisionNode, ocd_b._node_path.node())->get_collider_sort();
}
const CollisionTraverser &_trav;
};
////////////////////////////////////////////////////////////////////
@ -281,37 +289,59 @@ traverse(const NodePath &root) {
(*hi).first->begin_group();
}
bool do_array_pass = true;
if (_colliders.size() <= CollisionLevelStateWord::get_max_colliders() ||
!allow_collider_bitarray) {
// Use the word-at-a-time traverser, which might need to make
// several passes.
LevelStatesWord level_states;
prepare_colliders_word(level_states, root);
bool traversal_done = false;
if (_colliders.size() <= CollisionLevelStateSingle::get_max_colliders() ||
!allow_collider_multiple) {
// Use the single-word-at-a-time traverser, which might need to make
// lots of passes.
LevelStatesSingle level_states;
prepare_colliders_single(level_states, root);
if (level_states.size() == 1 || !allow_collider_multiple) {
traversal_done = true;
if (level_states.size() == 1 || !allow_collider_bitarray) {
do_array_pass = false;
// Make a number of passes, one for each group of 32 Colliders (or
// whatever number of bits we have available in CurrentMask).
for (size_t pass = 0; pass < level_states.size(); ++pass) {
#ifdef DO_PSTATS
PStatTimer pass_timer(get_pass_collector(pass));
#endif
r_traverse_word(level_states[pass], pass);
r_traverse_single(level_states[pass], pass);
}
}
}
if (do_array_pass) {
// Use the array-at-a-time traverser, which can do the whole thing
// at once, even if there are many colliders.
CollisionLevelStateArray level_state(root);
prepare_colliders_array(level_state, root);
if (!traversal_done &&
_colliders.size() <= CollisionLevelStateDouble::get_max_colliders()) {
// Try the double-word-at-a-time traverser.
LevelStatesDouble level_states;
prepare_colliders_double(level_states, root);
if (level_states.size() == 1) {
traversal_done = true;
for (size_t pass = 0; pass < level_states.size(); ++pass) {
#ifdef DO_PSTATS
PStatTimer pass_timer(get_pass_collector(0));
PStatTimer pass_timer(get_pass_collector(pass));
#endif
r_traverse_array(level_state);
r_traverse_double(level_states[pass], pass);
}
}
}
if (!traversal_done) {
// OK, do the quad-word-at-a-time traverser.
LevelStatesQuad level_states;
prepare_colliders_quad(level_states, root);
traversal_done = true;
for (size_t pass = 0; pass < level_states.size(); ++pass) {
#ifdef DO_PSTATS
PStatTimer pass_timer(get_pass_collector(pass));
#endif
r_traverse_quad(level_states[pass], pass);
}
}
hi = _handlers.begin();
@ -478,24 +508,23 @@ write(ostream &out, int indent_level) const {
}
}
////////////////////////////////////////////////////////////////////
// Function: CollisionTraverser::prepare_colliders_word
// Function: CollisionTraverser::prepare_colliders_single
// Access: Private
// Description: Fills up the set of LevelStates corresponding to the
// active colliders in use.
//
// This flavor uses a CollisionLevelStateWord, which is
// This flavor uses a CollisionLevelStateSingle, which is
// limited to a certain number of colliders per pass
// (typically 32).
////////////////////////////////////////////////////////////////////
void CollisionTraverser::
prepare_colliders_word(CollisionTraverser::LevelStatesWord &level_states,
const NodePath &root) {
prepare_colliders_single(CollisionTraverser::LevelStatesSingle &level_states,
const NodePath &root) {
int num_colliders = _colliders.size();
int max_colliders = CollisionLevelStateWord::get_max_colliders();
int max_colliders = CollisionLevelStateSingle::get_max_colliders();
CollisionLevelStateWord level_state(root);
CollisionLevelStateSingle level_state(root);
// This reserve() call is only correct if there is exactly one solid
// per collider added to the traverser, which is the normal case.
// If there is more than one solid in any of the colliders, this
@ -503,27 +532,34 @@ prepare_colliders_word(CollisionTraverser::LevelStatesWord &level_states,
// correct.
level_state.reserve(min(num_colliders, max_colliders));
OrderedColliders sorted = _ordered_colliders;
sort(sorted.begin(), sorted.end(), SortByColliderSort());
// Create an indirect index array to walk through the colliders in
// sorted order, without affect the actual collider order.
int *indirect = (int *)alloca(sizeof(int) * num_colliders);
int i;
for (i = 0; i < num_colliders; ++i) {
indirect[i] = i;
}
sort(indirect, indirect + num_colliders, SortByColliderSort(*this));
OrderedColliders::iterator oci;
for (oci = sorted.begin(); oci != sorted.end(); ++oci) {
NodePath cnode_path = (*oci)._node_path;
int num_remaining_colliders = num_colliders;
for (i = 0; i < num_colliders; ++i) {
OrderedColliderDef &ocd = _ordered_colliders[indirect[i]];
NodePath cnode_path = ocd._node_path;
if (!cnode_path.is_same_graph(root)) {
if ((*oci)._in_graph) {
if (ocd._in_graph) {
// Only report this warning once.
collide_cat.info()
<< "Collider " << cnode_path
<< " is not in scene graph. Ignoring.\n";
(*oci)._in_graph = false;
ocd._in_graph = false;
}
} else {
(*oci)._in_graph = true;
ocd._in_graph = true;
CollisionNode *cnode = DCAST(CollisionNode, cnode_path.node());
CollisionLevelStateWord::ColliderDef def;
CollisionLevelStateSingle::ColliderDef def;
def._node = cnode;
def._node_path = cnode_path;
@ -538,28 +574,28 @@ prepare_colliders_word(CollisionTraverser::LevelStatesWord &level_states,
// new one.
level_states.push_back(level_state);
level_state.clear();
level_state.reserve(min(num_colliders, max_colliders));
level_state.reserve(min(num_remaining_colliders, max_colliders));
}
}
}
--num_colliders;
nassertv(num_colliders >= 0);
--num_remaining_colliders;
nassertv(num_remaining_colliders >= 0);
}
if (level_state.get_num_colliders() != 0) {
level_states.push_back(level_state);
}
nassertv(num_colliders == 0);
nassertv(num_remaining_colliders == 0);
}
////////////////////////////////////////////////////////////////////
// Function: CollisionTraverser::r_traverse_word
// Function: CollisionTraverser::r_traverse_single
// Access: Private
// Description:
////////////////////////////////////////////////////////////////////
void CollisionTraverser::
r_traverse_word(CollisionLevelStateWord &level_state, size_t pass) {
r_traverse_single(CollisionLevelStateSingle &level_state, size_t pass) {
if (!level_state.any_in_bounds()) {
return;
}
@ -655,8 +691,8 @@ r_traverse_word(CollisionLevelStateWord &level_state, size_t pass) {
// visible child.
int index = node->get_visible_child();
if (index >= 0 && index < node->get_num_children()) {
CollisionLevelStateWord next_state(level_state, node->get_child(index));
r_traverse_word(next_state, pass);
CollisionLevelStateSingle next_state(level_state, node->get_child(index));
r_traverse_single(next_state, pass);
}
} else if (node->is_lod_node()) {
@ -669,66 +705,76 @@ r_traverse_word(CollisionLevelStateWord &level_state, size_t pass) {
int index = DCAST(LODNode, node)->get_lowest_switch();
int num_children = node->get_num_children();
for (int i = 0; i < num_children; ++i) {
CollisionLevelStateWord next_state(level_state, node->get_child(i));
CollisionLevelStateSingle next_state(level_state, node->get_child(i));
if (i != index) {
next_state.set_include_mask(next_state.get_include_mask() &
~GeomNode::get_default_collide_mask());
}
r_traverse_word(next_state, pass);
r_traverse_single(next_state, pass);
}
} else {
// Otherwise, visit all the children.
int num_children = node->get_num_children();
for (int i = 0; i < num_children; ++i) {
CollisionLevelStateWord next_state(level_state, node->get_child(i));
r_traverse_word(next_state, pass);
CollisionLevelStateSingle next_state(level_state, node->get_child(i));
r_traverse_single(next_state, pass);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: CollisionTraverser::prepare_colliders_array
// Function: CollisionTraverser::prepare_colliders_double
// Access: Private
// Description: Fills up the set of LevelStates corresponding to the
// active colliders in use.
//
// This flavor uses a CollisionLevelStateArray, which
// has no limit in the number of colliders it can handle
// in one pass.
// This flavor uses a CollisionLevelStateDouble, which is
// limited to a certain number of colliders per pass
// (typically 32).
////////////////////////////////////////////////////////////////////
void CollisionTraverser::
prepare_colliders_array(CollisionLevelStateArray &level_state,
const NodePath &root) {
prepare_colliders_double(CollisionTraverser::LevelStatesDouble &level_states,
const NodePath &root) {
int num_colliders = _colliders.size();
int max_colliders = CollisionLevelStateDouble::get_max_colliders();
CollisionLevelStateDouble level_state(root);
// This reserve() call is only correct if there is exactly one solid
// per collider added to the traverser, which is the normal case.
// If there is more than one solid in any of the colliders, this
// reserve() call won't reserve enough, but the code is otherwise
// correct.
level_state.reserve(num_colliders);
level_state.reserve(min(num_colliders, max_colliders));
OrderedColliders sorted = _ordered_colliders;
sort(sorted.begin(), sorted.end(), SortByColliderSort());
// Create an indirect index array to walk through the colliders in
// sorted order, without affect the actual collider order.
int *indirect = (int *)alloca(sizeof(int) * num_colliders);
int i;
for (i = 0; i < num_colliders; ++i) {
indirect[i] = i;
}
sort(indirect, indirect + num_colliders, SortByColliderSort(*this));
OrderedColliders::iterator oci;
for (oci = sorted.begin(); oci != sorted.end(); ++oci) {
NodePath cnode_path = (*oci)._node_path;
int num_remaining_colliders = num_colliders;
for (i = 0; i < num_colliders; ++i) {
OrderedColliderDef &ocd = _ordered_colliders[indirect[i]];
NodePath cnode_path = ocd._node_path;
if (!cnode_path.is_same_graph(root)) {
if ((*oci)._in_graph) {
if (ocd._in_graph) {
// Only report this warning once.
collide_cat.info()
<< "Collider " << cnode_path
<< " is not in scene graph. Ignoring.\n";
(*oci)._in_graph = false;
ocd._in_graph = false;
}
} else {
(*oci)._in_graph = true;
ocd._in_graph = true;
CollisionNode *cnode = DCAST(CollisionNode, cnode_path.node());
CollisionLevelStateArray::ColliderDef def;
CollisionLevelStateDouble::ColliderDef def;
def._node = cnode;
def._node_path = cnode_path;
@ -737,23 +783,34 @@ prepare_colliders_array(CollisionLevelStateArray &level_state,
CollisionSolid *collider = cnode->get_solid(s);
def._collider = collider;
level_state.prepare_collider(def, root);
if (level_state.get_num_colliders() == max_colliders) {
// That's the limit. Save off this level state and make a
// new one.
level_states.push_back(level_state);
level_state.clear();
level_state.reserve(min(num_remaining_colliders, max_colliders));
}
}
}
--num_colliders;
nassertv(num_colliders >= 0);
--num_remaining_colliders;
nassertv(num_remaining_colliders >= 0);
}
nassertv(num_colliders == 0);
if (level_state.get_num_colliders() != 0) {
level_states.push_back(level_state);
}
nassertv(num_remaining_colliders == 0);
}
////////////////////////////////////////////////////////////////////
// Function: CollisionTraverser::r_traverse_array
// Function: CollisionTraverser::r_traverse_double
// Access: Private
// Description:
////////////////////////////////////////////////////////////////////
void CollisionTraverser::
r_traverse_array(CollisionLevelStateArray &level_state) {
r_traverse_double(CollisionLevelStateDouble &level_state, size_t pass) {
if (!level_state.any_in_bounds()) {
return;
}
@ -784,7 +841,7 @@ r_traverse_array(CollisionLevelStateArray &level_state) {
if ((entry._from_node->get_from_collide_mask() &
cnode->get_into_collide_mask()) != 0) {
#ifdef DO_PSTATS
PStatTimer collide_timer(_solid_collide_collectors[0]);
PStatTimer collide_timer(_solid_collide_collectors[pass]);
#endif
entry._from_node_path = level_state.get_collider_node_path(c);
entry._from = level_state.get_collider(c);
@ -829,7 +886,7 @@ r_traverse_array(CollisionLevelStateArray &level_state) {
if ((entry._from_node->get_from_collide_mask() &
gnode->get_into_collide_mask()) != 0) {
#ifdef DO_PSTATS
PStatTimer collide_timer(_solid_collide_collectors[0]);
PStatTimer collide_timer(_solid_collide_collectors[pass]);
#endif
entry._from_node_path = level_state.get_collider_node_path(c);
entry._from = level_state.get_collider(c);
@ -849,8 +906,8 @@ r_traverse_array(CollisionLevelStateArray &level_state) {
// visible child.
int index = node->get_visible_child();
if (index >= 0 && index < node->get_num_children()) {
CollisionLevelStateArray next_state(level_state, node->get_child(index));
r_traverse_array(next_state);
CollisionLevelStateDouble next_state(level_state, node->get_child(index));
r_traverse_double(next_state, pass);
}
} else if (node->is_lod_node()) {
@ -863,20 +920,235 @@ r_traverse_array(CollisionLevelStateArray &level_state) {
int index = DCAST(LODNode, node)->get_lowest_switch();
int num_children = node->get_num_children();
for (int i = 0; i < num_children; ++i) {
CollisionLevelStateArray next_state(level_state, node->get_child(i));
CollisionLevelStateDouble next_state(level_state, node->get_child(i));
if (i != index) {
next_state.set_include_mask(next_state.get_include_mask() &
~GeomNode::get_default_collide_mask());
}
r_traverse_array(next_state);
r_traverse_double(next_state, pass);
}
} else {
// Otherwise, visit all the children.
int num_children = node->get_num_children();
for (int i = 0; i < num_children; ++i) {
CollisionLevelStateArray next_state(level_state, node->get_child(i));
r_traverse_array(next_state);
CollisionLevelStateDouble next_state(level_state, node->get_child(i));
r_traverse_double(next_state, pass);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: CollisionTraverser::prepare_colliders_quad
// Access: Private
// Description: Fills up the set of LevelStates corresponding to the
// active colliders in use.
//
// This flavor uses a CollisionLevelStateQuad, which is
// limited to a certain number of colliders per pass
// (typically 32).
////////////////////////////////////////////////////////////////////
void CollisionTraverser::
prepare_colliders_quad(CollisionTraverser::LevelStatesQuad &level_states,
const NodePath &root) {
int num_colliders = _colliders.size();
int max_colliders = CollisionLevelStateQuad::get_max_colliders();
CollisionLevelStateQuad level_state(root);
// This reserve() call is only correct if there is exactly one solid
// per collider added to the traverser, which is the normal case.
// If there is more than one solid in any of the colliders, this
// reserve() call won't reserve enough, but the code is otherwise
// correct.
level_state.reserve(min(num_colliders, max_colliders));
// Create an indirect index array to walk through the colliders in
// sorted order, without affect the actual collider order.
int *indirect = (int *)alloca(sizeof(int) * num_colliders);
int i;
for (i = 0; i < num_colliders; ++i) {
indirect[i] = i;
}
sort(indirect, indirect + num_colliders, SortByColliderSort(*this));
int num_remaining_colliders = num_colliders;
for (i = 0; i < num_colliders; ++i) {
OrderedColliderDef &ocd = _ordered_colliders[indirect[i]];
NodePath cnode_path = ocd._node_path;
if (!cnode_path.is_same_graph(root)) {
if (ocd._in_graph) {
// Only report this warning once.
collide_cat.info()
<< "Collider " << cnode_path
<< " is not in scene graph. Ignoring.\n";
ocd._in_graph = false;
}
} else {
ocd._in_graph = true;
CollisionNode *cnode = DCAST(CollisionNode, cnode_path.node());
CollisionLevelStateQuad::ColliderDef def;
def._node = cnode;
def._node_path = cnode_path;
int num_solids = cnode->get_num_solids();
for (int s = 0; s < num_solids; ++s) {
CollisionSolid *collider = cnode->get_solid(s);
def._collider = collider;
level_state.prepare_collider(def, root);
if (level_state.get_num_colliders() == max_colliders) {
// That's the limit. Save off this level state and make a
// new one.
level_states.push_back(level_state);
level_state.clear();
level_state.reserve(min(num_remaining_colliders, max_colliders));
}
}
}
--num_remaining_colliders;
nassertv(num_remaining_colliders >= 0);
}
if (level_state.get_num_colliders() != 0) {
level_states.push_back(level_state);
}
nassertv(num_remaining_colliders == 0);
}
////////////////////////////////////////////////////////////////////
// Function: CollisionTraverser::r_traverse_quad
// Access: Private
// Description:
////////////////////////////////////////////////////////////////////
void CollisionTraverser::
r_traverse_quad(CollisionLevelStateQuad &level_state, size_t pass) {
if (!level_state.any_in_bounds()) {
return;
}
level_state.apply_transform();
PandaNode *node = level_state.node();
if (node->is_exact_type(CollisionNode::get_class_type())) {
CollisionNode *cnode;
DCAST_INTO_V(cnode, node);
CPT(BoundingVolume) node_bv = cnode->get_bounds();
const GeometricBoundingVolume *node_gbv = NULL;
if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
DCAST_INTO_V(node_gbv, node_bv);
}
CollisionEntry entry;
entry._into_node = cnode;
entry._into_node_path = level_state.get_node_path();
if (_respect_prev_transform) {
entry._flags |= CollisionEntry::F_respect_prev_transform;
}
int num_colliders = level_state.get_num_colliders();
for (int c = 0; c < num_colliders; ++c) {
if (level_state.has_collider(c)) {
entry._from_node = level_state.get_collider_node(c);
if ((entry._from_node->get_from_collide_mask() &
cnode->get_into_collide_mask()) != 0) {
#ifdef DO_PSTATS
PStatTimer collide_timer(_solid_collide_collectors[pass]);
#endif
entry._from_node_path = level_state.get_collider_node_path(c);
entry._from = level_state.get_collider(c);
compare_collider_to_node(
entry,
level_state.get_parent_bound(c),
level_state.get_local_bound(c),
node_gbv);
}
}
}
} else if (node->is_geom_node()) {
#ifndef NDEBUG
if (collide_cat.is_spam()) {
collide_cat.spam()
<< "Reached " << *node << "\n";
}
#endif
GeomNode *gnode;
DCAST_INTO_V(gnode, node);
CPT(BoundingVolume) node_bv = gnode->get_bounds();
const GeometricBoundingVolume *node_gbv = NULL;
if (node_bv->is_of_type(GeometricBoundingVolume::get_class_type())) {
DCAST_INTO_V(node_gbv, node_bv);
}
CollisionEntry entry;
entry._into_node = gnode;
entry._into_node_path = level_state.get_node_path();
if (_respect_prev_transform) {
entry._flags |= CollisionEntry::F_respect_prev_transform;
}
int num_colliders = level_state.get_num_colliders();
for (int c = 0; c < num_colliders; ++c) {
if (level_state.has_collider(c)) {
entry._from_node = level_state.get_collider_node(c);
if ((entry._from_node->get_from_collide_mask() &
gnode->get_into_collide_mask()) != 0) {
#ifdef DO_PSTATS
PStatTimer collide_timer(_solid_collide_collectors[pass]);
#endif
entry._from_node_path = level_state.get_collider_node_path(c);
entry._from = level_state.get_collider(c);
compare_collider_to_geom_node(
entry,
level_state.get_parent_bound(c),
level_state.get_local_bound(c),
node_gbv);
}
}
}
}
if (node->has_single_child_visibility()) {
// If it's a switch node or sequence node, visit just the one
// visible child.
int index = node->get_visible_child();
if (index >= 0 && index < node->get_num_children()) {
CollisionLevelStateQuad next_state(level_state, node->get_child(index));
r_traverse_quad(next_state, pass);
}
} else if (node->is_lod_node()) {
// If it's an LODNode, visit the lowest level of detail with all
// bits, allowing collision with geometry under the lowest level
// of default; and visit all other levels without
// GeomNode::get_default_collide_mask(), allowing only collision
// with CollisionNodes and special geometry under higher levels of
// detail.
int index = DCAST(LODNode, node)->get_lowest_switch();
int num_children = node->get_num_children();
for (int i = 0; i < num_children; ++i) {
CollisionLevelStateQuad next_state(level_state, node->get_child(i));
if (i != index) {
next_state.set_include_mask(next_state.get_include_mask() &
~GeomNode::get_default_collide_mask());
}
r_traverse_quad(next_state, pass);
}
} else {
// Otherwise, visit all the children.
int num_children = node->get_num_children();
for (int i = 0; i < num_children; ++i) {
CollisionLevelStateQuad next_state(level_state, node->get_child(i));
r_traverse_quad(next_state, pass);
}
}
}

View File

@ -81,12 +81,17 @@ PUBLISHED:
void write(ostream &out, int indent_level) const;
private:
typedef pvector<CollisionLevelStateWord> LevelStatesWord;
void prepare_colliders_word(LevelStatesWord &level_states, const NodePath &root);
void r_traverse_word(CollisionLevelStateWord &level_state, size_t pass);
typedef pvector<CollisionLevelStateSingle> LevelStatesSingle;
void prepare_colliders_single(LevelStatesSingle &level_states, const NodePath &root);
void r_traverse_single(CollisionLevelStateSingle &level_state, size_t pass);
void prepare_colliders_array(CollisionLevelStateArray &level_state, const NodePath &root);
void r_traverse_array(CollisionLevelStateArray &level_state);
typedef pvector<CollisionLevelStateDouble> LevelStatesDouble;
void prepare_colliders_double(LevelStatesDouble &level_states, const NodePath &root);
void r_traverse_double(CollisionLevelStateDouble &level_state, size_t pass);
typedef pvector<CollisionLevelStateQuad> LevelStatesQuad;
void prepare_colliders_quad(LevelStatesQuad &level_states, const NodePath &root);
void r_traverse_quad(CollisionLevelStateQuad &level_state, size_t pass);
void compare_collider_to_node(CollisionEntry &entry,
const GeometricBoundingVolume *from_parent_gbv,

View File

@ -63,12 +63,13 @@ ConfigVariableBool respect_effective_normal
"collision solids (including polygons and planes) use their actual "
"normal for intersection and physics tests."));
ConfigVariableBool allow_collider_bitarray
("allow-collider-bitarray", false,
PRC_DESC("Set this true to enable the use of a BitArray to manage many "
ConfigVariableBool allow_collider_multiple
("allow-collider-multiple", false,
PRC_DESC("Set this true to enable the use of a DoubleBitMask or QuadBitMask "
"to manage many "
"colliders added to a single traverser in one pass. If this is "
"false, a finite BitMask is always used instead, which is faster "
"per node visited, but may require multiple passes."));
"false, a one-word BitMask is always used instead, which is faster "
"per pass, but may require more passes."));
////////////////////////////////////////////////////////////////////

View File

@ -27,7 +27,7 @@ NotifyCategoryDecl(collide, EXPCL_PANDA, EXPTP_PANDA);
extern EXPCL_PANDA ConfigVariableBool respect_prev_transform;
extern EXPCL_PANDA ConfigVariableBool respect_effective_normal;
extern EXPCL_PANDA ConfigVariableBool allow_collider_bitarray;
extern EXPCL_PANDA ConfigVariableBool allow_collider_multiple;
extern EXPCL_PANDA void init_libcollide();