cod5-sdk/raw/maps/mp/_geometry.gsc
2008-11-20 00:00:00 +00:00

1425 lines
42 KiB
Text

/*
_geometry.gsc
Copyright (c) 2008 Certain Affinity, Inc. All rights reserved.
Friday April 18, 2008 10:55am Stefan S.
CoD5 coordinate system:
+X= right | +y
+Y= forward |
+Z= up |______ +x
/
+z /
geometric & math primitives:
----------------------------
point3d= (x,y,z)
vector3d= (i,j,k)
matrix4x4= n[16]
Cylinder: default orientation is radial base in X-Y plane, stands in +Z direction
Cone: default orientation is radial base in X-Y plane, stands in +Z direction
Pill: default orientation is radius in X-Y plane, pill grows in +Z direction; same as cylinder, but with the addition of rounded end caps
Box: default orientation is axis-aligned
point-in-geometry collision test result returned as a vector:
---------------------------------------------------------------
v[0] >0 if the tested point is inside the primitive
v[1] == distance^2 from sphere center, box centroid, cylinder/pill/cone base
v[2] == distance^2 from cylinder/pill/cone axis
For point-in-geometry tests, there are 2 functions for each geometric primitive: one for single-point tests and one for testing a list of points.
They are each optimized for their specific case so use the appropriate one.
*/
/* ---------- includes */
#include common_scripts\utility;
#include maps\mp\_utility;
/* ---------- point3d */
//float
point3d_distance_squared(
p0,
p1)
{
/* result= (p1[0]-p0[0])*(p1[0]-p0[0]) + (p1[1]-p0[1])*(p1[1]-p0[1]) + ((p1[2]-p0[2])*p1[2]-p0[2]);
return result;*/
// use native code
return DistanceSquared(p0, p1);
}
//float
point3d_distance(
p0,
p1)
{
//result= sqrt(point3d_distance_squared(p0, p1));
// use native code
result= Distance(p0, p1);
return result;
}
//point3d
point3d_translate(
point,
translation)
{
result= point+translation;
return result;
}
/* ---------- vector3d */
//float
vector3d_magnitude_squared(
vector)
{
/*magnitude_squared= vector[0]*vector[0]+vector[1]*vector[1]+vector[2]*vector[2];
return magnitude_squared;*/
// use native code
return LengthSquared(vector);
}
//float
vector3d_magnitude(
vector)
{
/*magnitude_squared= vector3d_magnitude_squared(vector);
magnitude= sqrt(magnitude_squared);
return magnitude;*/
// use native code
return Length(vector);
}
//vector3d
vector3d_normalize(
vector)
{
/* magnitude_squared= vector3d_magnitude_squared(vector);
result= vector;
if (magnitude_squared>0.0001)
{
if ((magnitude_squared-1) > 0.0001)
{
one_over_magnitude= 1/sqrt(magnitude_squared);
result= vector*one_over_magnitude;
}
//else, already normalized
}*/
// use native code
result= VectorNormalize(vector);
return result;
}
//vector3d
vector3d_scale(
vector,
scale)
{
result= scale*vector;
return result;
}
// vector3d
vector3d_from_points(
source,
dest)
{
result= dest-source;
return result;
}
//float
vector3d_dot_product(
vector_a,
vector_b)
{
//return vector_a[0]*vector_b[0] + vector_a[1]*vector_b[1] + vector_a[2]*vector_b[2];
// use native code
return VectorDot(vector_a, vector_b);
}
//vector3d
vector3d_cross_product(
vector_a,
vector_b)
{
i= (vector_a[1]*vector_b[2]) - (vector_a[2]*vector_b[1]);
j= -1*((vector_a[0]*vector_b[2]) - (vector_a[2]*vector_b[0]));
k= (vector_a[0]*vector_b[1]) - (vector_a[1]*vector_b[0]);
result= (i, j, k);
return result;
}
//float
vector3d_angle_between(
vector_a,
vector_b)
{
ab= vector3d_magnitude(vector_a)*vector3d_magnitude(vector_b);
assertex((ab>0.001), "zero-vector");
result= Acos(vector3d_dot_product(vector_a, vector_b)/ab);
return result;
}
//float
vector3d_component_of_a_along_b(
vector_a,
vector_b)
{
b= vector3d_magnitude(vector_b);
assertex((b > 0.001), "zero-vector");
b_over_magnitude_b= vector3d_scale(vector_b, 1/b);
result= vector3d_dot_product(vector_a, b_over_magnitude_b);
return result;
}
// bool
vector3d_is_zero(
vector)
{
return (vector3d_magnitude_squared(vector) < 0.00001);
}
//bool
vector3d_is_normalized(
vector)
{
return (vector3d_magnitude_squared(vector)-1 < 0.001);
}
//bool
vector3d_orthogonal(
vector_a,
vector_b)
{
dot_product= vector3d_dot_product(vector_a, vector_b);
return dot_product < 0.001;
}
//bool
vector3d_parallel(
vector_a,
vector_b)
{
cross_product= vector3d_cross_product(vector_a, vector_b);
return vector3d_is_zero(cross_product);
}
/* ---------- matrix4x4 */
//matrix4x4
matrix4x4_build_identity()
{
matrix= [];
matrix[00]= 1; matrix[01]= 0; matrix[02]= 0; matrix[03]= 0;
matrix[04]= 0; matrix[05]= 1; matrix[06]= 0; matrix[07]= 0;
matrix[08]= 0; matrix[09]= 0; matrix[10]= 1; matrix[11]= 0;
matrix[12]= 0; matrix[13]= 0; matrix[14]= 0; matrix[15]= 1;
return matrix;
}
//matrix4x4
matrix4x4_build_translation(
translation_x,
translation_y,
translation_z)
{
matrix= matrix4x4_build_identity();
matrix[03]= translation_x;
matrix[07]= translation_y;
matrix[11]= translation_z;
return matrix;
}
//matrix4x4
matrix4x4_build_scale(
scale_x,
scale_y,
scale_z)
{
matrix= matrix4x4_build_identity();
matrix[00]= scale_x;
matrix[05]= scale_y;
matrix[10]= scale_z;
return matrix;
}
/* ###stefan $REVIEW this code should be fine but I have not tested it yet, so it's commented out
//matrix4x4
matrix4x4_build_rotation_from_axis_and_angle(
axis,
angle)
{
unit_axis= vector3d_normalize(axis);
c= Cos(angle);
s= Sin(angle);
one_minus_c= 1-c;
result= [];
result[00]= unit_axis[0]*unit_axis[0]*one_minus_c+c;
result[01]= unit_axis[0]*unit_axis[1]*one_minus_c+unit_axis[2]*s;
result[02]= unit_axis[0]*unit_axis[2]*one_minus_c-unit_axis[1]*s;
result[03]= 0;
result[04]= unit_axis[0]*unit_axis[1]*one_minus_c-unit_axis[2]*s;
result[05]= unit_axis[1]*unit_axis[1]*one_minus_c+c;
result[06]= unit_axis[1]*unit_axis[2]*one_minus_c+unit_axis[0]*s;
result[07]= 0;
result[08]= unit_axis[0]*unit_axis[2]*one_minus_c+unit_axis[1]*s;
result[09]= unit_axis[1]*unit_axis[1]*one_minus_c-unit_axis[0]*s;
result[10]= unit_axis[2]*unit_axis[2]*one_minus_c+c;
result[11]= 0;
result[12]= 0; result[13]= 0; result[14]= 0; result[15]= 1;
return result;
}*/
//matrix4x4
matrix4x4_build_rotation_from_forward_and_up(
forward,
up)
{
left= vector3d_cross_product(up, forward);
return matrix4x4_build_rotation_from_axes(forward, up, left);
}
//matrix4x4
matrix4x4_build_rotation_from_axes(
forward,
up,
left)
{
result= [];
result[00]= forward[0]; result[01]= forward[1]; result[02]= forward[2]; result[03]= 0;
result[04]= left[0]; result[05]= left[1]; result[06]= left[2]; result[07]= 0;
result[08]= up[0]; result[09]= up[1]; result[10]= up[2]; result[11]= 0;
result[12]= 0; result[13]= 0; result[14]= 0; result[15]= 1;
return result;
}
//matrix4x4
matrix4x4_build_from_rotation_and_translation(
forward,
up,
left,
translation)
{
result= [];
result[00]= forward[0]; result[01]= forward[1]; result[02]= forward[2]; result[03]= translation[0];
result[04]= left[0]; result[05]= left[1]; result[06]= left[2]; result[07]= translation[1];
result[08]= up[0]; result[09]= up[1]; result[10]= up[2]; result[11]= translation[2];
result[12]= 0; result[13]= 0; result[14]= 0; result[15]= 1;
return result;
}
//matrix4x4
matrix4x4_multiply(
matrix_a,
matrix_b)
{
result= [];
result[00]= matrix_a[00]*matrix_b[00] + matrix_a[01]*matrix_b[04] + matrix_a[02]*matrix_b[08] + matrix_a[03]*matrix_b[12];
result[01]= matrix_a[00]*matrix_b[01] + matrix_a[01]*matrix_b[05] + matrix_a[02]*matrix_b[09] + matrix_a[03]*matrix_b[13];
result[02]= matrix_a[00]*matrix_b[02] + matrix_a[01]*matrix_b[06] + matrix_a[02]*matrix_b[10] + matrix_a[03]*matrix_b[14];
result[03]= matrix_a[00]*matrix_b[03] + matrix_a[01]*matrix_b[07] + matrix_a[02]*matrix_b[11] + matrix_a[03]*matrix_b[15];
result[04]= matrix_a[04]*matrix_b[00] + matrix_a[05]*matrix_b[04] + matrix_a[06]*matrix_b[08] + matrix_a[07]*matrix_b[12];
result[05]= matrix_a[04]*matrix_b[01] + matrix_a[05]*matrix_b[05] + matrix_a[06]*matrix_b[09] + matrix_a[07]*matrix_b[13];
result[06]= matrix_a[04]*matrix_b[02] + matrix_a[05]*matrix_b[06] + matrix_a[06]*matrix_b[10] + matrix_a[07]*matrix_b[14];
result[07]= matrix_a[04]*matrix_b[03] + matrix_a[05]*matrix_b[07] + matrix_a[06]*matrix_b[11] + matrix_a[07]*matrix_b[15];
result[08]= matrix_a[08]*matrix_b[00] + matrix_a[09]*matrix_b[04] + matrix_a[10]*matrix_b[08] + matrix_a[11]*matrix_b[12];
result[09]= matrix_a[08]*matrix_b[01] + matrix_a[09]*matrix_b[05] + matrix_a[10]*matrix_b[09] + matrix_a[11]*matrix_b[13];
result[10]= matrix_a[08]*matrix_b[02] + matrix_a[09]*matrix_b[06] + matrix_a[10]*matrix_b[10] + matrix_a[11]*matrix_b[14];
result[11]= matrix_a[08]*matrix_b[03] + matrix_a[09]*matrix_b[07] + matrix_a[10]*matrix_b[11] + matrix_a[11]*matrix_b[15];
result[12]= matrix_a[12]*matrix_b[00] + matrix_a[13]*matrix_b[04] + matrix_a[14]*matrix_b[08] + matrix_a[15]*matrix_b[12];
result[13]= matrix_a[12]*matrix_b[01] + matrix_a[13]*matrix_b[05] + matrix_a[14]*matrix_b[09] + matrix_a[15]*matrix_b[13];
result[14]= matrix_a[12]*matrix_b[02] + matrix_a[13]*matrix_b[06] + matrix_a[14]*matrix_b[10] + matrix_a[15]*matrix_b[14];
result[15]= matrix_a[12]*matrix_b[03] + matrix_a[13]*matrix_b[07] + matrix_a[14]*matrix_b[11] + matrix_a[15]*matrix_b[15];
return result;
}
//vector3d
matrix4x4_transform_vector3d(
vector,
matrix)
{
x= vector[0]*matrix[00] + vector[1]*matrix[04] + vector[2]*matrix[08];
y= vector[0]*matrix[01] + vector[1]*matrix[05] + vector[2]*matrix[09];
z= vector[0]*matrix[02] + vector[1]*matrix[06] + vector[2]*matrix[10];
result= (x, y, z);
return result;
}
//point3d
matrix4x4_transform_point3d(
point,
matrix)
{
//###stefan $REVIEW could use native game script fxn Matrix4x4TransformPoints()
x= point[0]*matrix[00] + point[1]*matrix[04] + point[2]*matrix[08] + matrix[03];
y= point[0]*matrix[01] + point[1]*matrix[05] + point[2]*matrix[09] + matrix[07];
z= point[0]*matrix[02] + point[1]*matrix[06] + point[2]*matrix[10] + matrix[11];
result= (x, y, z);
return result;
}
//transformation of a points list
matrix4x4_transform_points3d(
points,
matrix)
{
/*###stefan $NOTE there is now a native code implementation (~ 30% faster debug rendering for player spawn state viewing
transformed_points= [];
for (point_index= 0; point_index<points.size; point_index++)
{
transformed_points[point_index]= matrix4x4_transform_point3d(points[point_index], matrix);
}
return transformed_points;*/
return Matrix4x4TransformPoints(matrix, points);
}
/* ---------- point-in-geometry tests */
//collision
collision_test_point_in_sphere(
point,
sphere_origin,
sphere_radius)
{
//prof_begin("collision_test_point_in_sphere");
assertex((sphere_radius>0.001), "invalid sphere radius");
collision= (0.0, 0.0, 0.0);
dsquared= point3d_distance_squared(point, sphere_origin);
rsquared= sphere_radius*sphere_radius;
if (dsquared<rsquared)
{
collision= (1.0, dsquared, 0.0);
}
//prof_end("collision_test_point_in_sphere");
return collision;
}
//collision
collision_test_points_in_sphere(
point_list_s,
sphere_origin,
sphere_radius)
{
//prof_begin("collision_test_points_in_sphere");
assertex((sphere_radius>0.001), "invalid sphere radius");
/*###stefan $NOTE we now call into native code
collision_results_s= spawn_array_struct();
// pre-computed parameters
rsquared= sphere_radius*sphere_radius;
// test point list
for (point_index= 0; point_index<point_list_s.a.size; point_index++)
{
collision= (0.0, 0.0, 0.0);
dsquared= point3d_distance_squared(point_list_s.a[point_index], sphere_origin);
if (dsquared<rsquared)
{
collision= (1.0, dsquared, 0.0);
}
collision_results_s.a[point_index]= collision;
}*/
/*###stefan $NOTE we now call into native code*/
collision_results_s= SpawnStruct();
collision_results_s.a= CollisionTestPointsInSphere(point_list_s.a, sphere_origin, 0.0+sphere_radius);
/**/
//prof_end("collision_test_points_in_sphere");
return collision_results_s;
}
//collision
collision_test_point_in_cylinder(
point,
cylinder_base, // base point of cylinder
cylinder_radius,
cylinder_height,
cylinder_height_unit_vector) // direction & height of cylinder from cylinder_base
{
//prof_begin("collision_test_point_in_cylinder");
assertex((cylinder_radius>0.001) && (cylinder_height>0.001), "invalid cylinder geometry");
assertex(vector3d_is_normalized(cylinder_height_unit_vector), "cylinder height vector must be unit vector");
collision= (0.0, 0.0, 0.0);
axis_midpoint= cylinder_base+vector3d_scale(cylinder_height_unit_vector, cylinder_height*0.5);
// perform a bounding sphere check first (fast)
bounding_sphere_radius= cylinder_radius+0.5*cylinder_height;
bounding_sphere_rsquared= bounding_sphere_radius*bounding_sphere_radius;
if (point3d_distance_squared(point, axis_midpoint) < bounding_sphere_rsquared)
{
// test for point between top & bottom planes
amp= vector3d_from_points(axis_midpoint, point);
midpoint_axial_distance= Abs(vector3d_component_of_a_along_b(amp, cylinder_height_unit_vector));
// between top & bottom if: midpoint_axial_distance < height/2
if (midpoint_axial_distance < cylinder_height*0.5)
{
// test for point inside cylinder radius
dsquared= point3d_distance_squared(point, axis_midpoint);
rsquared= dsquared-(midpoint_axial_distance*midpoint_axial_distance);
if (rsquared<(cylinder_radius*cylinder_radius))
{
collision= (1.0, point3d_distance_squared(cylinder_base, point), rsquared);
}
}
}
//prof_end("collision_test_point_in_cylinder");
return collision;
}
//collision
collision_test_points_in_cylinder(
point_list_s,
cylinder_base, // base point of cylinder
cylinder_radius,
cylinder_height,
cylinder_height_unit_vector) // direction & height of cylinder from cylinder_base
{
//prof_begin("collision_test_points_in_cylinder");
assertex((cylinder_radius>0.001) && (cylinder_height>0.001), "invalid cylinder geometry");
assertex(vector3d_is_normalized(cylinder_height_unit_vector), "cylinder height vector must be unit vector");
/*###stefan $NOTE we now call into native code
collision_results_s= spawn_array_struct();
// pre-computed parameters
axis_midpoint= cylinder_base+vector3d_scale(cylinder_height_unit_vector, cylinder_height*0.5);
bounding_sphere_radius= cylinder_radius+0.5*cylinder_height;
bounding_sphere_radius_squared= bounding_sphere_radius*bounding_sphere_radius;
cylinder_radius_squared= cylinder_radius*cylinder_radius;
half_cylinder_height= cylinder_height*0.5;
// test point list
for (point_index= 0; point_index<point_list_s.a.size; point_index++)
{
collision= (0.0, 0.0, 0.0);
// perform a bounding sphere check first (fast)
if (point3d_distance_squared(point_list_s.a[point_index], axis_midpoint)<bounding_sphere_radius_squared)
{
// test for point between top & bottom planes
amp= vector3d_from_points(axis_midpoint, point_list_s.a[point_index]);
midpoint_axial_distance= Abs(vector3d_component_of_a_along_b(amp, cylinder_height_unit_vector));
// between top & bottom if: midpoint_axial_distance < height/2
if (midpoint_axial_distance < half_cylinder_height)
{
// test for point inside cylinder radius
dsquared= point3d_distance_squared(point_list_s.a[point_index], axis_midpoint);
rsquared= dsquared-(midpoint_axial_distance*midpoint_axial_distance);
if (rsquared<cylinder_radius_squared)
{
collision= (1.0, point3d_distance_squared(cylinder_base, point_list_s.a[point_index]), rsquared);
}
}
}
collision_results_s.a[point_index]= collision;
}*/
/*###stefan $NOTE we now call into native code*/
collision_results_s= SpawnStruct();
collision_results_s.a= CollisionTestPointsInCylinder(
point_list_s.a,
cylinder_base,
0.0+cylinder_radius,
0.0+cylinder_height,
cylinder_height_unit_vector);
/**/
//prof_end("collision_test_points_in_cylinder");
return collision_results_s;
}
//collision
collision_test_point_in_pill(
point,
cylinder_base, // base point of cylinder
cylinder_radius,
cylinder_height,
cylinder_height_unit_vector, // direction of cylinder from cylinder_base
cylinder_radial_vector) // for conveniece in calculating collision results
{
//prof_begin("collision_test_point_in_pill");
collision= collision_test_point_in_sphere(point, cylinder_base, cylinder_radius);
if (collision[0]<1)
{
collision= collision_test_point_in_sphere(point, point3d_translate(cylinder_base, vector3d_scale(cylinder_height_unit_vector, cylinder_height)), cylinder_radius);
if (collision[0]<1)
{
collision= collision_test_point_in_cylinder(point, cylinder_base, cylinder_radius, cylinder_height, cylinder_height_unit_vector);
}
else
{
distance_from_axis= vector3d_component_of_a_along_b(vector3d_from_points(cylinder_base, point), cylinder_radial_vector);
collision= (1.0, point3d_distance_squared(cylinder_base, point), distance_from_axis*distance_from_axis);
}
}
else
{
distance_from_axis= vector3d_component_of_a_along_b(vector3d_from_points(cylinder_base, point), cylinder_radial_vector);
collision= (1.0, point3d_distance_squared(cylinder_base, point), distance_from_axis*distance_from_axis);
}
//prof_end("collision_test_point_in_pill");
return collision;
}
//collision
collision_test_points_in_pill(
point_list_s,
cylinder_base, // base point of cylinder
cylinder_radius,
cylinder_height,
cylinder_height_unit_vector, // direction of cylinder from cylinder_base
cylinder_radial_vector) // for conveniece in calculating collision results
{
//prof_begin("collision_test_points_in_pill");
collision_results_s= spawn_array_struct();
// pre-computed parameters
cylinder_top= point3d_translate(cylinder_base, vector3d_scale(cylinder_height_unit_vector, cylinder_height));
axis_midpoint= point3d_translate(cylinder_base, vector3d_scale(cylinder_height_unit_vector, cylinder_height*0.5));
bounding_sphere_radius= cylinder_radius+0.5*cylinder_height;
bounding_sphere_radius_squared= bounding_sphere_radius*bounding_sphere_radius;
cylinder_radius_squared= cylinder_radius*cylinder_radius;
half_cylinder_height= cylinder_height*0.5;
// test point list
for (point_index= 0; point_index<point_list_s.a.size; point_index++)
{
collision= (0.0, 0.0, 0.0);
// perform a bounding sphere check first (fast)
if (point3d_distance_squared(point_list_s.a[point_index], axis_midpoint)<bounding_sphere_radius_squared)
{
// test against base sphere
if (point3d_distance_squared(point_list_s.a[point_index], cylinder_base)<cylinder_radius_squared)
{
collision= (1.0, 0.0, 0.0);
}
// test against top sphere
else if (point3d_distance_squared(point_list_s.a[point_index], cylinder_top)<cylinder_radius_squared)
{
collision= (1.0, 0.0, 0.0);
}
// test against cylinder
else
{
// test for point between top & bottom planes
amp= vector3d_from_points(axis_midpoint, point_list_s.a[point_index]);
midpoint_axial_distance= Abs(vector3d_component_of_a_along_b(amp, cylinder_height_unit_vector));
// between top & bottom if: midpoint_axial_distance < height/2
if (midpoint_axial_distance < half_cylinder_height)
{
// test for point inside cylinder radius
dsquared= point3d_distance_squared(point_list_s.a[point_index], axis_midpoint);
rsquared= dsquared-(midpoint_axial_distance*midpoint_axial_distance);
if (rsquared<cylinder_radius_squared)
{
collision= (1.0, 0.0, 0.0);
}
}
}
}
if (collision[0]>0.0)
{
distance_from_axis= vector3d_component_of_a_along_b(vector3d_from_points(cylinder_base, point_list_s.a[point_index]), cylinder_radial_vector);
collision= (1.0, point3d_distance_squared(cylinder_base, point_list_s.a[point_index]), distance_from_axis*distance_from_axis);
}
collision_results_s.a[point_index]= collision;
}
//prof_end("collision_test_points_in_pill");
return collision_results_s;
}
//collision
collision_test_point_in_cone(
point,
cone_base, // base point of cone
cone_base_radius,
cone_height,
cone_height_unit_vector, // direction & height of cone from cone_base to the tip
cone_radial_unit_vector) // a radial vector (any unit vector orthogonal to the height vector - passed in for convenience, since scripting always has available)
{
//prof_begin("collision_test_point_in_cone");
assertex((cone_base_radius>0.001) && (cone_height>0.001), "invalid cone geometry");
assertex(vector3d_is_normalized(cone_height_unit_vector), "cone height vector must be unit vector");
assertex(vector3d_is_normalized(cone_radial_unit_vector), "cone radial vector must be unit vector");
collision= (0.0, 0.0, 0.0);
axis_midpoint= cone_base+vector3d_scale(cone_height_unit_vector, cone_height*0.5);
// perform a bounding sphere check first (fast)
bounding_sphere_radius= cone_base_radius+0.5*cone_height;
bounding_sphere_rsquared= bounding_sphere_radius*bounding_sphere_radius;
if (point3d_distance_squared(point, axis_midpoint) < bounding_sphere_rsquared)
{
// test for point between top & bottom planes
amp= vector3d_from_points(axis_midpoint, point);
midpoint_axial_distance= Abs(vector3d_component_of_a_along_b(amp, cone_height_unit_vector));
// between top & bottom if: midpoint_axial_distance < height/2
if (midpoint_axial_distance < cone_height*0.5)
{
// test for point inside cylinder radius (using comparison of angles between vectors)
cone_tip= cone_base+vector3d_scale(cone_height_unit_vector, cone_height);
radial_edge= cone_base+vector3d_scale(cone_radial_unit_vector, cone_base_radius);
tp= vector3d_from_points(cone_tip, point);
te= vector3d_from_points(cone_tip, radial_edge);
to= vector3d_from_points(cone_tip, cone_base);
tp_dot_to= vector3d_dot_product(tp, to);
te_dot_to= vector3d_dot_product(te, to);
tp_magnitude_squared= Max(0.00001, vector3d_magnitude_squared(tp));
te_magnitude_squared= Max(0.00001, vector3d_magnitude_squared(te));
if ((tp_dot_to*tp_dot_to/tp_magnitude_squared) > (te_dot_to*te_dot_to/te_magnitude_squared))
{
distance_from_origin= point3d_distance(cone_base, point);
distance_from_axis= vector3d_component_of_a_along_b(vector3d_from_points(cone_base, point), cone_radial_unit_vector);
collision= (1.0, distance_from_origin*distance_from_origin, distance_from_axis*distance_from_axis);
}
}
}
//prof_end("collision_test_point_in_cone");
return collision;
}
//collision
collision_test_points_in_cone(
point_list_s,
cone_base, // base point of cone
cone_base_radius,
cone_height,
cone_height_unit_vector, // direction & height of cone from cone_base to the tip
cone_radial_unit_vector) // a radial vector (any unit vector orthogonal to the height vector - passed in for convenience, since scripting always has available)
{
//prof_begin("collision_test_points_in_cone");
assertex((cone_base_radius>0.001) && (cone_height>0.001), "invalid cone geometry");
assertex(vector3d_is_normalized(cone_height_unit_vector), "cone height vector must be unit vector");
assertex(vector3d_is_normalized(cone_radial_unit_vector), "cone radial vector must be unit vector");
collision_results_s= spawn_array_struct();
// pre-computed parameters
axis_midpoint= cone_base+vector3d_scale(cone_height_unit_vector, cone_height*0.5);
bounding_sphere_radius= cone_base_radius+0.5*cone_height;
bounding_sphere_radius_squared= bounding_sphere_radius*bounding_sphere_radius;
half_cone_height= 0.5*cone_height;
cone_tip= cone_base+vector3d_scale(cone_height_unit_vector, cone_height);
radial_edge= cone_base+vector3d_scale(cone_radial_unit_vector, cone_base_radius);
te= vector3d_from_points(cone_tip, radial_edge);
to= vector3d_from_points(cone_tip, cone_base);
te_dot_to= vector3d_dot_product(te, to);
te_magnitude_squared= Max(0.00001, vector3d_magnitude_squared(te));
teto2_over_te= te_dot_to*te_dot_to/te_magnitude_squared;
// test point list
for (point_index= 0; point_index<point_list_s.a.size; point_index++)
{
collision= (0.0, 0.0, 0.0);
// perform a bounding sphere check first (fast)
if (point3d_distance_squared(point_list_s.a[point_index], axis_midpoint)<bounding_sphere_radius_squared)
{
// test for point between top & bottom planes
amp= vector3d_from_points(axis_midpoint, point_list_s.a[point_index]);
midpoint_axial_distance= Abs(vector3d_component_of_a_along_b(amp, cone_height_unit_vector));
// between top & bottom if: midpoint_axial_distance < height/2
if (midpoint_axial_distance < half_cone_height)
{
// test for point inside cylinder radius (using comparison of angles between vectors)
tp= vector3d_from_points(cone_tip, point_list_s.a[point_index]);
tp_dot_to= vector3d_dot_product(tp, to);
tp_magnitude_squared= Max(0.00001, vector3d_magnitude_squared(tp));
if ((tp_dot_to*tp_dot_to/tp_magnitude_squared) > teto2_over_te)
{
distance_from_origin= point3d_distance(cone_base, point_list_s.a[point_index]);
distance_from_axis= vector3d_component_of_a_along_b(vector3d_from_points(cone_base, point_list_s.a[point_index]), cone_radial_unit_vector);
collision= (1.0, distance_from_origin*distance_from_origin, distance_from_axis*distance_from_axis);
}
}
}
collision_results_s.a[point_index]= collision;
}
//prof_end("collision_test_points_in_cone");
return collision_results_s;
}
//collision
collision_test_point_in_box(
point,
box_origin, // box centroid
box_width, // x-dimension
box_height, // y-dimension
box_depth, // z-dimension
forward,
up)
{
//prof_begin("collision_test_point_in_box");
assertex((box_width>0.001) && (box_height>0.001) && (box_depth>0.001), "invalid box geometry");
collision= (0.0, 0.0, 0.0);
// perform a bounding sphere check first (fast)
// use radius= sum(largest 2 dimensions)
bounding_sphere_radius= (box_width+box_height+box_depth) - Min(Min(box_width, box_height), box_depth);
dsquared= point3d_distance_squared(box_origin, point);
if (dsquared<(bounding_sphere_radius*bounding_sphere_radius))
{
// test if point is inside faces of the box
transform= matrix4x4_build_rotation_from_forward_and_up(forward, up);
box_forward= matrix4x4_transform_vector3d((box_width, 0, 0), transform);
box_left= matrix4x4_transform_vector3d((0, box_height, 0), transform);
box_up= matrix4x4_transform_vector3d((0, 0, box_depth), transform);
bp= vector3d_from_points(box_origin, point);
// front & back faces
forward_distance= Abs(vector3d_component_of_a_along_b(bp, box_forward));
if (forward_distance < (0.5*box_width))
{
// left & right faces
left_distance= Abs(vector3d_component_of_a_along_b(bp, box_left));
if (left_distance < (0.5*box_height))
{
// top & bottom faces
up_distance= Abs(vector3d_component_of_a_along_b(bp, box_up));
if (up_distance < (0.5*box_depth))
{
collision= (1.0, point3d_distance_squared(box_origin, point), 0.0);
}
}
}
}
//prof_end("collision_test_point_in_box");
return collision;
}
//collision
collision_test_points_in_box(
point_list_s,
box_origin, // box centroid
box_width, // x-dimension
box_height, // y-dimension
box_depth, // z-dimension
forward,
up)
{
//prof_begin("collision_test_points_in_box");
assertex((box_width>0.001) && (box_height>0.001) && (box_depth>0.001), "invalid box geometry");
collision_results_s= spawn_array_struct();
// pre-computed parameters
bounding_sphere_radius= (box_width+box_height+box_depth) - Min(Min(box_width, box_height), box_depth); // use radius= sum(largest 2 dimensions)
bounding_sphere_radius_squared= bounding_sphere_radius*bounding_sphere_radius;
transform= matrix4x4_build_rotation_from_forward_and_up(forward, up);
box_forward= matrix4x4_transform_vector3d((box_width, 0, 0), transform);
box_left= matrix4x4_transform_vector3d((0, box_height, 0), transform);
box_up= matrix4x4_transform_vector3d((0, 0, box_depth), transform);
half_box_width= 0.5*box_width;
half_box_height= 0.5*box_height;
half_box_depth= 0.5*box_depth;
// test point list
for (point_index= 0; point_index<point_list_s.a.size; point_index++)
{
collision= (0.0, 0.0, 0.0);
// perform a bounding sphere check first (fast)
if (point3d_distance_squared(box_origin, point_list_s.a[point_index])<bounding_sphere_radius_squared)
{
// test if point is inside faces of the box
bp= vector3d_from_points(box_origin, point_list_s.a[point_index]);
// front & back faces
forward_distance= Abs(vector3d_component_of_a_along_b(bp, box_forward));
if (forward_distance < half_box_width)
{
// left & right faces
left_distance= Abs(vector3d_component_of_a_along_b(bp, box_left));
if (left_distance < half_box_height)
{
// top & bottom faces
up_distance= Abs(vector3d_component_of_a_along_b(bp, box_up));
if (up_distance < half_box_depth)
{
collision= (1.0, point3d_distance_squared(box_origin, point_list_s.a[point_index]), 0.0);
}
}
}
}
collision_results_s.a[point_index]= collision;
}
//prof_end("collision_test_points_in_box");
return collision_results_s;
}
/* ---------- geometry debug rendering */
draw_line(
p0,
p1,
rgb_color)
{
k_depth_test= false;
k_draw_duration_server_frames= 1;
Line(p0, p1, rgb_color, k_depth_test, k_draw_duration_server_frames);
return;
}
draw_line_segments(
points,
rgb_color)
{
k_depth_test= false;
k_draw_duration_server_frames= 1;
for (point_index= 1; point_index<points.size; point_index++)
{
Line(points[point_index-1], points[point_index], rgb_color, k_depth_test, k_draw_duration_server_frames);
}
return;
}
draw_line_list(
points,
rgb_color)
{
k_depth_test= false;
k_draw_duration_server_frames= 1;
LineList(points, rgb_color, k_depth_test, k_draw_duration_server_frames);
return;
}
draw_sphere(
sphere_origin, //point3d
sphere_radius, //float
forward, //vector3d
up, //vector3d
rgb_color)
{
points= generate_sphere_points_list(sphere_origin, sphere_radius, forward, up);
draw_line_list(points, rgb_color);
/*for (list_index= 0; list_index<points.size; list_index++)
{
draw_line_segments(points[list_index], rgb_color);
}*/
return;
}
draw_cylinder(
cylinder_base, //point3d
cylinder_radius, //float
cylinder_height, //float
forward, //vector3d
up, //vector3d
rgb_color)
{
points= generate_cylinder_points_list(cylinder_base, cylinder_radius, cylinder_height, forward, up);
draw_line_list(points, rgb_color);
/*for (list_index= 0; list_index<points.size; list_index++)
{
draw_line_segments(points[list_index], rgb_color);
}*/
return;
}
draw_box(
centroid, //point3d
dimensions, //vector3d
forward, //vector3d
up, //vector3d
rgb_color)
{
points= generate_box_points_lists(centroid, dimensions, forward, up);
for (list_index= 0; list_index<points.size; list_index++)
{
draw_line_segments(points[list_index], rgb_color);
}
return;
}
draw_pill(
pill_base, //point3d
pill_radius, //float
pill_height, //float
forward, //vector3d
up, //vector3d
rgb_color)
{
points= generate_pill_points_lists(pill_base, pill_radius, pill_height, forward, up);
for (list_index= 0; list_index<points.size; list_index++)
{
draw_line_segments(points[list_index], rgb_color);
}
return;
}
draw_cone(
cone_base, //point3d
cone_radius, //float
cone_height, //float
forward, //vector3d
up, //vector3d
rgb_color)
{
points= generate_cone_points_lists(cone_base, cone_radius, cone_height, forward, up);
for (list_index= 0; list_index<points.size; list_index++)
{
draw_line_segments(points[list_index], rgb_color);
}
return;
}
/* ---------- private code */
//int
get_debug_geometry_circular_segment_count()
{
return 16;
}
//points[][]
generate_sphere_points_list(
sphere_origin, //point3d
radius, //float
forward, //vector3d
up) //vector3d
{
points= [];
if (radius>0.0)
{
k_segment_count= get_debug_geometry_circular_segment_count();
k_rotation_delta= 360/k_segment_count;
// generate 3 circles
c0= [];
c1= [];
c2= [];
// rotate points about origin & apply translation
left= vector3d_cross_product(up, forward);
transform= matrix4x4_build_from_rotation_and_translation(forward, up, left, sphere_origin);
for (index= 0; index<=k_segment_count; index++)
{
theta= index*k_rotation_delta;
rcos_theta= radius*Cos(theta);
rsin_theta= radius*Sin(theta);
c0[index]= (rcos_theta, rsin_theta, 0.0);
c1[index]= (0.0, rcos_theta, rsin_theta);
c2[index]= (rsin_theta, 0.0, rcos_theta);
}
c0= matrix4x4_transform_points3d(c0, transform);
c1= matrix4x4_transform_points3d(c1, transform);
c2= matrix4x4_transform_points3d(c2, transform);
// return transformed points list
for (point_index= 0; point_index<c0.size; point_index++)
{
points[points.size]= c0[point_index];
if (point_index==(c0.size-1))
{
points[points.size]= c0[0];
}
else
{
points[points.size]= c0[point_index+1];
}
}
for (point_index= 0; point_index<c1.size; point_index++)
{
points[points.size]= c1[point_index];
if (point_index==(c1.size-1))
{
points[points.size]= c1[0];
}
else
{
points[points.size]= c1[point_index+1];
}
}
for (point_index= 0; point_index<c2.size; point_index++)
{
points[points.size]= c2[point_index];
if (point_index==(c2.size-1))
{
points[points.size]= c2[0];
}
else
{
points[points.size]= c2[point_index+1];
}
}
/*
list_index= 0;
points[list_index]= c0; list_index++;
points[list_index]= c1; list_index++;
points[list_index]= c2;
*/
}
return points;
}
// points[][]
generate_cylinder_points_list(
cylinder_base, //point3d
cylinder_radius, //float
cylinder_height, //float
forward, //vector3d
up) //vector3d
{
points= [];
if (cylinder_radius>0.0 && cylinder_height>0.0)
{
k_segment_count= get_debug_geometry_circular_segment_count();
k_rotation_delta= 360/k_segment_count;
// top & bottom faces
bottom= [];
top= [];
// rotate points about origin & apply translation
left= vector3d_cross_product(up, forward);
transform= matrix4x4_build_from_rotation_and_translation(forward, up, left, cylinder_base);
for (index= 0; index<=k_segment_count; index++)
{
theta= index*k_rotation_delta;
rcos_theta= cylinder_radius*Cos(theta);
rsin_theta= cylinder_radius*Sin(theta);
bottom[index]= (rcos_theta, rsin_theta, 0.0);
top[index]= (rcos_theta, rsin_theta, cylinder_height);
}
bottom= matrix4x4_transform_points3d(bottom, transform);
top= matrix4x4_transform_points3d(top, transform);
// return transformed points list
for (point_index= 0; point_index<bottom.size; point_index++)
{
// bottom circle
points[points.size]= bottom[point_index];
if (point_index==(bottom.size-1))
{
points[points.size]= bottom[0];
}
else
{
points[points.size]= bottom[point_index+1];
}
// top circle
points[points.size]= top[point_index];
if (point_index==(top.size-1))
{
points[points.size]= top[0];
}
else
{
points[points.size]= top[point_index+1];
}
// bottom->top
points[points.size]= bottom[point_index];
points[points.size]= top[point_index];
}
/*list_index= 0;
points[list_index]= bottom; list_index++;
points[list_index]= top; list_index++;
for (index= 0; index<bottom.size; index++)
{
theta= index*k_rotation_delta;
//if (theta==0 || theta==90 || theta==180 || theta==270)
{
line_segment= [];
line_segment[0]= bottom[index];
line_segment[1]= top[index];
points[list_index]= line_segment; list_index++;
}
}*/
}
return points;
}
// points[][]
generate_pill_points_lists(
pill_base, //point3d
pill_radius, //float
pill_height, //float
forward, //vector3d
up) //vector3d
{
points= [];
if (pill_radius>0.0 && pill_height>0.0)
{
k_segment_count= get_debug_geometry_circular_segment_count();
k_rotation_delta= 360/k_segment_count;
// top & bottom faces, top & bottom end-cap arcs
bottom= [];
top= [];
t0= [];
t1= [];
t2= [];
b0= [];
b1= [];
b2= [];
// rotate points about origin & apply translation
left= vector3d_cross_product(up, forward);
transform= matrix4x4_build_from_rotation_and_translation(forward, up, left, pill_base);
cap0_index= 0;
cap1_index= 0;
cap2_index= 0;
for (index= 0; index<=k_segment_count; index++)
{
theta= index*k_rotation_delta;
rcos_theta= pill_radius*Cos(theta);
rsin_theta= pill_radius*Sin(theta);
bottom[index]= (rcos_theta, rsin_theta, 0.0);
top[index]= (rcos_theta, rsin_theta, pill_height);
if (0<=theta && theta<=180)
{
b0[cap0_index]= (0.0, rcos_theta, -1.0*rsin_theta);
t0[cap0_index]= (0.0, rcos_theta, rsin_theta+pill_height);
cap0_index++;
}
if (0<=theta && theta<=90)
{
b1[cap1_index]= (rsin_theta, 0.0, -1.0*rcos_theta);
t1[cap1_index]= (rsin_theta, 0.0, rcos_theta+pill_height);
cap1_index++;
}
if (270<=theta && theta<=360)
{
b2[cap2_index]= (rsin_theta, 0.0, -1.0*rcos_theta);
t2[cap2_index]= (rsin_theta, 0.0, rcos_theta+pill_height);
cap2_index++;
}
}
bottom= matrix4x4_transform_points3d(bottom, transform);
top= matrix4x4_transform_points3d(top, transform);
t0= matrix4x4_transform_points3d(t0, transform);
t1= matrix4x4_transform_points3d(t1, transform);
t2= matrix4x4_transform_points3d(t2, transform);
b0= matrix4x4_transform_points3d(b0, transform);
b1= matrix4x4_transform_points3d(b1, transform);
b2= matrix4x4_transform_points3d(b2, transform);
// return transformed points lists
list_index= 0;
points[list_index]= bottom; list_index++;
points[list_index]= top; list_index++;
points[list_index]= b0; list_index++;
points[list_index]= b1; list_index++;
points[list_index]= b2; list_index++;
points[list_index]= t0; list_index++;
points[list_index]= t1; list_index++;
points[list_index]= t2; list_index++;
for (index= 0; index<bottom.size; index++)
{
theta= index*k_rotation_delta;
//if (theta==0 || theta==90 || theta==180 || theta==270)
{
line_segment= [];
line_segment[0]= bottom[index];
line_segment[1]= top[index];
points[list_index]= line_segment; list_index++;
}
}
}
return points;
}
// points[][]
generate_cone_points_lists(
cone_base, //point3d
cone_radius, //float
cone_height, //float
forward, //vector3d
up) //vector3d
{
points= [];
if (cone_radius>0 && cone_height>0)
{
k_segment_count= get_debug_geometry_circular_segment_count();
k_rotation_delta= 360/k_segment_count;
// bottom face
bottom= [];
// rotate points about origin & apply translation
left= vector3d_cross_product(up, forward);
transform= matrix4x4_build_from_rotation_and_translation(forward, up, left, cone_base);
for (index= 0; index<=k_segment_count; index++)
{
theta= index*k_rotation_delta;
rcos_theta= cone_radius*Cos(theta);
rsin_theta= cone_radius*Sin(theta);
bottom[index]= (rcos_theta, rsin_theta, 0.0);
}
matrix4x4_transform_points3d(bottom, transform);
// top point
top= matrix4x4_transform_point3d((0, 0, cone_height), transform);
// return transformed points lists
list_index= 0;
points[list_index]= bottom; list_index++;
for (index= 0; index<bottom.size; index++)
{
line_segment= [];
line_segment[0]= bottom[index];
line_segment[1]= top;
points[list_index+index]= line_segment;
}
}
return points;
}
// points[][]
generate_box_points_lists(
centroid, //point3d
dimensions, //vector3d
forward, //vector3d
up) //vector3d
{
points= [];
if (dimensions[0]>0 && dimensions[1]>0 && dimensions[2]>0)
{
// rotate points about origin & apply translation
left= vector3d_cross_product(up, forward);
transform= matrix4x4_build_from_rotation_and_translation(forward, up, left, centroid);
// apply transform to unit cube
half_x= dimensions[0]*0.5;
half_y= dimensions[1]*0.5;
half_z= dimensions[2]*0.5;
ftl= matrix4x4_transform_point3d((half_x, half_y, half_z), transform); // front-top-left
ftr= matrix4x4_transform_point3d((half_x, -1.0*half_y, half_z), transform); // front-top-right
fbl= matrix4x4_transform_point3d((half_x, half_y, -1*half_z), transform); // front-bottom-left
fbr= matrix4x4_transform_point3d((half_x, -1.0*half_y, -1.0*half_z), transform); // front-bottom-right
btl= matrix4x4_transform_point3d((-1*half_x, half_y, half_z), transform); // back-top-left
btr= matrix4x4_transform_point3d((-1*half_x, -1.0*half_y, half_z), transform); // back-top-right
bbl= matrix4x4_transform_point3d((-1*half_x, half_y, -1.0*half_z), transform); // back-bottom-left
bbr= matrix4x4_transform_point3d((-1*half_x, -1.0*half_y, -1*half_z), transform); // back-bottom-right
front_face= [];
back_face= [];
tl= [];
tr= [];
br= [];
bl= [];
front_face[0]= ftl; front_face[1]= ftr; front_face[2]= fbr; front_face[3]= fbl; front_face[4]= ftl;
back_face[0]= btl; back_face[1]= btr; back_face[2]= bbr; back_face[3]= bbl; back_face[4]= btl;
tl[0]= ftl; tl[1]= btl;
tr[0]= ftr; tr[1]= btr;
br[0]= fbr; br[1]= bbr;
bl[0]= fbl; bl[1]= bbl;
// return transformed points lists
list_index= 0;
points[list_index]= front_face; list_index++;
points[list_index]= back_face; list_index++;
points[list_index]= tl; list_index++;
points[list_index]= tr; list_index++;
points[list_index]= br; list_index++;
points[list_index]= bl;
}
return points;
}