From f53ad7d557083c3be8275e41ca84ec63a861d4fb Mon Sep 17 00:00:00 2001 From: kichikuou Date: Wed, 28 Aug 2024 08:33:56 +0900 Subject: [PATCH] Initial implementation of TapirEngine.CalcInstance2DDetection This is used for collision detection between a map object and the player character. The current implementation is very simple, only performing AABB intersection tests, but it is enough to allow the player to walk around the first dungeon in Rance Quest. --- CMakeLists.txt | 1 + include/reign.h | 1 + src/3d/3d_internal.h | 17 ++++++++++ src/3d/collision.c | 72 +++++++++++++++++++++++++++++++++++++++++++ src/3d/model.c | 7 +++++ src/3d/reign.c | 15 +++++++++ src/hll/ReignEngine.c | 9 ++++-- src/meson.build | 1 + 8 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 src/3d/collision.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 6454540..2c98aa4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,6 +81,7 @@ target_sources(xsystem4 PRIVATE src/video.c src/vm.c + src/3d/collision.c src/3d/debug.c src/3d/model.c src/3d/parser.c diff --git a/include/reign.h b/include/reign.h index a59de14..13cbaa9 100644 --- a/include/reign.h +++ b/include/reign.h @@ -212,6 +212,7 @@ bool RE_instance_set_vertex_pos(struct RE_instance *instance, int index, float x int RE_instance_get_bone_index(struct RE_instance *instance, const char *name); bool RE_instance_trans_local_pos_to_world_pos_by_bone(struct RE_instance *instance, int bone, vec3 offset, vec3 out); float RE_instance_calc_height(struct RE_instance *instance, float x, float z); +bool RE_instance_calc_2d_detection(struct RE_instance *instance, float x0, float y0, float z0, float x1, float y1, float z1, float *x2, float *y2, float *z2, float radius); bool RE_instance_set_debug_draw_shadow_volume(struct RE_instance *instance, bool draw); int RE_motion_get_state(struct motion *motion); diff --git a/src/3d/3d_internal.h b/src/3d/3d_internal.h index e617610..7a90dda 100644 --- a/src/3d/3d_internal.h +++ b/src/3d/3d_internal.h @@ -44,6 +44,7 @@ struct model { struct hash_table *bone_map; // bone id in POL/MOT -> struct bone * struct hash_table *bone_name_map; // bone name -> (struct bone * | NULL) struct hash_table *mot_cache; // name -> struct mot * + struct collider *collider; vec3 aabb[2]; // axis-aligned bounding box bool has_transparent_material; }; @@ -509,4 +510,20 @@ struct amt *amt_parse(uint8_t *data, size_t size); void amt_free(struct amt *amt); struct amt_material *amt_find_material(struct amt *amt, const char *name); +// collision.c + +struct collider { + struct collider_triangle *triangles; + uint32_t nr_triangles; +}; + +struct collider_triangle { + vec3 vertices[3]; + vec3 aabb[2]; +}; + +struct collider *collider_create(struct pol *pol); +void collider_free(struct collider *collider); +bool check_collision(struct collider *collider, vec3 p0, vec3 p1, float radius, vec3 out); + #endif /* SYSTEM4_3D_3D_INTERNAL_H */ diff --git a/src/3d/collision.c b/src/3d/collision.c new file mode 100644 index 0000000..5443ef2 --- /dev/null +++ b/src/3d/collision.c @@ -0,0 +1,72 @@ +/* Copyright (C) 2024 kichikuou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include + +#include "system4.h" + +#include "3d_internal.h" + +struct collider *collider_create(struct pol *pol) +{ + struct collider *collider = xcalloc(1, sizeof(struct collider)); + + int nr_triangles = 0; + for (int i = 0; i < pol->nr_meshes; i++) { + nr_triangles += pol->meshes[i]->nr_triangles; + } + + collider->nr_triangles = nr_triangles; + collider->triangles = xcalloc(nr_triangles, sizeof(struct collider_triangle)); + struct collider_triangle *t = collider->triangles; + for (int mesh_i = 0; mesh_i < pol->nr_meshes; mesh_i++) { + struct pol_mesh *mesh = pol->meshes[mesh_i]; + for (int tri_i = 0; tri_i < mesh->nr_triangles; tri_i++) { + glm_aabb_invalidate(t->aabb); + for (int i = 0; i < 3; i++) { + glm_vec3_copy(mesh->vertices[mesh->triangles[tri_i].vert_index[i]].pos, t->vertices[i]); + glm_vec3_minv(t->vertices[i], t->aabb[0], t->aabb[0]); + glm_vec3_maxv(t->vertices[i], t->aabb[1], t->aabb[1]); + } + t++; + } + } + + return collider; +} + +void collider_free(struct collider *collider) +{ + free(collider->triangles); + free(collider); +} + +// When a sphere of radius `radius` moves from `p0` to `p1`, return the point +// where it collides with `collider` in `out`. If no collision is detected, +// return `p1` in `out`. +bool check_collision(struct collider *collider, vec3 p0, vec3 p1, float radius, vec3 out) +{ + // XXX: Very rough collision detection; just check if p1 is inside any triangle's AABB. + for (int i = 0; i < collider->nr_triangles; i++) { + if (glm_aabb_point(collider->triangles[i].aabb, p1)) { + glm_vec3_copy(p0, out); + return true; + } + } + glm_vec3_copy(p1, out); + return true; +} diff --git a/src/3d/model.c b/src/3d/model.c index fb63fb6..46c4c7a 100644 --- a/src/3d/model.c +++ b/src/3d/model.c @@ -474,6 +474,10 @@ struct model *model_load(struct archive *aar, const char *path) } } + // Collision detection is only required for maps. + if (strstr(path, "Map\\")) + model->collider = collider_create(pol); + pol_compute_aabb(pol, model->aabb); free(material_offsets); @@ -485,6 +489,9 @@ struct model *model_load(struct archive *aar, const char *path) void model_free(struct model *model) { + if (model->collider) + collider_free(model->collider); + for (int i = 0; i < model->nr_meshes; i++) destroy_mesh(&model->meshes[i]); free(model->meshes); diff --git a/src/3d/reign.c b/src/3d/reign.c index e0e6edd..7a953d3 100644 --- a/src/3d/reign.c +++ b/src/3d/reign.c @@ -549,6 +549,21 @@ float RE_instance_calc_height(struct RE_instance *instance, float x, float z) return cnt ? total / cnt : 0.0f; } +bool RE_instance_calc_2d_detection(struct RE_instance *instance, float x0, float y0, float z0, float x1, float y1, float z1, float *x2, float *y2, float *z2, float radius) +{ + if (!instance || !instance->model->collider) + return false; + vec3 p0 = { x0, y0, -z0 }; + vec3 p1 = { x1, y1, -z1 }; + vec3 p2; + if (!check_collision(instance->model->collider, p0, p1, radius, p2)) + return false; + *x2 = p2[0]; + *y2 = p2[1]; + *z2 = -p2[2]; + return true; +} + bool RE_instance_set_debug_draw_shadow_volume(struct RE_instance *inst, bool draw) { if (!inst) diff --git a/src/hll/ReignEngine.c b/src/hll/ReignEngine.c index 0e4243a..4f93a35 100644 --- a/src/hll/ReignEngine.c +++ b/src/hll/ReignEngine.c @@ -1826,7 +1826,12 @@ static int TapirEngine_CreatePlugin(void) HLL_WARN_UNIMPLEMENTED(false, bool, TapirEngine, SetInstanceDrawParam, int plugin_number, int instance_number, int draw_param, int value); //bool TapirEngine_GetInstanceDrawParam(int PluginNumber, int InstanceNumber, int DrawParam, int *Value); HLL_WARN_UNIMPLEMENTED(0.0f, float, TapirEngine, CalcInstance2DDetectionHeight, int plugin_number, int instance_number, float x, float z); -//bool TapirEngine_CalcInstance2DDetection(int PluginNumber, int InstanceNumber, float X0, float Y0, float Z0, float X1, float Y1, float Z1, float *X2, float *Y2, float *Z2, float Radius); + +static bool TapirEngine_CalcInstance2DDetection(int plugin, int instance, float x0, float y0, float z0, float x1, float y1, float z1, float *x2, float *y2, float *z2, float radius) +{ + return RE_instance_calc_2d_detection(get_instance(plugin, instance), x0, y0, z0, x1, y1, z1, x2, y2, z2, radius); +} + //bool TapirEngine_FindInstancePath(int PluginNumber, int InstanceNumber, float StartX, float StartY, float StartZ, float GoalX, float GoalY, float GoalZ); //bool TapirEngine_CalcPathFinderIntersectEyeVec(int nPlugin, int nInstance, int nMouseX, int nMouseY, float *pfX, float *pfY, float *pfZ); //bool TapirEngine_OptimizeInstancePathLine(int PluginNumber, int InstanceNumber); @@ -2238,7 +2243,7 @@ HLL_LIBRARY(ReignEngine, REIGN_EXPORTS, HLL_EXPORT(SetInstanceDrawParam, TapirEngine_SetInstanceDrawParam), \ HLL_TODO_EXPORT(GetInstanceDrawParam, TapirEngine_GetInstanceDrawParam), \ HLL_EXPORT(CalcInstance2DDetectionHeight, TapirEngine_CalcInstance2DDetectionHeight), \ - HLL_TODO_EXPORT(CalcInstance2DDetection, TapirEngine_CalcInstance2DDetection), \ + HLL_EXPORT(CalcInstance2DDetection, TapirEngine_CalcInstance2DDetection), \ HLL_TODO_EXPORT(FindInstancePath, TapirEngine_FindInstancePath), \ HLL_TODO_EXPORT(CalcPathFinderIntersectEyeVec, TapirEngine_CalcPathFinderIntersectEyeVec), \ HLL_TODO_EXPORT(OptimizeInstancePathLine, TapirEngine_OptimizeInstancePathLine), \ diff --git a/src/meson.build b/src/meson.build index 55f371f..88d3648 100644 --- a/src/meson.build +++ b/src/meson.build @@ -35,6 +35,7 @@ xsystem4 = [version_h, 'video.c', 'vm.c', + '3d/collision.c', '3d/debug.c', '3d/model.c', '3d/parser.c',