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.
This commit is contained in:
kichikuou
2024-09-16 15:27:11 +09:00
parent ffe4d0d4ae
commit f53ad7d557
8 changed files with 121 additions and 2 deletions
+1
View File
@@ -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
+1
View File
@@ -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);
+17
View File
@@ -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 */
+72
View File
@@ -0,0 +1,72 @@
/* Copyright (C) 2024 kichikuou <KichikuouChrome@gmail.com>
*
* 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 <http://gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <cglm/cglm.h>
#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;
}
+7
View File
@@ -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);
+15
View File
@@ -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)
+7 -2
View File
@@ -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), \
+1
View File
@@ -35,6 +35,7 @@ xsystem4 = [version_h,
'video.c',
'vm.c',
'3d/collision.c',
'3d/debug.c',
'3d/model.c',
'3d/parser.c',