diff options
author | nathansmithsmith <thenathansmithsmith@gmail.com> | 2023-07-21 13:48:02 -0600 |
---|---|---|
committer | nathansmithsmith <thenathansmithsmith@gmail.com> | 2023-07-21 13:48:02 -0600 |
commit | a408b352f13df546b2262ca01cf162b60891cdae (patch) | |
tree | 6a7f7053efbf01c65cb82df4478721c3a818f07a /src | |
parent | d4b40dcf7589bef2bbd0b6b940ee992da9db2343 (diff) |
Sonic fast collision
Diffstat (limited to 'src')
-rw-r--r-- | src/entities/antifaShip.c | 9 | ||||
-rw-r--r-- | src/entities/soldato.c | 9 | ||||
-rw-r--r-- | src/entity.c | 240 | ||||
-rw-r--r-- | src/entity.h | 33 | ||||
-rw-r--r-- | src/gameCommon.h | 2 | ||||
-rw-r--r-- | src/util.c | 12 | ||||
-rw-r--r-- | src/util.h | 10 | ||||
-rw-r--r-- | src/world.c | 2 |
8 files changed, 251 insertions, 66 deletions
diff --git a/src/entities/antifaShip.c b/src/entities/antifaShip.c index 6fe4b66..94fc8c8 100644 --- a/src/entities/antifaShip.c +++ b/src/entities/antifaShip.c @@ -5,6 +5,8 @@ void initAntifaShip(Entity * entity, Game * game) { entity->model = &game->assets.models[ANTIFA_SHIP_ASSET]; + entity->collisionModel = entityCreateCollisionModel(*entity->model); + entity->transformedCollisionModel = entityCreateCollisionModel(*entity->model); setEntityRadius(entity); // Acceleration stuff. @@ -31,6 +33,9 @@ void initAntifaShip(Entity * entity, Game * game) { void closeAntifaShip(Entity * entity) { if (entity->data != NULL) KF_FREE(entity->data); + + entityFreeCollisionModel(entity->collisionModel); + entityFreeCollisionModel(entity->transformedCollisionModel); } void controlAntifaShipJoystick(Game * game, Entity * entity) { @@ -102,6 +107,8 @@ void controlAntifaShipKeyboardAndMouse(Game * game, Entity * entity) { } void updateAntifaShip(Game * game, Entity * entity) { + entityUpdateLastValues(entity); + switch (game->settings.controlMode) { case JOYSTICK_CONTROL: controlAntifaShipJoystick(game, entity); @@ -112,6 +119,8 @@ void updateAntifaShip(Game * game, Entity * entity) { default: break; } + + entityCheckTransformedCollisionModel(entity); } void drawAntifaShip(Game * game, Entity * entity) { diff --git a/src/entities/soldato.c b/src/entities/soldato.c index 74544aa..2cc0e07 100644 --- a/src/entities/soldato.c +++ b/src/entities/soldato.c @@ -3,6 +3,8 @@ void initSoldato(Entity * entity, Game * game) { entity->model = &game->assets.models[SOLDATO_ASSET]; + entity->collisionModel = entityCreateCollisionModel(*entity->model); + entity->transformedCollisionModel = entityCreateCollisionModel(*entity->model); setEntityRadius(entity); // Acceleration. @@ -46,9 +48,14 @@ void initSoldato(Entity * entity, Game * game) { void closeSoldato(Entity * entity) { if (entity->data != NULL) KF_FREE(entity->data); + + entityFreeCollisionModel(entity->collisionModel); + entityFreeCollisionModel(entity->transformedCollisionModel); } void updateSoldato(Game * game, Entity * entity) { + entityUpdateLastValues(entity); + Entity * player = getEntityFromWorld(game->world, 0); Soldato * data = (Soldato*)entity->data; @@ -57,6 +64,8 @@ void updateSoldato(Game * game, Entity * entity) { player->position, &data->flyToPoint ); + + entityCheckTransformedCollisionModel(entity); } void drawSoldato(Game * game, Entity * entity) { diff --git a/src/entity.c b/src/entity.c index e65f5d4..e5afbcd 100644 --- a/src/entity.c +++ b/src/entity.c @@ -54,8 +54,13 @@ Entity createEntity(EntityType type, Game * game) { .type = type, .model = NULL, .radius = 0.0, + .collisionModel = (EntityCollisionModel){0, NULL}, + .transformedCollisionModel = (EntityCollisionModel){0, NULL}, + .collisionModelTransformed = false, .position = Vector3Zero(), .rotation = QuaternionIdentity(), + .lastPosition = Vector3Zero(), + .lastRotation = QuaternionIdentity(), .velocity = entityVelocityIdentity(), .lastVelocity = entityVelocityIdentity(), .useAcceleration = false, @@ -102,7 +107,7 @@ void setEntityRadius(Entity * entity) { } // Little triangle helper for checkEntityMeshCollision. -void getTriangleFromMeshAndTransform(int num, Mesh mesh, Vector3 * triangle, Quaternion rotation, Vector3 position) { +void getTriangleFromMeshAndTransform(int num, Mesh mesh, Triangle3D triangle, Quaternion rotation, Vector3 position) { int i; int triangleLocation = num * 9; int vertexLocation; @@ -123,65 +128,178 @@ void getTriangleFromMeshAndTransform(int num, Mesh mesh, Vector3 * triangle, Qua } } +// Little normals helper for checkEntityMeshCollision. num is not triangle number for fast fast reasons. +Vector3 getNormalsFromMeshAndTransform(int num, Mesh mesh, Quaternion rotation) { + // Get normals. + Vector3 normals = (Vector3){ + mesh.normals[num], + mesh.normals[num + 1], + mesh.normals[num + 2] + }; + + // Transform. + normals = Vector3RotateByQuaternion(normals, rotation); + + return normals; +} + +EntityCollisionMesh createCollisionMesh(Mesh mesh) { + int i, j; + EntityCollisionMesh collisionMesh; + collisionMesh.triangleCount = mesh.triangleCount; + + // Allocate. + collisionMesh.triangles = (Triangle3D*)KF_CALLOC(collisionMesh.triangleCount, sizeof(Triangle3D)); + collisionMesh.normals = (Vector3*)KF_CALLOC(collisionMesh.triangleCount, sizeof(Vector3)); + + if (collisionMesh.triangles == NULL || collisionMesh.normals == NULL) { + ALLOCATION_ERROR; + return (EntityCollisionMesh){0, NULL, NULL}; + } + + int triangleLocation; + int vertexLocation; + int normalLocation; + + // Copy triangles and normals over. + for (i = 0; i < collisionMesh.triangleCount; ++i) { + triangleLocation = i * 9; + normalLocation = i * 3; + + // Get triangle. + for (j = 0; j < 3; ++j) { + vertexLocation = triangleLocation + (j * 3); + + collisionMesh.triangles[i][j] = (Vector3){ + mesh.vertices[vertexLocation], + mesh.vertices[vertexLocation + 1], + mesh.vertices[vertexLocation + 2] + }; + } + + // Get normal. + collisionMesh.normals[i] = (Vector3){ + mesh.normals[normalLocation], + mesh.normals[normalLocation + 1], + mesh.normals[normalLocation + 2], + }; + } + + return collisionMesh; +} + +void freeCollisionMesh(EntityCollisionMesh mesh) { + if (mesh.triangles == NULL) + return; + + KF_FREE(mesh.triangles); + KF_FREE(mesh.normals); +} + +EntityCollisionModel entityCreateCollisionModel(Model model) { + int i; + EntityCollisionModel collisionModel; + collisionModel.meshCount = model.meshCount; + + // Allocate. + collisionModel.meshes = (EntityCollisionMesh*)KF_CALLOC(collisionModel.meshCount, sizeof(EntityCollisionMesh)); + + if (collisionModel.meshes == NULL) { + ALLOCATION_ERROR; + return (EntityCollisionModel){0, NULL}; + } + + // Create meshes. + for (i = 0; i < collisionModel.meshCount; ++i) + collisionModel.meshes[i] = createCollisionMesh(model.meshes[i]); + + return collisionModel; +} + +void entityFreeCollisionModel(EntityCollisionModel model) { + int i; + + if (model.meshes == NULL) + return; + + for (i = 0; i < model.meshCount; ++i) + freeCollisionMesh(model.meshes[i]); + + KF_FREE(model.meshes); +} + +void transformCollisionMesh(Entity * entity, int num) { + int i, j; + EntityCollisionMesh mesh1 = entity->collisionModel.meshes[num]; + EntityCollisionMesh * mesh2 = &entity->transformedCollisionModel.meshes[num]; + Vector3 vertex; + + Matrix m = QuaternionToMatrix(QuaternionInvert(entity->rotation)); + + for (i = 0; i < mesh1.triangleCount; ++i) { + + // Transform triangle. + for (j = 0; j < 3; ++j) { + vertex = mesh1.triangles[i][j]; + + // Rotate. + mesh2->triangles[i][j] = (Vector3){ + m.m0 * vertex.x + m.m1 * vertex.y + m.m2 * vertex.z, + m.m4 * vertex.x + m.m5 * vertex.y + m.m6 * vertex.z, + m.m8 * vertex.x + m.m9 * vertex.y + m.m10 * vertex.z + }; + + // Move to position. + mesh2->triangles[i][j] = Vector3Add(entity->position, mesh2->triangles[i][j]); + } + + // Transform normals. + vertex = mesh1.normals[i]; + + // Rotate. + mesh2->normals[i] = (Vector3){ + m.m0 * vertex.x + m.m1 * vertex.y + m.m2 * vertex.z, + m.m4 * vertex.x + m.m5 * vertex.y + m.m6 * vertex.z, + m.m8 * vertex.x + m.m9 * vertex.y + m.m10 * vertex.z + }; + + } +} + +void entityTransformCollisionModel(Entity * entity) { + int i; + + for (i = 0; i < entity->collisionModel.meshCount; ++i) + transformCollisionMesh(entity, i); +} + +void entityCheckTransformedCollisionModel(Entity * entity) { + bool moved = !Vector3Equals(entity->lastPosition, entity->position); + moved |= !QuaternionEquals(entity->lastRotation, entity->rotation); + + if (moved) + entity->collisionModelTransformed = false; +} + // Big mesh helper for checkEntityCollision. bool checkEntityMeshCollision(Entity entity1, Entity entity2, int entity1MeshNum, int entity2MeshNum) { int triangle1Num; int triangle2Num; bool collided; - Vector3 triangle1[3]; - Vector3 triangle2[3]; - - Mesh mesh1 = entity1.model->meshes[entity1MeshNum]; - Mesh mesh2 = entity2.model->meshes[entity2MeshNum]; - - Vector3 mesh2Triangles[mesh2.triangleCount][3]; - - // Get mesh2Triangles. - for (triangle2Num = 0; triangle2Num < mesh2.triangleCount; ++triangle2Num) { - getTriangleFromMeshAndTransform( - triangle2Num, - mesh2, - mesh2Triangles[triangle2Num], - entity2.rotation, - entity2.position - ); - } + EntityCollisionMesh mesh1 = entity1.transformedCollisionModel.meshes[entity1MeshNum]; + EntityCollisionMesh mesh2 = entity2.transformedCollisionModel.meshes[entity2MeshNum]; // Test every triangle for collision. for (triangle1Num = 0; triangle1Num < mesh1.triangleCount; ++triangle1Num) { - getTriangleFromMeshAndTransform( - triangle1Num, - mesh1, - triangle1, - entity1.rotation, - entity1.position - ); - for (triangle2Num = 0; triangle2Num < mesh2.triangleCount; ++triangle2Num) { - triangle2[0] = mesh2Triangles[triangle2Num][0]; - triangle2[1] = mesh2Triangles[triangle2Num][1]; - triangle2[2] = mesh2Triangles[triangle2Num][2]; - - // Get normals. - Vector3 normal1 = (Vector3){ - mesh1.normals[triangle1Num * 3], - mesh1.normals[triangle1Num * 3 + 1], - mesh1.normals[triangle1Num * 3 + 2] - }; - - Vector3 normal2 = (Vector3){ - mesh2.normals[triangle2Num * 3], - mesh2.normals[triangle2Num * 3 + 1], - mesh2.normals[triangle2Num * 3 + 2] - }; - // Check collision. + // Check for collision. collided = checkTriangleCollision3D( - triangle1, - triangle2, - normal1, - normal2 + mesh1.triangles[triangle1Num], + mesh2.triangles[triangle2Num], + mesh1.normals[triangle1Num], + mesh2.normals[triangle2Num] ); if (collided) @@ -192,17 +310,28 @@ bool checkEntityMeshCollision(Entity entity1, Entity entity2, int entity1MeshNum return false; } -bool checkEntityCollision(Entity entity1, Entity entity2) { +bool checkEntityCollision(Entity * entity1, Entity * entity2) { int i, j; // Failed quick check. - if (Vector3Distance(entity1.position, entity2.position) > entity1.radius + entity2.radius) + if (Vector3Distance(entity1->position, entity2->position) > entity1->radius + entity2->radius) return false; + // Transform collision model. + if (!entity1->collisionModelTransformed) { + entityTransformCollisionModel(entity1); + entity1->collisionModelTransformed = true; + } + + if (!entity2->collisionModelTransformed) { + entityTransformCollisionModel(entity2); + entity2->collisionModelTransformed = true; + } + // Loop through every mesh and check. - for (i = 0; i < entity1.model->meshCount; ++i) - for (j = 0; j < entity2.model->meshCount; ++j) - if (checkEntityMeshCollision(entity1, entity2, i, j)) + for (i = 0; i < entity1->collisionModel.meshCount; ++i) + for (j = 0; j < entity2->collisionModel.meshCount; ++j) + if (checkEntityMeshCollision(*entity1, *entity2, i, j)) return true; return false; @@ -289,6 +418,11 @@ void entityJoystickControl(Entity * entity, Vector3 stick, float speed) { entityUpdatePosition(entity); } +void entityUpdateLastValues(Entity * entity) { + entity->lastPosition = entity->position; + entity->lastRotation = entity->rotation; +} + void entityFlyToPoint(Entity * entity, Vector3 point, EntityFlyToPointInfo * info) { float t = GetFrameTime(); diff --git a/src/entity.h b/src/entity.h index 1ac3422..6246fdd 100644 --- a/src/entity.h +++ b/src/entity.h @@ -45,6 +45,26 @@ typedef struct EntityVelocity { EntityVelocity entityVelocityIdentity(); +typedef struct EntityCollisionMesh { + size_t triangleCount; + Triangle3D * triangles; + Vector3 * normals; +} EntityCollisionMesh; + +typedef struct EntityCollisionModel { + size_t meshCount; + EntityCollisionMesh * meshes; +} EntityCollisionModel; + +EntityCollisionModel entityCreateCollisionModel(Model model); +void entityFreeCollisionModel(EntityCollisionModel model); + +// Only use if both collision models came from the same model. +void entityTransformCollisionModel(Entity * entity); + +// Checks if collision model will need to be transformed. +void entityCheckTransformedCollisionModel(Entity * entity); + // This fucker hit something. typedef struct EntityCollision { bool hit; @@ -64,10 +84,18 @@ typedef struct Entity { Model * model; float radius; // Used for quick collision detection. + + EntityCollisionModel collisionModel; + EntityCollisionModel transformedCollisionModel; + // Use for checking if model been transformed indeed. + bool collisionModelTransformed; Vector3 position; Quaternion rotation; + Vector3 lastPosition; + Quaternion lastRotation; + EntityVelocity velocity; EntityVelocity lastVelocity; @@ -104,12 +132,13 @@ Entity createEntity(EntityType type, Game * game); void closeEntity(Entity * entity); void setEntityRadius(Entity * entity); // Uses model to set radius; -bool checkEntityCollision(Entity entity1, Entity entity2); +bool checkEntityCollision(Entity * entity1, Entity * entity2); -// Helper functions for updating and drawing. +// Helper functions for updating, drawing... void entityDraw(Entity * entity); void entityUpdatePosition(Entity * entity); void entityUpdateRotation(Entity * entity); +void entityUpdateLastValues(Entity * entity); // Should be at top of update function. void entityJoystickControl(Entity * entity, Vector3 stick, float speed); diff --git a/src/gameCommon.h b/src/gameCommon.h index 4c22664..d1d78ee 100644 --- a/src/gameCommon.h +++ b/src/gameCommon.h @@ -34,6 +34,8 @@ typedef struct Entity Entity; #define ALLOCATION_ERROR TraceLog(LOG_ERROR, "Allocation error in %s:%d", __FILE__, __LINE__) +typedef Vector3 Triangle3D[3]; + // Errors. typedef enum KfError { KFERROR = -1, @@ -20,7 +20,7 @@ void printVector3(Vector3 v) { // Warning. Mostly chatgpt written. -static void projectTriangleOntoAxis(const Vector3 triangle[3], const Vector3 axis, float* min, float* max) { +static void projectTriangleOntoAxis(const Triangle3D triangle, const Vector3 axis, float* min, float* max) { float dot1 = Vector3DotProduct(triangle[0], axis); float dot2 = Vector3DotProduct(triangle[1], axis); float dot3 = Vector3DotProduct(triangle[2], axis); @@ -29,7 +29,7 @@ static void projectTriangleOntoAxis(const Vector3 triangle[3], const Vector3 axi *max = fmaxf(fmaxf(dot1, dot2), dot3); } -static bool trianglesIntersectOnAxis(const Vector3 triangleA[3], const Vector3 triangleB[3], Vector3 axis) { +static bool trianglesIntersectOnAxis(const Triangle3D triangleA, const Triangle3D triangleB, Vector3 axis) { float minA, maxA; float minB, maxB; @@ -39,7 +39,7 @@ static bool trianglesIntersectOnAxis(const Vector3 triangleA[3], const Vector3 t return (minA <= maxB && maxA >= minB); } -bool checkTriangleCollision3D(const Vector3 triangleA[3], const Vector3 triangleB[3], Vector3 normalA, Vector3 normalB) { +bool checkTriangleCollision3D(const Triangle3D triangleA, const Triangle3D triangleB, Vector3 normalA, Vector3 normalB) { // Test triangle normals if (!trianglesIntersectOnAxis(triangleA, triangleB, normalA)) return false; @@ -72,3 +72,9 @@ bool checkTriangleCollision3D(const Vector3 triangleA[3], const Vector3 triangle return true; } + +void copyTriangle3D(Triangle3D a, const Triangle3D b) { + a[0] = b[0]; + a[1] = b[1]; + a[2] = b[2]; +} @@ -23,13 +23,9 @@ float signum(float n); // Debugging stuff. void printVector3(Vector3 v); -typedef struct Triangle3D { - Vector3 v1; - Vector3 v2; - Vector3 v3; -} Triangle3D; - // Thank you chatgpt. -bool checkTriangleCollision3D(const Vector3 triangleA[3], const Vector3 triangleB[3], Vector3 normalA, Vector3 normalB); +bool checkTriangleCollision3D(const Triangle3D triangleA, const Triangle3D triangleB, Vector3 normalA, Vector3 normalB); + +void copyTriangle3D(Triangle3D a, const Triangle3D b); #endif diff --git a/src/world.c b/src/world.c index e6f24bb..1a3edde 100644 --- a/src/world.c +++ b/src/world.c @@ -255,7 +255,7 @@ void updateWorld(World * world, Game * game) { entity2 = &world->entities[j]; // Collided. - if (checkEntityCollision(*entity, *entity2)) + if (checkEntityCollision(entity, entity2)) handleCollisionInWorld(entity, entity2); } |