support CollisionTube
This commit is contained in:
parent
0a34bd4345
commit
ab922b1353
|
|
@ -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 }";
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue