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',