mirror of
https://github.com/Cesarus85/ringfall-mjolnir-vr.git
synced 2026-10-23 01:00:30 +02:00
425 lines
12 KiB
C
425 lines
12 KiB
C
/*
|
|
COLLISION_MODELS.C
|
|
*/
|
|
|
|
/* ---------- headers */
|
|
|
|
#include "cseries.h"
|
|
#include "cseries/errors.h" /* port: error */
|
|
#include "collisions.h"
|
|
|
|
#include "collision_bsp.h"
|
|
#include "collision_bsp_definitions.h"
|
|
#include "collision_model_definitions.h"
|
|
#include "collision_models.h"
|
|
#include "collision_features.h"
|
|
#include "collision_usage.h"
|
|
|
|
#include "objects/object_definitions.h" /* port: MAXIMUM_REGIONS_PER_OBJECT */
|
|
#include "objects/object_types.h"
|
|
#include "objects/objects.h"
|
|
#include "tag_files/tag_groups.h"
|
|
|
|
/* ---------- constants */
|
|
|
|
/* ---------- macros */
|
|
|
|
/* ---------- structures */
|
|
|
|
/* ---------- prototypes */
|
|
|
|
static boolean collision_node_region_valid(
|
|
struct collision_node const *collision_node);
|
|
|
|
/* ---------- globals */
|
|
|
|
/* port: whether a map's malformed collision model was reported (once each) */
|
|
static boolean warned_about_collision_model_nodes;
|
|
static boolean warned_about_collision_model_regions;
|
|
|
|
/* ---------- public code */
|
|
|
|
boolean collision_model_instance_new(
|
|
struct collision_model_instance *instance,
|
|
long object_index)
|
|
{
|
|
boolean result = FALSE;
|
|
|
|
struct object_datum *object = object_get(object_index);
|
|
struct object_definition *object_definition = object_definition_get(object->definition_index);
|
|
|
|
if (object_definition->object.collision_model.index != NONE)
|
|
{
|
|
long node_count;
|
|
|
|
instance->object_index = object_index;
|
|
instance->model = collision_model_definition_get(object_definition->object.collision_model.index);
|
|
instance->region_permutation_indices = object->object.region_permutations;
|
|
instance->matrices = object_get_node_matrices(object_index);
|
|
|
|
/* port: a collision model (from the map) with more nodes than its
|
|
object has node matrices (its model's nodes: the retail collision
|
|
models have no more) tests only those the object has */
|
|
node_count = object->object.node_matrices.size / (long)sizeof(real_matrix4x3);
|
|
if (instance->model->nodes.count > node_count)
|
|
{
|
|
if (!warned_about_collision_model_nodes)
|
|
{
|
|
error(_error_silent, "collision model %s has %ld nodes, its object %ld",
|
|
tag_get_name(object_definition->object.collision_model.index),
|
|
instance->model->nodes.count,
|
|
node_count);
|
|
warned_about_collision_model_nodes = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
node_count = instance->model->nodes.count;
|
|
}
|
|
instance->node_count = (short)MAX(node_count, 0);
|
|
|
|
result = TRUE;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
boolean collision_model_test_sphere(
|
|
struct collision_model_instance const *instance,
|
|
real_point3d const *center,
|
|
real radius)
|
|
{
|
|
const struct collision_node *collision_node;
|
|
struct collision_bsp *collision_bsp;
|
|
short node_index;
|
|
short perm;
|
|
real_point3d transformed_center;
|
|
real_matrix4x3 inverted_matrix;
|
|
|
|
/* port: (instance->node_count, collision_node_region_valid) */
|
|
for (node_index = 0; node_index < instance->node_count; ++node_index)
|
|
{
|
|
collision_node = TAG_BLOCK_GET_ELEMENT(&instance->model->nodes, node_index, struct collision_node);
|
|
|
|
if (collision_node_region_valid(collision_node))
|
|
{
|
|
perm = instance->region_permutation_indices[collision_node->region_index];
|
|
|
|
if (perm != NONE && collision_node->bsps.count > 0)
|
|
{
|
|
perm = PIN(perm, 0, collision_node->bsps.count - 1);
|
|
|
|
collision_bsp = TAG_BLOCK_GET_ELEMENT(&collision_node->bsps, perm, struct collision_bsp);
|
|
|
|
if (collision_bsp->bsp3d.nodes.count > 0)
|
|
{
|
|
matrix4x3_inverse(&instance->matrices[node_index], &inverted_matrix);
|
|
matrix4x3_transform_point(&inverted_matrix, center, &transformed_center);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
boolean collision_model_test_point(
|
|
struct collision_model_instance const *instance,
|
|
real_point3d const *point)
|
|
{
|
|
const struct collision_node *collision_node;
|
|
struct collision_bsp *collision_bsp;
|
|
short i;
|
|
short perm;
|
|
real_point3d transformed_point;
|
|
|
|
/* port: (instance->node_count, collision_node_region_valid) */
|
|
for (i = 0; i < instance->node_count; i++)
|
|
{
|
|
collision_node = TAG_BLOCK_GET_ELEMENT(&instance->model->nodes, i, struct collision_node);
|
|
|
|
if (collision_node_region_valid(collision_node))
|
|
{
|
|
perm = instance->region_permutation_indices[collision_node->region_index];
|
|
|
|
if (perm != NONE && collision_node->bsps.count > 0)
|
|
{
|
|
perm = PIN(perm, 0, collision_node->bsps.count - 1);
|
|
|
|
collision_bsp = TAG_BLOCK_GET_ELEMENT(&collision_node->bsps, perm, struct collision_bsp);
|
|
|
|
if (collision_bsp->bsp3d.nodes.count > 0)
|
|
{
|
|
matrix4x3_inverse_transform_point(&instance->matrices[i], point, &transformed_point);
|
|
|
|
if (bsp3d_test_point(&collision_bsp->bsp3d, 0, &transformed_point) == NONE)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static LARGE_INTEGER collision_model_test_vector_time;
|
|
|
|
boolean collision_model_test_vector(
|
|
struct collision_model_instance const *instance,
|
|
unsigned long flags,
|
|
real_point3d const *point,
|
|
real_vector3d const *vector,
|
|
struct collision_model_test_vector_result *result)
|
|
{
|
|
short node_index;
|
|
boolean return_value = FALSE;
|
|
|
|
collision_log_usage(3);
|
|
collision_log_start_time(&collision_model_test_vector_time);
|
|
|
|
result->bsp_result.t = FLT_MAX;
|
|
|
|
/* port: (instance->node_count, collision_node_region_valid) */
|
|
for (node_index = 0; node_index < instance->node_count; ++node_index)
|
|
{
|
|
struct collision_node const *collision_node = TAG_BLOCK_GET_ELEMENT(&instance->model->nodes, node_index, struct collision_node);
|
|
|
|
if (collision_node_region_valid(collision_node))
|
|
{
|
|
short perm = instance->region_permutation_indices[collision_node->region_index];
|
|
|
|
if (perm != NONE && collision_node->bsps.count > 0)
|
|
{
|
|
struct collision_bsp *collision_bsp;
|
|
|
|
perm = PIN(perm, 0, collision_node->bsps.count - 1);
|
|
|
|
collision_bsp = TAG_BLOCK_GET_ELEMENT(&collision_node->bsps, perm, struct collision_bsp);
|
|
|
|
if (collision_bsp->bsp3d.nodes.count > 0)
|
|
{
|
|
real_matrix4x3 inverted_matrix;
|
|
real_point3d transformed_point;
|
|
real_vector3d transformed_vector;
|
|
|
|
matrix4x3_inverse(&instance->matrices[node_index], &inverted_matrix);
|
|
matrix4x3_transform_point(&inverted_matrix, point, &transformed_point);
|
|
matrix4x3_transform_vector(&inverted_matrix, vector, &transformed_vector);
|
|
|
|
if (collision_bsp_test_vector(flags, collision_bsp, 0, NULL, &transformed_point, &transformed_vector, result->bsp_result.t, &result->bsp_result))
|
|
{
|
|
result->node_index = node_index;
|
|
result->region_index = collision_node->region_index;
|
|
result->bsp_index = perm;
|
|
|
|
return_value = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
collision_log_end_time(3, collision_model_test_vector_time.QuadPart);
|
|
|
|
return return_value;
|
|
}
|
|
|
|
boolean collision_model_test_pill(
|
|
struct collision_model_instance const *instance,
|
|
real_point3d const *point,
|
|
real_vector3d const *vector,
|
|
real radius,
|
|
struct collision_model_test_pill_result *result)
|
|
{
|
|
const struct collision_node *collision_node;
|
|
struct collision_bsp *collision_bsp;
|
|
short i;
|
|
short perm;
|
|
real_point3d transformed_point;
|
|
real_matrix4x3 inverted_matrix;
|
|
real_vector3d transformed_vector;
|
|
boolean return_val;
|
|
real scaled_radius;
|
|
|
|
result->bsp_result.t = FLT_MAX;
|
|
|
|
return_val = FALSE;
|
|
|
|
/* port: (instance->node_count, collision_node_region_valid) */
|
|
for (i = 0; i < instance->node_count; i++)
|
|
{
|
|
collision_node = TAG_BLOCK_GET_ELEMENT(&instance->model->nodes, i, struct collision_node);
|
|
|
|
if (collision_node_region_valid(collision_node))
|
|
{
|
|
perm = instance->region_permutation_indices[collision_node->region_index];
|
|
|
|
if (perm != NONE && collision_node->bsps.count > 0)
|
|
{
|
|
perm = PIN(perm, 0, collision_node->bsps.count - 1);
|
|
|
|
collision_bsp = TAG_BLOCK_GET_ELEMENT(&collision_node->bsps, perm, struct collision_bsp);
|
|
|
|
if (collision_bsp->bsp3d.nodes.count > 0)
|
|
{
|
|
matrix4x3_inverse(&instance->matrices[i], &inverted_matrix);
|
|
matrix4x3_transform_point(&inverted_matrix, point, &transformed_point);
|
|
matrix4x3_transform_vector(&inverted_matrix, vector, &transformed_vector);
|
|
|
|
scaled_radius = inverted_matrix.scale * radius;
|
|
|
|
if (collision_bsp_test_pill(
|
|
collision_bsp,
|
|
&transformed_point,
|
|
&transformed_vector,
|
|
scaled_radius,
|
|
result->bsp_result.t,
|
|
&result->bsp_result))
|
|
{
|
|
result->node_index = i;
|
|
result->region_index = collision_node->region_index;
|
|
result->bsp_index = perm;
|
|
|
|
return_val = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return return_val;
|
|
}
|
|
|
|
boolean collision_model_get_features_in_sphere(
|
|
const struct collision_model_instance *instance,
|
|
const real_point3d *center,
|
|
real radius,
|
|
real height,
|
|
real width,
|
|
struct collision_feature_list *features)
|
|
{
|
|
const struct collision_node *collision_node;
|
|
struct collision_bsp *collision_bsp;
|
|
short i;
|
|
short perm;
|
|
real_point3d transformed_center;
|
|
real_matrix4x3 inverted_matrix;
|
|
boolean return_val;
|
|
struct collision_bsp_test_sphere_result result;
|
|
real scaled_radius;
|
|
const real_matrix4x3 *matrix;
|
|
|
|
return_val = FALSE;
|
|
|
|
/* port: (instance->node_count, collision_node_region_valid) */
|
|
for (i = 0; i < instance->node_count; i++)
|
|
{
|
|
collision_node = TAG_BLOCK_GET_ELEMENT(&instance->model->nodes, i, struct collision_node);
|
|
|
|
if (collision_node_region_valid(collision_node))
|
|
{
|
|
perm = instance->region_permutation_indices[collision_node->region_index];
|
|
|
|
if (perm != NONE && collision_node->bsps.count > 0)
|
|
{
|
|
perm = PIN(perm, 0, collision_node->bsps.count - 1);
|
|
|
|
collision_bsp = TAG_BLOCK_GET_ELEMENT(&collision_node->bsps, perm, struct collision_bsp);
|
|
|
|
if (collision_bsp->bsp3d.nodes.count > 0)
|
|
{
|
|
matrix = &instance->matrices[i];
|
|
matrix4x3_inverse(matrix, &inverted_matrix);
|
|
matrix4x3_transform_point(&inverted_matrix, center, &transformed_center);
|
|
|
|
scaled_radius = inverted_matrix.scale * radius;
|
|
|
|
if (collision_bsp_test_sphere(
|
|
collision_bsp,
|
|
0,
|
|
0,
|
|
&transformed_center,
|
|
scaled_radius,
|
|
&result))
|
|
{
|
|
collision_bsp_get_features_in_sphere(
|
|
collision_bsp,
|
|
&result,
|
|
matrix,
|
|
height,
|
|
width,
|
|
instance->object_index,
|
|
features);
|
|
|
|
return_val = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return return_val;
|
|
}
|
|
|
|
void render_debug_collision_model(
|
|
struct collision_model_instance const *instance)
|
|
{
|
|
const struct collision_node *collision_node;
|
|
struct collision_bsp *collision_bsp;
|
|
short i;
|
|
short perm;
|
|
|
|
/* port: (instance->node_count, collision_node_region_valid) */
|
|
for (i = 0; i < instance->node_count; i++)
|
|
{
|
|
collision_node = TAG_BLOCK_GET_ELEMENT(&instance->model->nodes, i, struct collision_node);
|
|
|
|
if (collision_node_region_valid(collision_node))
|
|
{
|
|
perm = instance->region_permutation_indices[collision_node->region_index];
|
|
|
|
if (perm != NONE && collision_node->bsps.count > 0)
|
|
{
|
|
perm = PIN(perm, 0, collision_node->bsps.count - 1);
|
|
|
|
collision_bsp = TAG_BLOCK_GET_ELEMENT(&collision_node->bsps, perm, struct collision_bsp);
|
|
|
|
if (collision_bsp->surfaces.count > 0 && collision_bsp->edges.count > 0 && collision_bsp->vertices.count > 0)
|
|
{
|
|
render_debug_collision_bsp(collision_bsp, &instance->matrices[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ---------- private code */
|
|
|
|
/* port: whether a collision node is tested: one of no region (NONE) is
|
|
not, as before, nor is one whose region (from the map) is none of an
|
|
object's MAXIMUM_REGIONS_PER_OBJECT (the retail ones are at most 4) */
|
|
static boolean collision_node_region_valid(
|
|
struct collision_node const *collision_node)
|
|
{
|
|
if (collision_node->region_index == NONE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
if (collision_node->region_index < 0 ||
|
|
collision_node->region_index >= MAXIMUM_REGIONS_PER_OBJECT)
|
|
{
|
|
if (!warned_about_collision_model_regions)
|
|
{
|
|
error(_error_silent, "a collision node's region #%d is not one of an object's %d",
|
|
collision_node->region_index,
|
|
MAXIMUM_REGIONS_PER_OBJECT);
|
|
warned_about_collision_model_regions = TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|