support CollisionTube

This commit is contained in:
David Rose 2003-10-01 18:26:34 +00:00
parent 0a34bd4345
commit ab922b1353
2 changed files with 218 additions and 24 deletions

View File

@ -60,12 +60,14 @@
#include "selectiveChildNode.h"
#include "collisionNode.h"
#include "collisionSphere.h"
#include "collisionTube.h"
#include "collisionPlane.h"
#include "collisionPolygon.h"
#include "parametricCurve.h"
#include "nurbsCurve.h"
#include "classicNurbsCurve.h"
#include "nurbsCurveInterface.h"
#include "look_at.h"
#include <ctype.h>
#include <algorithm>
@ -1583,6 +1585,10 @@ make_collision_solids(EggGroup *start_group, EggGroup *egg_group,
case EggGroup::CST_sphere:
make_collision_sphere(egg_group, cnode, start_group->get_collide_flags());
break;
case EggGroup::CST_tube:
make_collision_tube(egg_group, cnode, start_group->get_collide_flags());
break;
}
if ((start_group->get_collide_flags() & EggGroup::CF_descend) != 0) {
@ -1697,17 +1703,8 @@ make_collision_sphere(EggGroup *egg_group, CollisionNode *cnode,
for (vi = vertices.begin(); vi != vertices.end(); ++vi) {
EggVertex *vtx = (*vi);
if (vtx->get_num_dimensions() == 3) {
center += vtx->get_pos3();
num_vertices++;
} else if (vtx->get_num_dimensions() == 4) {
LPoint4d p4 = vtx->get_pos4();
if (p4[3] != 0.0) {
center += LPoint3d(p4[0], p4[1], p4[2]) / p4[3];
num_vertices++;
}
}
center += vtx->get_pos3();
num_vertices++;
}
if (num_vertices > 0) {
@ -1720,19 +1717,9 @@ make_collision_sphere(EggGroup *egg_group, CollisionNode *cnode,
double radius2 = 0.0;
for (vi = vertices.begin(); vi != vertices.end(); ++vi) {
EggVertex *vtx = (*vi);
if (vtx->get_num_dimensions() == 3) {
LPoint3d p3 = vtx->get_pos3();
LVector3d v = p3 * mat - center;
radius2 = max(radius2, v.length_squared());
} else if (vtx->get_num_dimensions() == 4) {
LPoint4d p4 = vtx->get_pos4();
if (p4[3] != 0.0) {
LPoint3d p3 = LPoint3d(p4[0], p4[1], p4[2]) / p4[3];
LVector3d v = p3 * mat - center;
radius2 = max(radius2, v.length_squared());
}
}
LPoint3d p3 = vtx->get_pos3();
LVector3d v = p3 * mat - center;
radius2 = max(radius2, v.length_squared());
}
float radius = sqrtf(radius2);
@ -1744,6 +1731,208 @@ make_collision_sphere(EggGroup *egg_group, CollisionNode *cnode,
}
}
////////////////////////////////////////////////////////////////////
// Function: EggLoader::make_collision_tube
// Access: Private
// Description: Creates a single CollisionTube corresponding
// to the polygons associated with this group.
////////////////////////////////////////////////////////////////////
void EggLoader::
make_collision_tube(EggGroup *egg_group, CollisionNode *cnode,
EggGroup::CollideFlags flags) {
EggGroup *geom_group = find_collision_geometry(egg_group);
if (geom_group != (EggGroup *)NULL) {
// Collect all of the vertices.
pset<EggVertex *> vertices;
EggGroup::const_iterator ci;
for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) {
if ((*ci)->is_of_type(EggPrimitive::get_class_type())) {
EggPrimitive *prim = DCAST(EggPrimitive, *ci);
EggPrimitive::const_iterator pi;
for (pi = prim->begin(); pi != prim->end(); ++pi) {
vertices.insert(*pi);
}
}
}
// Now store the 3-d values in a vector for convenient access (and
// also determine the centroid). We compute this in node space.
size_t num_vertices = vertices.size();
if (num_vertices != 0) {
LMatrix4d mat = egg_group->get_vertex_to_node();
pvector<LPoint3d> vpos;
vpos.reserve(num_vertices);
LPoint3d center(0.0, 0.0, 0.0);
pset<EggVertex *>::const_iterator vi;
for (vi = vertices.begin(); vi != vertices.end(); ++vi) {
EggVertex *vtx = (*vi);
LPoint3d pos = vtx->get_pos3() * mat;
vpos.push_back(pos);
center += vtx->get_pos3();
}
center /= (double)num_vertices;
// Now that we have the centroid, we have to try to figure out
// the cylinder's major axis. Start by finding a point farthest
// from the centroid.
size_t i;
double radius2 = 0.0;
LPoint3d far_a = center;
for (i = 0; i < num_vertices; i++) {
double dist2 = (vpos[i] - center).length_squared();
if (dist2 > radius2) {
radius2 = dist2;
far_a = vpos[i];
}
}
// The point we have found above, far_a, must be one one of the
// endcaps. Now find another point, far_b, that is the farthest
// from far_a. This will be a point on the other endcap.
radius2 = 0.0;
LPoint3d far_b = center;
for (i = 0; i < num_vertices; i++) {
double dist2 = (vpos[i] - far_a).length_squared();
if (dist2 > radius2) {
radius2 = dist2;
far_b = vpos[i];
}
}
// Now we have far_a and far_b, one point on each endcap.
// However, these points are not necessarily centered on the
// endcaps, so we haven't figured out the cylinder's axis yet
// (the line between far_a and far_b will probably pass through
// the cylinder at an angle).
// So we still need to determine the full set of points in each
// endcap. To do this, we pass back through the set of points,
// categorizing each point into either "endcap a" or "endcap b".
// We also leave a hefty chunk of points in the middle
// uncategorized; this helps prevent us from getting a little
// bit lopsided with points near the middle that may appear to
// be closer to the wrong endcap.
LPoint3d cap_a_center(0.0, 0.0, 0.0);
LPoint3d cap_b_center(0.0, 0.0, 0.0);
int num_a = 0;
int num_b = 0;
// This is the threshold length; points farther away from the
// center than this are deemed to be in one endcap or the other.
double center_length = (far_a - far_b).length() / 4.0;
double center_length2 = center_length * center_length;
for (i = 0; i < num_vertices; i++) {
double dist2 = (vpos[i] - center).length_squared();
if (dist2 > center_length2) {
// This point is farther away from the center than
// center_length; therefore it belongs in an endcap.
double dist_a2 = (vpos[i] - far_a).length_squared();
double dist_b2 = (vpos[i] - far_b).length_squared();
if (dist_a2 < dist_b2) {
// It's in endcap a.
cap_a_center += vpos[i];
num_a++;
} else {
// It's in endcap b.
cap_b_center += vpos[i];
num_b++;
}
}
}
if (num_a > 0 && num_b > 0) {
cap_a_center /= (double)num_a;
cap_b_center /= (double)num_b;
// Now we finally have the major axis of the cylinder.
LVector3d axis = cap_b_center - cap_a_center;
axis.normalize();
// If the axis is *almost* parallel with a major axis, assume
// it is meant to be exactly parallel.
if (IS_THRESHOLD_ZERO(axis[0], 0.01)) {
axis[0] = 0.0;
}
if (IS_THRESHOLD_ZERO(axis[1], 0.01)) {
axis[1] = 0.0;
}
if (IS_THRESHOLD_ZERO(axis[2], 0.01)) {
axis[2] = 0.0;
}
axis.normalize();
// Transform all of the points so that the major axis is along
// the Y axis, and the origin is the center. This is very
// similar to the CollisionTube's idea of its canonical
// orientation (although not exactly the same, since it is
// centered on the origin instead of having point_a on the
// origin). It makes it easier to determine the length and
// radius of the cylinder.
LMatrix4d mat;
look_at(mat, axis, LVector3d(0.0, 0.0, 1.0), CS_zup_right);
mat.set_row(3, center);
LMatrix4d inv_mat;
inv_mat.invert_from(mat);
for (i = 0; i < num_vertices; i++) {
vpos[i] = vpos[i] * inv_mat;
}
double max_radius2 = 0.0;
// Now determine the radius.
for (i = 0; i < num_vertices; i++) {
LVector2d v(vpos[i][0], vpos[i][2]);
double radius2 = v.length_squared();
if (radius2 > max_radius2) {
max_radius2 = radius2;
}
}
// And with the radius, we can determine the length. We need
// to know the radius first because we want the round endcaps
// to enclose all points.
double min_y = 0.0;
double max_y = 0.0;
for (i = 0; i < num_vertices; i++) {
LVector2d v(vpos[i][0], vpos[i][2]);
double radius2 = v.length_squared();
if (vpos[i][1] < min_y) {
// Adjust the Y pos to account for the point's distance
// from the axis.
double factor = sqrt(max_radius2 - radius2);
min_y = min(min_y, vpos[i][1] + factor);
} else if (vpos[i][1] > max_y) {
double factor = sqrt(max_radius2 - radius2);
max_y = max(max_y, vpos[i][1] - factor);
}
}
double length = max_y - min_y;
double radius = sqrt(max_radius2);
// Finally, we have everything we need to define the cylinder.
LVector3d half = axis * (length / 2.0);
LPoint3d point_a = center - half;
LPoint3d point_b = center + half;
CollisionTube *cstube =
new CollisionTube(LCAST(float, point_a), LCAST(float, point_b),
radius);
apply_collision_flags(cstube, flags);
cnode->add_solid(cstube);
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: EggLoader::apply_collision_flags
// Access: Private
@ -2028,6 +2217,9 @@ do_expand_object_type(EggGroup *egg_group, const pset<string> &expanded,
} else if (cmp_nocase_uh(object_type, "sphere") == 0) {
egg_syntax = "<Collide> { Sphere descend }";
} else if (cmp_nocase_uh(object_type, "tube") == 0) {
egg_syntax = "<Collide> { Tube descend }";
} else if (cmp_nocase_uh(object_type, "trigger") == 0) {
egg_syntax = "<Collide> { Polyset descend intangible }";

View File

@ -112,6 +112,8 @@ private:
EggGroup::CollideFlags flags);
void make_collision_sphere(EggGroup *egg_group, CollisionNode *cnode,
EggGroup::CollideFlags flags);
void make_collision_tube(EggGroup *egg_group, CollisionNode *cnode,
EggGroup::CollideFlags flags);
void apply_collision_flags(CollisionSolid *solid,
EggGroup::CollideFlags flags);
EggGroup *find_collision_geometry(EggGroup *egg_group);