Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Debug colliders #566

Merged
merged 7 commits into from
Aug 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 27 additions & 11 deletions apps/game/src/input.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,9 +267,9 @@ static void update_camera_movement_debug(
static SceneQueryFilter select_filter(InputManagerComp* input) {
if (input_layer_active(input, string_hash_lit("Debug"))) {
/**
* Allow selecting all objects in debug mode.
* Allow selecting all objects (including debug shapes) in debug mode.
*/
return (SceneQueryFilter){.layerMask = SceneLayer_All};
return (SceneQueryFilter){.layerMask = SceneLayer_AllIncludingDebug};
}
/**
* Only allow selecting your own units.
Expand Down Expand Up @@ -551,15 +551,29 @@ static void input_state_init(EcsWorld* world, const EcsEntityId windowEntity) {
.uiCanvas = ui_canvas_create(world, windowEntity, UiCanvasCreateFlags_ToBack));
}

/**
* Update the global collision mask to include debug colliders when we have the debug input active.
* This allows us to use the debug colliders to select entities that have no collider.
*/
static void input_update_collision_mask(SceneCollisionEnvComp* env, const InputManagerComp* input) {
SceneLayer ignoreMask = scene_collision_ignore_mask(env);
if (input_layer_active(input, string_hash_lit("Debug"))) {
ignoreMask &= ~SceneLayer_Debug; // Include debug layer.
} else {
ignoreMask |= SceneLayer_Debug; // Ignore debug layer;
}
scene_collision_ignore_mask_set(env, ignoreMask);
}

ecs_view_define(GlobalUpdateView) {
ecs_access_maybe_read(SceneTerrainComp);
ecs_access_maybe_write(DebugStatsGlobalComp);
ecs_access_read(SceneCollisionEnvComp);
ecs_access_read(SceneNavEnvComp);
ecs_access_read(SceneSelectionComp);
ecs_access_read(SceneTimeComp);
ecs_access_write(CmdControllerComp);
ecs_access_write(InputManagerComp);
ecs_access_write(SceneCollisionEnvComp);
}

ecs_view_define(CameraView) {
Expand All @@ -574,14 +588,16 @@ ecs_system_define(InputUpdateSys) {
if (!globalItr) {
return;
}
CmdControllerComp* cmdController = ecs_view_write_t(globalItr, CmdControllerComp);
const SceneCollisionEnvComp* colEnv = ecs_view_read_t(globalItr, SceneCollisionEnvComp);
const SceneSelectionComp* sel = ecs_view_read_t(globalItr, SceneSelectionComp);
const SceneTerrainComp* terrain = ecs_view_read_t(globalItr, SceneTerrainComp);
const SceneNavEnvComp* nav = ecs_view_read_t(globalItr, SceneNavEnvComp);
const SceneTimeComp* time = ecs_view_read_t(globalItr, SceneTimeComp);
InputManagerComp* input = ecs_view_write_t(globalItr, InputManagerComp);
DebugStatsGlobalComp* debugStats = ecs_view_write_t(globalItr, DebugStatsGlobalComp);
CmdControllerComp* cmdController = ecs_view_write_t(globalItr, CmdControllerComp);
const SceneNavEnvComp* nav = ecs_view_read_t(globalItr, SceneNavEnvComp);
const SceneSelectionComp* sel = ecs_view_read_t(globalItr, SceneSelectionComp);
const SceneTerrainComp* terrain = ecs_view_read_t(globalItr, SceneTerrainComp);
const SceneTimeComp* time = ecs_view_read_t(globalItr, SceneTimeComp);
DebugStatsGlobalComp* debugStats = ecs_view_write_t(globalItr, DebugStatsGlobalComp);
InputManagerComp* input = ecs_view_write_t(globalItr, InputManagerComp);
SceneCollisionEnvComp* colEnv = ecs_view_write_t(globalItr, SceneCollisionEnvComp);

input_update_collision_mask(colEnv, input);

if (input_triggered_lit(input, "OrderStop")) {
input_order_stop(cmdController, sel, debugStats);
Expand Down
2 changes: 1 addition & 1 deletion libs/debug/src/camera.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ static void debug_camera_draw_input_ray(
debug_line(shape, start, end, geo_color_fuchsia);

SceneRayHit hit;
const SceneQueryFilter filter = {.layerMask = SceneLayer_All};
const SceneQueryFilter filter = {.layerMask = SceneLayer_AllNonDebug};
const f32 maxDist = 1e5f;
if (scene_query_ray(collisionEnv, &ray, maxDist, &filter, &hit)) {
debug_sphere(shape, hit.position, 0.04f, geo_color_lime, DebugShape_Overlay);
Expand Down
2 changes: 1 addition & 1 deletion libs/geo/include/geo_box.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ GeoBox geo_box_dilate(const GeoBox*, GeoVector size);
/**
* Retrieve the 8 corners of the 3d box.
*/
void geo_box_corners3(const GeoBox*, GeoVector corners[8]);
void geo_box_corners3(const GeoBox*, GeoVector corners[PARAM_ARRAY_SIZE(8)]);

/**
* Construct a transformed 3d box.
Expand Down
1 change: 1 addition & 0 deletions libs/geo/include/geo_sphere.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ f32 geo_sphere_intersect_ray(const GeoSphere*, const GeoRay*);
*/
bool geo_sphere_overlap(const GeoSphere*, const GeoSphere*);
bool geo_sphere_overlap_box(const GeoSphere*, const GeoBox*);
bool geo_sphere_overlap_frustum(const GeoSphere*, const GeoVector[PARAM_ARRAY_SIZE(8)]);
2 changes: 1 addition & 1 deletion libs/geo/src/box.c
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ GeoBox geo_box_dilate(const GeoBox* b, const GeoVector size) {
#endif
}

void geo_box_corners3(const GeoBox* box, GeoVector corners[8]) {
void geo_box_corners3(const GeoBox* box, GeoVector corners[PARAM_ARRAY_SIZE(8)]) {
corners[0] = geo_vector(box->min.x, box->min.y, box->min.z);
corners[1] = geo_vector(box->min.x, box->min.y, box->max.z);
corners[2] = geo_vector(box->max.x, box->min.y, box->min.z);
Expand Down
22 changes: 22 additions & 0 deletions libs/geo/src/plane.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
#include "core_math.h"
#include "geo_plane.h"

#define geo_plane_simd_enable 1

#if geo_plane_simd_enable
#include "core_simd.h"
#endif

static void assert_normalized(const GeoVector v) {
MAYBE_UNUSED const f32 sqrMag = geo_vector_mag_sqr(v);
diag_assert_msg(math_abs(sqrMag - 1) < 1e-4, "Given vector is not normalized");
Expand All @@ -13,10 +19,26 @@ GeoPlane geo_plane_at(const GeoVector normal, const GeoVector position) {
}

GeoPlane geo_plane_at_triangle(const GeoVector a, const GeoVector b, const GeoVector c) {
#if geo_plane_simd_enable
const SimdVec aVec = simd_vec_load(a.comps);
const SimdVec bVec = simd_vec_load(b.comps);
const SimdVec cVec = simd_vec_load(c.comps);
const SimdVec toB = simd_vec_sub(bVec, aVec);
const SimdVec toC = simd_vec_sub(cVec, aVec);
const SimdVec cross = simd_vec_cross3(toB, toC);
const SimdVec crossMag = simd_vec_sqrt(simd_vec_dot4(cross, cross));
const SimdVec normal = simd_vec_div(cross, crossMag);

GeoPlane res;
simd_vec_store(normal, res.normal.comps);
res.distance = simd_vec_x(simd_vec_dot3(normal, aVec));
return res;
#else
const GeoVector toB = geo_vector_sub(b, a);
const GeoVector toC = geo_vector_sub(c, a);
const GeoVector normal = geo_vector_norm(geo_vector_cross3(toB, toC));
return (GeoPlane){.normal = normal, .distance = geo_vector_dot(normal, a)};
#endif
}

GeoVector geo_plane_position(const GeoPlane* plane) {
Expand Down
7 changes: 1 addition & 6 deletions libs/geo/src/query.c
Original file line number Diff line number Diff line change
Expand Up @@ -190,12 +190,7 @@ static bool geo_prim_overlap_frustum(
switch (prim->type) {
case GeoQueryPrim_Sphere: {
const GeoSphere* sphere = &((const GeoSphere*)prim->shapes)[entryIdx];
// TODO: Implement sphere <-> frustum overlap instead of converting spheres to boxes.
const GeoBoxRotated box = {
.box = geo_box_from_sphere(sphere->point, sphere->radius),
.rotation = geo_quat_ident,
};
return geo_box_rotated_overlap_frustum(&box, frustum);
return geo_sphere_overlap_frustum(sphere, frustum);
}
case GeoQueryPrim_Capsule: {
const GeoCapsule* cap = &((const GeoCapsule*)prim->shapes)[entryIdx];
Expand Down
19 changes: 19 additions & 0 deletions libs/geo/src/sphere.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include "core_array.h"
#include "core_float.h"
#include "core_intrinsic.h"
#include "core_math.h"
Expand Down Expand Up @@ -45,3 +46,21 @@ bool geo_sphere_overlap_box(const GeoSphere* sphere, const GeoBox* box) {
const f32 distSqr = geo_vector_mag_sqr(geo_vector_sub(closest, sphere->point));
return distSqr <= (sphere->radius * sphere->radius);
}

bool geo_sphere_overlap_frustum(
const GeoSphere* sphere, const GeoVector frustum[PARAM_ARRAY_SIZE(8)]) {
const GeoPlane frustumPlanes[] = {
geo_plane_at_triangle(frustum[3], frustum[6], frustum[2]), // Right.
geo_plane_at_triangle(frustum[1], frustum[4], frustum[0]), // Left.
geo_plane_at_triangle(frustum[2], frustum[5], frustum[1]), // Up.
geo_plane_at_triangle(frustum[4], frustum[7], frustum[0]), // Down.
geo_plane_at_triangle(frustum[4], frustum[5], frustum[6]), // Back.
geo_plane_at_triangle(frustum[2], frustum[1], frustum[0]), // Front.
};
array_for_t(frustumPlanes, GeoPlane, plane) {
if ((geo_vector_dot(sphere->point, plane->normal) - plane->distance) < -sphere->radius) {
return false;
}
}
return true;
}
13 changes: 10 additions & 3 deletions libs/scene/include/scene_collision.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,10 @@ typedef enum {
SceneLayer_UnitFactionC = SceneLayer_InfantryFactionC | SceneLayer_StructureFactionC,
SceneLayer_UnitFactionD = SceneLayer_InfantryFactionD | SceneLayer_StructureFactionD,

SceneLayer_Count = 13,
SceneLayer_None = 0,
SceneLayer_All = ~0,
SceneLayer_Count = 13,
SceneLayer_None = 0,
SceneLayer_AllIncludingDebug = ~0,
SceneLayer_AllNonDebug = ~SceneLayer_Debug,
} SceneLayer;

/**
Expand Down Expand Up @@ -119,6 +120,12 @@ String scene_layer_name(SceneLayer);
*/
String scene_collision_type_name(SceneCollisionType);

/**
* Set a mask to ignore colliders on specific layers globally.
*/
SceneLayer scene_collision_ignore_mask(const SceneCollisionEnvComp*);
void scene_collision_ignore_mask_set(SceneCollisionEnvComp*, SceneLayer);

/**
* Add a collision shape to the given entity.
*/
Expand Down
50 changes: 43 additions & 7 deletions libs/scene/src/collision.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ ASSERT(sizeof(EcsEntityId) == sizeof(u64), "EntityId's have to be interpretable
ASSERT(geo_query_max_hits == scene_query_max_hits, "Mismatching maximum query hits");
ASSERT(scene_query_stat_count == GeoQueryStat_Count, "Mismatching collision query stat count");

ecs_comp_define(SceneCollisionEnvComp) { GeoQueryEnv* queryEnv; };
ecs_comp_define(SceneCollisionEnvComp) {
SceneLayer ignoreMask; // Layers to ignore globally.
GeoQueryEnv* queryEnv;
};
ecs_comp_define_public(SceneCollisionStatsComp);
ecs_comp_define_public(SceneCollisionComp);

Expand All @@ -24,12 +27,14 @@ static void ecs_destruct_collision_env_comp(void* data) {

ecs_view_define(UpdateGlobalView) { ecs_access_write(SceneCollisionEnvComp); }

ecs_view_define(CollisionEntityView) {
ecs_view_define(CollisionView) {
ecs_access_read(SceneCollisionComp);
ecs_access_maybe_read(SceneTransformComp);
ecs_access_read(SceneTransformComp);
ecs_access_maybe_read(SceneScaleComp);
}

ecs_view_define(TransformView) { ecs_access_read(SceneTransformComp); }

static void collision_env_create(EcsWorld* world) {
GeoQueryEnv* queryEnv = geo_query_env_create(g_alloc_heap);

Expand Down Expand Up @@ -58,16 +63,24 @@ ecs_system_define(SceneCollisionUpdateSys) {
return;
}

EcsView* collisionView = ecs_world_view_t(world, CollisionView);
EcsView* transformView = ecs_world_view_t(world, TransformView);

SceneCollisionEnvComp* env = ecs_view_write_t(globalItr, SceneCollisionEnvComp);
geo_query_env_clear(env->queryEnv);

EcsView* collisionEntities = ecs_world_view_t(world, CollisionEntityView);
for (EcsIterator* itr = ecs_view_itr(collisionEntities); ecs_view_walk(itr);) {
/**
* Insert geo shapes for all colliders.
*/
for (EcsIterator* itr = ecs_view_itr(collisionView); ecs_view_walk(itr);) {
const SceneCollisionComp* collision = ecs_view_read_t(itr, SceneCollisionComp);
const SceneTransformComp* trans = ecs_view_read_t(itr, SceneTransformComp);
const SceneScaleComp* scale = ecs_view_read_t(itr, SceneScaleComp);

diag_assert_msg(collision->layer, "SceneCollision needs at least one layer");
if (collision->layer & env->ignoreMask) {
continue;
}

const u64 id = (u64)ecs_view_entity(itr);
const GeoQueryLayer queryLayer = (GeoQueryLayer)collision->layer;
Expand All @@ -94,6 +107,19 @@ ecs_system_define(SceneCollisionUpdateSys) {
diag_crash();
}
}

/**
* Insert a debug sphere shape for all entities with a transform.
* The debug shapes are useful to be able to select entities without a collider.
*/
if (!(env->ignoreMask & SceneLayer_Debug)) {
for (EcsIterator* itr = ecs_view_itr(transformView); ecs_view_walk(itr);) {
const SceneTransformComp* trans = ecs_view_read_t(itr, SceneTransformComp);
const GeoSphere sphere = {.point = trans->position, .radius = 0.25f};
const u64 id = (u64)ecs_view_entity(itr);
geo_query_insert_sphere(env->queryEnv, sphere, id, (GeoQueryLayer)SceneLayer_Debug);
}
}
}

ecs_view_define(UpdateStatsGlobalView) {
Expand All @@ -119,10 +145,14 @@ ecs_module_init(scene_collision_module) {
ecs_register_comp(SceneCollisionComp);

ecs_register_view(UpdateGlobalView);
ecs_register_view(CollisionEntityView);
ecs_register_view(CollisionView);
ecs_register_view(TransformView);

ecs_register_system(
SceneCollisionUpdateSys, ecs_view_id(UpdateGlobalView), ecs_view_id(CollisionEntityView));
SceneCollisionUpdateSys,
ecs_view_id(UpdateGlobalView),
ecs_view_id(CollisionView),
ecs_view_id(TransformView));

ecs_order(SceneCollisionUpdateSys, SceneOrder_CollisionUpdate);

Expand Down Expand Up @@ -168,6 +198,12 @@ String scene_collision_type_name(const SceneCollisionType type) {
return g_names[type];
}

SceneLayer scene_collision_ignore_mask(const SceneCollisionEnvComp* env) { return env->ignoreMask; }

void scene_collision_ignore_mask_set(SceneCollisionEnvComp* env, const SceneLayer mask) {
env->ignoreMask = mask;
}

void scene_collision_add_sphere(
EcsWorld* world,
const EcsEntityId entity,
Expand Down