#include "common.h" #include "model.h" typedef struct HitFile { /* 0x00 */ u32 collisionOffset; /* 0x04 */ u32 zoneOffset; } HitFile; // size = 0x08 typedef struct ColliderBackupEntry { /* 0x00 */ s32 flags; /* 0x04 */ s16 parentModelIndex; /* 0x06 */ s16 unk_06; } ColliderBackupEntry; // size = 0x08 typedef struct HitFileHeader { /* 0x00 */ s16 numColliders; /* 0x02 */ s16 unk_02; /* 0x04 */ s32 collidersOffset; /* 0x08 */ s16 numVertices; /* 0x0A */ s16 unk_0a; /* 0x0C */ s32 verticesOffset; /* 0x10 */ s16 boundingBoxesDataSize; /* 0x12 */ s16 unk_12; /* 0x14 */ s32 boundingBoxesOffset; } HitFileHeader; // size = 0x18 typedef struct HitAssetCollider { /* 0x00 */ s16 boundingBoxOffset; /* 0x02 */ s16 nextSibling; /* 0x04 */ s16 firstChild; /* 0x06 */ s16 numTriangles; /* 0x08 */ s32 trianglesOffset; } HitAssetCollider; // size = 0x0C typedef struct VertexIndexStruct { /* 0x00 */ s16 i1; /* 0x02 */ s16 i2; /* 0x04 */ s16 i3; } VertexIndexStruct; // size = 0x06 extern VertexIndexStruct gEntityColliderFaces[]; extern Vec3f gEntityColliderNormals[]; extern f32 gCollisionRayStartX; extern f32 gCollisionRayStartY; extern f32 gCollisionRayStartZ; extern f32 gCollisionRayDirX; extern f32 gCollisionRayDirY; extern f32 gCollisionRayDirZ; extern f32 gCollisionPointX; extern f32 gCollisionPointY; extern f32 gCollisionPointZ; extern f32 gCollisionRayLength; extern f32 gCollisionNormalX; extern f32 gCollisionNormalY; extern f32 gCollisionNormalZ; extern ColliderBackupEntry* gCollisionDataBackup; extern ColliderBackupEntry* gCollisionDataZoneBackup; s32 collision_heap_create(void); void* collision_heap_malloc(s32 size); void collision_heap_free(void*); void load_hit_data(s32 idx, HitFile* hit); void _add_hit_vert_to_buffer(Vec3f** buf, Vec3f* vert, s32* bufSize); s32 _get_hit_vert_index_from_buffer(Vec3f** buffer, Vec3f* vert, s32* bufferSize); void backup_map_collision_data(void) { CollisionData* collisionData; Collider* collider; ColliderBackupEntry* backupEntry; s32 i; collisionData = &gCollisionData; gCollisionDataBackup = general_heap_malloc(collisionData->numColliders * sizeof(ColliderBackupEntry)); for (i = 0, backupEntry = gCollisionDataBackup; i < collisionData->numColliders; i++, backupEntry++) { collider = &collisionData->colliderList[i]; backupEntry->flags = collider->flags; backupEntry->parentModelIndex = collider->parentModelIndex; } collisionData = &gZoneCollisionData; gCollisionDataZoneBackup = general_heap_malloc(collisionData->numColliders * sizeof(ColliderBackupEntry)); for (i = 0, backupEntry = gCollisionDataZoneBackup; i < collisionData->numColliders; i++, backupEntry++) { collider = &collisionData->colliderList[i]; backupEntry->flags = collider->flags; backupEntry->parentModelIndex = collider->parentModelIndex; } gZoneCollisionData.numColliders = 0; } void func_8005AF84(void) { } void func_8005AF8C(void) { } void initialize_collision(void) { gCollisionData.numColliders = 0; gZoneCollisionData.numColliders = 0; collision_heap_create(); } void load_map_hit_asset(void) { u32 assetSize; MapConfig* map = get_current_map_header(); void* compressedData = load_asset_by_name(&wMapHitName, &assetSize); HitFile* uncompressedData = heap_malloc(assetSize); decode_yay0(compressedData, uncompressedData); general_heap_free(compressedData); map->hitAssetCollisionOffset = uncompressedData->collisionOffset; map->hitAssetZoneOffset = uncompressedData->zoneOffset; load_hit_data(0, uncompressedData); load_hit_data(1, uncompressedData); heap_free(uncompressedData); } void restore_map_collision_data(void) { CollisionData* collisionData; Collider* collider; ColliderBackupEntry* backupEntry; s32 i; load_map_hit_asset(); collisionData = &gCollisionData; for (i = 0, backupEntry = gCollisionDataBackup; i < collisionData->numColliders; i++, backupEntry++) { collider = &collisionData->colliderList[i]; collider->flags = backupEntry->flags; collider->parentModelIndex = backupEntry->parentModelIndex; if (collider->flags != -1 && collider->flags & 0x80000000) { parent_collider_to_model(i, collider->parentModelIndex); update_collider_transform(i); } } collisionData = &gZoneCollisionData; for (i = 0, backupEntry = gCollisionDataZoneBackup; i < collisionData->numColliders; i++, backupEntry++) { collider = &collisionData->colliderList[i]; collider->flags = backupEntry->flags; collider->parentModelIndex = backupEntry->parentModelIndex; } general_heap_free(gCollisionDataBackup); general_heap_free(gCollisionDataZoneBackup); } void load_battle_hit_asset(const char* hitName) { if (hitName == NULL) { gCollisionData.numColliders = 0; } else { u32 assetSize; MapConfig* map = get_current_map_header(); void* compressedData = load_asset_by_name(hitName, &assetSize); HitFile* uncompressedData = heap_malloc(assetSize); decode_yay0(compressedData, uncompressedData); general_heap_free(compressedData); map->hitAssetCollisionOffset = uncompressedData->collisionOffset; load_hit_data(0, uncompressedData); heap_free(uncompressedData); } } void load_hit_data(s32 idx, HitFile* hit) { s32 collisionOffset; MapConfig* map; CollisionData* collisionData; HitFileHeader* assetCollisionData; Vec3f* vertices; Vec3s* assetVertices; u32* boundingBox; u32* assetBoundingBox; Collider* collider; HitAssetCollider* assetCollider; ColliderTriangle* triangle; s32* trianglePacked; s16 numTriangles; s32 i,j; f32 e13_y, e21_z, e13_z, e21_y, e21_x, e13_x, normalX, normalY, normalZ, coeff; assetCollisionData = NULL; collisionData = NULL; map = get_current_map_header(); switch (idx) { case 0: collisionOffset = map->hitAssetCollisionOffset; if (collisionOffset == 0) return; assetCollisionData = (HitFileHeader*)((void*)hit + collisionOffset); collisionData = &gCollisionData; break; case 1: collisionOffset = map->hitAssetZoneOffset; if (collisionOffset == 0) return; assetCollisionData = (HitFileHeader*)((void*)hit + collisionOffset); collisionData = &gZoneCollisionData; break; } assetBoundingBox = (u32*)((void*)hit + assetCollisionData->boundingBoxesOffset);; collisionData->aabbs = collision_heap_malloc(assetCollisionData->boundingBoxesDataSize * 4); for (i = 0, boundingBox = (u32*)(collisionData->aabbs); i < assetCollisionData->boundingBoxesDataSize; assetBoundingBox++, boundingBox++, i++) { *boundingBox = *assetBoundingBox; } assetVertices = (Vec3s*)((void*)hit + assetCollisionData->verticesOffset); collisionData->vertices = collision_heap_malloc(assetCollisionData->numVertices * sizeof(Vec3f)); for (i = 0, vertices = collisionData->vertices; i < assetCollisionData->numVertices; vertices++, assetVertices++, i++) { vertices->x = assetVertices->x; vertices->y = assetVertices->y; vertices->z = assetVertices->z; } assetCollider = (HitAssetCollider*)((void*)hit + assetCollisionData->collidersOffset); collider = collisionData->colliderList = collision_heap_malloc(assetCollisionData->numColliders * sizeof(Collider)); collisionData->numColliders = assetCollisionData->numColliders; for (i = 0; i < assetCollisionData->numColliders; assetCollider++, collider++, i++) { collider->flags = 0; collider->nextSibling = assetCollider->nextSibling; collider->firstChild = assetCollider->firstChild; collider->numTriangles = assetCollider->numTriangles; numTriangles = collider->numTriangles; if (numTriangles) { collider->triangleTable = triangle = collision_heap_malloc(assetCollider->numTriangles * sizeof(ColliderTriangle)); if (assetCollider->boundingBoxOffset < 0) { collider->aabb = NULL; } else { collider->aabb = (ColliderBoundingBox*)((u32*)(collisionData->aabbs) + assetCollider->boundingBoxOffset); if (idx == 0) { collider->aabb->min.x -= 1; collider->aabb->min.y -= 1; collider->aabb->min.z -= 1; collider->aabb->max.x += 1; collider->aabb->max.y += 1; collider->aabb->max.z += 1; collider->flags = collider->aabb->flagsForCollider; } } trianglePacked = (s32*)((void*)hit + assetCollider->trianglesOffset); for (j = 0; j < assetCollider->numTriangles; trianglePacked++, triangle++, j++) { Vec3f* v1 = triangle->v1 = &collisionData->vertices[(*trianglePacked) & 0x3FF]; Vec3f* v2 = triangle->v2 = &collisionData->vertices[(*trianglePacked >> 10) & 0x3FF]; Vec3f* v3 = triangle->v3 = &collisionData->vertices[(*trianglePacked >> 20) & 0x3FF]; triangle->oneSided = (*trianglePacked >> 30) & 1; triangle->e13.x = v3->x - v1->x; triangle->e13.y = v3->y - v1->y; triangle->e13.z = v3->z - v1->z; triangle->e21.x = v1->x - v2->x; triangle->e21.y = v1->y - v2->y; triangle->e21.z = v1->z - v2->z; triangle->e32.x = v2->x - v3->x; triangle->e32.y = v2->y - v3->y; triangle->e32.z = v2->z - v3->z; e13_x = triangle->e13.x; e13_y = triangle->e13.y; e13_z = triangle->e13.z; e21_x = triangle->e21.x; e21_y = triangle->e21.y; e21_z = triangle->e21.z; // vector product normalX = e13_y * e21_z - e13_z * e21_y; normalY = e13_z * e21_x - e13_x * e21_z; normalZ = e13_x * e21_y - e13_y * e21_x; coeff = SQ(normalX) + SQ(normalY) + SQ(normalZ); if (coeff != 0) coeff = 1.0f / sqrtf(coeff); else coeff = 0.0f; triangle->normal.x = normalX * coeff; triangle->normal.y = normalY * coeff; triangle->normal.z = normalZ * coeff; } } } } void parent_collider_to_model(s16 colliderID, s16 modelIndex) { Collider* collider; ColliderTriangle* triangle; s32 i; Vec3f** vertexBuffer; Vec3f** vertexPtr; s32 vertexBufferSize; Vec3f* vertexTable; Vec3f* vertex; collider = &gCollisionData.colliderList[colliderID]; collider->parentModelIndex = modelIndex; collider->flags |= 0x80000000; vertexBuffer = collision_heap_malloc(collider->numTriangles * 0xC); vertexBufferSize = 0; vertexPtr = vertexBuffer; for (i = 0, triangle = collider->triangleTable; i < collider->numTriangles; i++, triangle++) { _add_hit_vert_to_buffer(vertexBuffer, triangle->v1, &vertexBufferSize); _add_hit_vert_to_buffer(vertexBuffer, triangle->v2, &vertexBufferSize); _add_hit_vert_to_buffer(vertexBuffer, triangle->v3, &vertexBufferSize); } collider->numVertices = vertexBufferSize; collider->vertexTable = collision_heap_malloc(vertexBufferSize * 0x18); for (i = 0, vertexTable = collider->vertexTable; i < vertexBufferSize; vertexPtr++, vertexTable += 2, i++) { vertex = *vertexPtr; vertexTable->x = vertexTable[1].x = vertex->x; vertexTable->y = vertexTable[1].y = vertex->y; vertexTable->z = vertexTable[1].z = vertex->z; } vertexTable = collider->vertexTable; for (i = 0, triangle = collider->triangleTable; i < collider->numTriangles; triangle++, i++) { triangle->v1 = &vertexTable[_get_hit_vert_index_from_buffer(vertexBuffer, triangle->v1, &vertexBufferSize) * 2]; triangle->v2 = &vertexTable[_get_hit_vert_index_from_buffer(vertexBuffer, triangle->v2, &vertexBufferSize) * 2]; triangle->v3 = &vertexTable[_get_hit_vert_index_from_buffer(vertexBuffer, triangle->v3, &vertexBufferSize) * 2]; } collision_heap_free(vertexBuffer); } void _add_hit_vert_to_buffer(Vec3f** buf, Vec3f* vert, s32* bufSize) { s32 i; for (i = 0; i < *bufSize; i++) if (buf[i] == vert) break; if (i == *bufSize) { buf[i] = vert; (*bufSize)++; } } s32 _get_hit_vert_index_from_buffer(Vec3f** buffer, Vec3f* vert, s32* bufferSize) { s32 i; for (i = 0; i < *bufferSize; i++) { if (*buffer == vert) { break; } buffer++; } return i; } void update_collider_transform(s16 colliderID) { Collider* collider; struct Model* model; Matrix4f matrix; s32 i; Vec3f* vertexTable; f32 min_x, min_y, min_z, max_x, max_y, max_z; ColliderTriangle* triangle; f32 e13_y, e21_z, e13_z, e21_y, e21_x, e13_x, normalX, normalY, normalZ, coeff; collider = &gCollisionData.colliderList[colliderID]; model = get_model_from_list_index(collider->parentModelIndex); if (!model->currentMatrix) { copy_matrix(model->transformMatrix, matrix); } else { guMtxL2F(matrix, (Mtx*)model->currentMatrix); guMtxCatF(model->transformMatrix, matrix, matrix); } triangle = collider->triangleTable; vertexTable = collider->vertexTable; min_x = min_y = min_z = 999999.9f; max_x = max_y = max_z = -999999.9f; for (i = 0; i < collider->numVertices; vertexTable += 2, i++) { guMtxXFMF(matrix, vertexTable[1].x, vertexTable[1].y, vertexTable[1].z, &vertexTable[0].x, &vertexTable[0].y, &vertexTable[0].z); if (vertexTable[0].x < min_x) min_x = vertexTable[0].x; if (vertexTable[0].x > max_x) max_x = vertexTable[0].x; if (vertexTable[0].y < min_y) min_y = vertexTable[0].y; if (vertexTable[0].y > max_y) max_y = vertexTable[0].y; if (vertexTable[0].z < min_z) min_z = vertexTable[0].z; if (vertexTable[0].z > max_z) max_z = vertexTable[0].z; } collider->aabb->min.x = min_x; collider->aabb->min.y = min_y; collider->aabb->min.z = min_z; collider->aabb->max.x = max_x; collider->aabb->max.y = max_y; collider->aabb->max.z = max_z; for (i = 0; i < collider->numTriangles; triangle++, i++) { Vec3f* v1 = triangle->v1; Vec3f* v2 = triangle->v2; Vec3f* v3 = triangle->v3; triangle->e13.x = v3->x - v1->x; triangle->e13.y = v3->y - v1->y; triangle->e13.z = v3->z - v1->z; triangle->e21.x = v1->x - v2->x; triangle->e21.y = v1->y - v2->y; triangle->e21.z = v1->z - v2->z; triangle->e32.x = v2->x - v3->x; triangle->e32.y = v2->y - v3->y; triangle->e32.z = v2->z - v3->z; e13_x = triangle->e13.x; e13_y = triangle->e13.y; e13_z = triangle->e13.z; e21_x = triangle->e21.x; e21_y = triangle->e21.y; e21_z = triangle->e21.z; // vector product normalX = e13_y * e21_z - e13_z * e21_y; normalY = e13_z * e21_x - e13_x * e21_z; normalZ = e13_x * e21_y - e13_y * e21_x; coeff = SQ(normalX) + SQ(normalY) + SQ(normalZ); if (coeff != 0) coeff = 1.0f / sqrtf(coeff); else coeff = 0.0f; triangle->normal.x = normalX * coeff; triangle->normal.y = normalY * coeff; triangle->normal.z = normalZ * coeff; } } s32 get_collider_type_by_id(s32 colliderID) { if (colliderID & 0x4000) { return 0; } else { return gCollisionData.colliderList[colliderID].flags; } } void get_flat_collider_normal(s32 colliderID, f32* x, f32* y, f32* z) { ColliderTriangle* triangle = &gCollisionData.colliderList[colliderID].triangleTable[0]; *x = triangle->normal.x; *y = triangle->normal.y; *z = triangle->normal.z; } void get_collider_center(s32 colliderID, f32* x, f32* y, f32* z) { ColliderBoundingBox* aabb = gCollisionData.colliderList[colliderID].aabb; *x = (aabb->min.x + aabb->max.x) * 0.5f; *y = (aabb->min.y + aabb->max.y) * 0.5f; *z = (aabb->min.z + aabb->max.z) * 0.5f; } s32 test_ray_triangle_general(ColliderTriangle* triangle, Vec3f* vertices) { f32 distToTrianglePlane; f32 cosAngle; Vec3f* v1; Vec3f* v2; Vec3f* v3; if (triangle->normal.x == 0 && triangle->normal.y == 0 && triangle->normal.z == 0) return FALSE; v1 = triangle->v1; v2 = triangle->v2; v3 = triangle->v3; distToTrianglePlane = triangle->normal.x * (gCollisionRayStartX - v1->x) + triangle->normal.y * (gCollisionRayStartY - v1->y) + triangle->normal.z * (gCollisionRayStartZ - v1->z); if (triangle->oneSided) { if (distToTrianglePlane < 0) return FALSE; if (triangle->normal.x * gCollisionRayDirX + triangle->normal.y * gCollisionRayDirY + triangle->normal.z * gCollisionRayDirZ >= 0) return FALSE; if ((gCollisionRayStartX - v1->x) * (triangle->e13.z * gCollisionRayDirY - triangle->e13.y * gCollisionRayDirZ) + (gCollisionRayStartY - v1->y) * (triangle->e13.x * gCollisionRayDirZ - triangle->e13.z * gCollisionRayDirX) + (gCollisionRayStartZ - v1->z) * (triangle->e13.y * gCollisionRayDirX - triangle->e13.x * gCollisionRayDirY) < 0) return FALSE; if ((gCollisionRayStartX - v2->x) * (triangle->e21.z * gCollisionRayDirY - triangle->e21.y * gCollisionRayDirZ) + (gCollisionRayStartY - v2->y) * (triangle->e21.x * gCollisionRayDirZ - triangle->e21.z * gCollisionRayDirX) + (gCollisionRayStartZ - v2->z) * (triangle->e21.y * gCollisionRayDirX - triangle->e21.x * gCollisionRayDirY) < 0) return FALSE; if ((gCollisionRayStartX - v3->x) * (triangle->e32.z * gCollisionRayDirY - triangle->e32.y * gCollisionRayDirZ) + (gCollisionRayStartY - v3->y) * (triangle->e32.x * gCollisionRayDirZ - triangle->e32.z * gCollisionRayDirX) + (gCollisionRayStartZ - v3->z) * (triangle->e32.y * gCollisionRayDirX - triangle->e32.x * gCollisionRayDirY) < 0) return FALSE; } else { if ((triangle->normal.x * gCollisionRayDirX + triangle->normal.y * gCollisionRayDirY + triangle->normal.z * gCollisionRayDirZ) * distToTrianglePlane >= 0) return FALSE; if (((gCollisionRayStartX - v1->x) * (triangle->e13.z * gCollisionRayDirY - triangle->e13.y * gCollisionRayDirZ) + (gCollisionRayStartY - v1->y) * (triangle->e13.x * gCollisionRayDirZ - triangle->e13.z * gCollisionRayDirX) + (gCollisionRayStartZ - v1->z) * (triangle->e13.y * gCollisionRayDirX - triangle->e13.x * gCollisionRayDirY)) * distToTrianglePlane < 0) return FALSE; if (((gCollisionRayStartX - v2->x) * (triangle->e21.z * gCollisionRayDirY - triangle->e21.y * gCollisionRayDirZ) + (gCollisionRayStartY - v2->y) * (triangle->e21.x * gCollisionRayDirZ - triangle->e21.z * gCollisionRayDirX) + (gCollisionRayStartZ - v2->z) * (triangle->e21.y * gCollisionRayDirX - triangle->e21.x * gCollisionRayDirY)) * distToTrianglePlane < 0) return FALSE; if (((gCollisionRayStartX - v3->x) * (triangle->e32.z * gCollisionRayDirY - triangle->e32.y * gCollisionRayDirZ) + (gCollisionRayStartY - v3->y) * (triangle->e32.x * gCollisionRayDirZ - triangle->e32.z * gCollisionRayDirX) + (gCollisionRayStartZ - v3->z) * (triangle->e32.y * gCollisionRayDirX - triangle->e32.x * gCollisionRayDirY)) * distToTrianglePlane < 0) return FALSE; } cosAngle = triangle->normal.x * gCollisionRayDirX + triangle->normal.y * gCollisionRayDirY + triangle->normal.z * gCollisionRayDirZ; if (gCollisionRayLength >= 0 && gCollisionRayLength <= -distToTrianglePlane / cosAngle) return FALSE; gCollisionRayLength = -distToTrianglePlane / cosAngle; gCollisionPointX = gCollisionRayStartX + gCollisionRayDirX * gCollisionRayLength; gCollisionPointY = gCollisionRayStartY + gCollisionRayDirY * gCollisionRayLength; gCollisionPointZ = gCollisionRayStartZ + gCollisionRayDirZ * gCollisionRayLength; gCollisionNormalX = triangle->normal.x; gCollisionNormalY = triangle->normal.y; gCollisionNormalZ = triangle->normal.z; return TRUE; } s32 test_ray_triangle_down(ColliderTriangle* triangle, Vec3f* vertices) { f32 distToTrianglePlane, cosAngle; Vec3f* v1; Vec3f* v2; Vec3f* v3; if (triangle->normal.x == 0 && triangle->normal.y == 0 && triangle->normal.z == 0) return FALSE; v1 = triangle->v1; v2 = triangle->v2; v3 = triangle->v3; distToTrianglePlane = triangle->normal.x * (gCollisionRayStartX - v1->x) + triangle->normal.y * (gCollisionRayStartY - v1->y) + triangle->normal.z * (gCollisionRayStartZ - v1->z); if (triangle->oneSided) { if (distToTrianglePlane < 0) return FALSE; if (triangle->normal.y <= 0) return FALSE; if ((gCollisionRayStartZ - v1->z) * triangle->e13.x - (gCollisionRayStartX - v1->x) * triangle->e13.z < 0) return FALSE; if ((gCollisionRayStartZ - v2->z) * triangle->e21.x - (gCollisionRayStartX - v2->x) * triangle->e21.z < 0) return FALSE; if ((gCollisionRayStartZ - v3->z) * triangle->e32.x - (gCollisionRayStartX - v3->x) * triangle->e32.z < 0) return FALSE; } else { if (triangle->normal.y * distToTrianglePlane <= 0) return FALSE; if (((gCollisionRayStartZ - v1->z) * triangle->e13.x - (gCollisionRayStartX - v1->x) * triangle->e13.z) * distToTrianglePlane < 0) return FALSE; if (((gCollisionRayStartZ - v2->z) * triangle->e21.x - (gCollisionRayStartX - v2->x) * triangle->e21.z) * distToTrianglePlane < 0) return FALSE; if (((gCollisionRayStartZ - v3->z) * triangle->e32.x - (gCollisionRayStartX - v3->x) * triangle->e32.z) * distToTrianglePlane < 0) return FALSE; } cosAngle = -triangle->normal.y; if (gCollisionRayLength >= 0 && gCollisionRayLength <= -distToTrianglePlane / cosAngle) return FALSE; gCollisionRayLength = -distToTrianglePlane / cosAngle; gCollisionPointX = gCollisionRayStartX; gCollisionPointY = gCollisionRayStartY - gCollisionRayLength; gCollisionPointZ = gCollisionRayStartZ; gCollisionNormalX = triangle->normal.x; gCollisionNormalY = triangle->normal.y; gCollisionNormalZ = triangle->normal.z; return TRUE; } s32 test_ray_triangle_horizontal(ColliderTriangle* triangle, Vec3f *vertices) { f32 distToTrianglePlane, cosAngle; Vec3f* v1; Vec3f* v2; Vec3f* v3; if (triangle->normal.x == 0 && triangle->normal.y == 0 && triangle->normal.z == 0) return FALSE; v1 = triangle->v1; v2 = triangle->v2; v3 = triangle->v3; distToTrianglePlane = triangle->normal.x * (gCollisionRayStartX - v1->x) + triangle->normal.y * (gCollisionRayStartY - v1->y) + triangle->normal.z * (gCollisionRayStartZ - v1->z); if (triangle->oneSided) { if (distToTrianglePlane < 0) return FALSE; if (triangle->normal.x * gCollisionRayDirX + triangle->normal.z * gCollisionRayDirZ >= 0) return FALSE; if ((gCollisionRayStartX - v1->x) * (-triangle->e13.y * gCollisionRayDirZ) + (gCollisionRayStartY - v1->y) * (triangle->e13.x * gCollisionRayDirZ - triangle->e13.z * gCollisionRayDirX) + (gCollisionRayStartZ - v1->z) * (triangle->e13.y * gCollisionRayDirX) < 0) return FALSE; if ((gCollisionRayStartX - v2->x) * (-triangle->e21.y * gCollisionRayDirZ) + (gCollisionRayStartY - v2->y) * (triangle->e21.x * gCollisionRayDirZ - triangle->e21.z * gCollisionRayDirX) + (gCollisionRayStartZ - v2->z) * (triangle->e21.y * gCollisionRayDirX) < 0) return FALSE; if ((gCollisionRayStartX - v3->x) * (-triangle->e32.y * gCollisionRayDirZ) + (gCollisionRayStartY - v3->y) * (triangle->e32.x * gCollisionRayDirZ - triangle->e32.z * gCollisionRayDirX) + (gCollisionRayStartZ - v3->z) * (triangle->e32.y * gCollisionRayDirX) < 0) return FALSE; } else { if ((triangle->normal.x * gCollisionRayDirX + triangle->normal.z * gCollisionRayDirZ) * distToTrianglePlane >= 0) return FALSE; if (((gCollisionRayStartX - v1->x) * (-triangle->e13.y * gCollisionRayDirZ) + (gCollisionRayStartY - v1->y) * (triangle->e13.x * gCollisionRayDirZ - triangle->e13.z * gCollisionRayDirX) + (gCollisionRayStartZ - v1->z) * (triangle->e13.y * gCollisionRayDirX)) * distToTrianglePlane < 0) return FALSE; if (((gCollisionRayStartX - v2->x) * (-triangle->e21.y * gCollisionRayDirZ) + (gCollisionRayStartY - v2->y) * (triangle->e21.x * gCollisionRayDirZ - triangle->e21.z * gCollisionRayDirX) + (gCollisionRayStartZ - v2->z) * (triangle->e21.y * gCollisionRayDirX)) * distToTrianglePlane < 0) return FALSE; if (((gCollisionRayStartX - v3->x) * (-triangle->e32.y * gCollisionRayDirZ) + (gCollisionRayStartY - v3->y) * (triangle->e32.x * gCollisionRayDirZ - triangle->e32.z * gCollisionRayDirX) + (gCollisionRayStartZ - v3->z) * (triangle->e32.y * gCollisionRayDirX)) * distToTrianglePlane < 0) return FALSE; } cosAngle = triangle->normal.x * gCollisionRayDirX + triangle->normal.z * gCollisionRayDirZ; if (gCollisionRayLength >= 0 && gCollisionRayLength <= -distToTrianglePlane / cosAngle) return FALSE; gCollisionRayLength = -distToTrianglePlane / cosAngle; gCollisionPointX = gCollisionRayStartX + gCollisionRayDirX * gCollisionRayLength; gCollisionPointY = gCollisionRayStartY; gCollisionPointZ = gCollisionRayStartZ + gCollisionRayDirZ * gCollisionRayLength; gCollisionNormalX = triangle->normal.x; gCollisionNormalY = triangle->normal.y; gCollisionNormalZ = triangle->normal.z; return TRUE; } s32 test_ray_colliders(s32 ignoreFlags, f32 startX, f32 startY, f32 startZ, f32 dirX, f32 dirY, f32 dirZ, f32* hitX, f32* hitY, f32* hitZ, f32* hitDepth, f32* hitNx, f32* hitNy, f32* hitNz) { Collider* collider; CollisionData* collisionData; ColliderTriangle* triangle; s32 i, j; s32 colliderID; f32 min_x, min_y, min_z, max_x, max_y, max_z; if (dirX == 0 && dirY == 0 && dirZ == 0) return 0; collisionData = &gCollisionData; gCollisionRayDirX = dirX; gCollisionRayDirY = dirY; gCollisionRayDirZ = dirZ; gCollisionRayStartX = startX; gCollisionRayStartY = startY; gCollisionRayStartZ = startZ; gCollisionRayLength = *hitDepth; colliderID = -1; if (dirX < 0) { min_x = startX + dirX * gCollisionRayLength; max_x = startX; } else { min_x = startX; max_x = startX + dirX * gCollisionRayLength; } if (dirY < 0) { min_y = startY + dirY * gCollisionRayLength; max_y = startY; } else { min_y = startY; max_y = startY + dirY * gCollisionRayLength; } if (dirZ < 0) { min_z = startZ + dirZ * gCollisionRayLength; max_z = startZ; } else { min_z = startZ; max_z = startZ + dirZ * gCollisionRayLength; } for (i = 0; i < collisionData->numColliders; i++) { collider = &collisionData->colliderList[i]; if ((collider->flags & ignoreFlags) || collider->numTriangles == 0 || max_x < collider->aabb->min.x || min_x > collider->aabb->max.x || max_z < collider->aabb->min.z || min_z > collider->aabb->max.z || max_y < collider->aabb->min.y || min_y > collider->aabb->max.y) continue; triangle = collider->triangleTable; if (gCollisionRayDirX == 0 && gCollisionRayDirZ == 0 && gCollisionRayDirY == -1.0) { for (j = 0; j < collider->numTriangles; j++) if (test_ray_triangle_down(triangle++, collisionData->vertices)) colliderID = i; } else if (gCollisionRayDirY == 0) { for (j = 0; j < collider->numTriangles; j++) if (test_ray_triangle_horizontal(triangle++, collisionData->vertices)) colliderID = i; } else { for (j = 0; j < collider->numTriangles; j++) if (test_ray_triangle_general(triangle++, collisionData->vertices)) colliderID = i; } } if (colliderID >= 0) { *hitX = gCollisionPointX; *hitY = gCollisionPointY; *hitZ = gCollisionPointZ; *hitDepth = gCollisionRayLength; *hitNx = gCollisionNormalX; *hitNy = gCollisionNormalY; *hitNz = gCollisionNormalZ; return colliderID; } else return colliderID; } s32 test_ray_zones(f32 startX, f32 startY, f32 startZ, f32 dirX, f32 dirY, f32 dirZ, f32* hitX, f32* hitY, f32* hitZ, f32* hitDepth, f32* hitNx, f32* hitNy, f32* hitNz) { Collider* collider; CollisionData* collisionData; ColliderTriangle* triangle; s32 i, j; s32 colliderID; collisionData = &gZoneCollisionData; gCollisionRayDirX = dirX; gCollisionRayDirY = dirY; gCollisionRayDirZ = dirZ; gCollisionRayStartX = startX; gCollisionRayStartY = startY; gCollisionRayStartZ = startZ; gCollisionRayLength = *hitDepth; colliderID = -1; for (i = 0; i < collisionData->numColliders; i++) { collider = &collisionData->colliderList[i]; if (collider->flags & 0x10000) continue; if (collider->numTriangles == 0 || collider->aabb == NULL) continue; triangle = collider->triangleTable; for (j = 0; j < collider->numTriangles; j++) if (test_ray_triangle_down(triangle++, collisionData->vertices)) colliderID = i; } if (colliderID >= 0) { *hitX = gCollisionPointX; *hitY = gCollisionPointY; *hitZ = gCollisionPointZ; *hitDepth = gCollisionRayLength; *hitNx = gCollisionNormalX; *hitNy = gCollisionNormalY; *hitNz = gCollisionNormalZ; return colliderID; } else return colliderID; } f32 test_ray_collider_horizontal(s32 ignoreFlags, s32 colliderID, f32 x, f32 y, f32 z, f32 length, f32 yaw) { CollisionData* collisionData = &gCollisionData; f32 cosTheta; f32 sinTheta; Collider* collider; ColliderTriangle* triangleTable; s32 i; f32 ret; sin_cos_rad(yaw * TAU / 360.0f, &sinTheta, &cosTheta); collider = &collisionData->colliderList[colliderID]; gCollisionRayDirY = 0; gCollisionRayStartX = x; gCollisionRayStartY = y; gCollisionRayStartZ = z; gCollisionRayLength = length; gCollisionRayDirX = sinTheta; gCollisionRayDirZ = -cosTheta; ret = -1.0f; if (!(collider->flags & ignoreFlags) && collider->numTriangles != 0) { triangleTable = collider->triangleTable; for (i = 0; i < collider->numTriangles; i++) if (test_ray_triangle_horizontal(triangleTable++, collisionData->vertices)) ret = gCollisionRayLength; } return ret; } s32 test_ray_entities(f32 startX, f32 startY, f32 startZ, f32 dirX, f32 dirY, f32 dirZ, f32* hitX, f32* hitY, f32* hitZ, f32* hitDepth, f32* hitNx, f32* hitNy, f32* hitNz) { f32 hitDepthDown, hitDepthHoriz; s32 type; s32 i, j; Entity* entity; Matrix4f tempMatrix1; Matrix4f tempMatrix2; Vec3f boxVertices[8]; ColliderTriangle entityTriangle; s32 entityIndex; f32 h; f32 aabbX, aabbZ; s32 hasCollision; f32 dist, dist2; ColliderTriangle *triangle = &entityTriangle; entityIndex = -1; type = 0; hitDepthDown = hitDepthHoriz = *hitDepth; if (dirX == 0 && dirZ == 0 && dirY < 0) { hitDepthHoriz = 0; type = 1; } else if (dirY == 0) { hitDepthDown = 0; type = 2; } gCollisionRayLength = -1; triangle->oneSided = TRUE; for (i = 0; i < MAX_ENTITIES; i++) { entity = get_entity_by_index(i); if (entity == NULL || (entity->flags & 0x40000020)) continue; dist = hitDepthHoriz + entity->effectiveSize; if (startX > entity->position.x + dist || startX < entity->position.x - dist) continue; if (startZ > entity->position.z + dist || startZ < entity->position.z - dist) continue; switch (type) { case 0: case 1: dist = entity->position.y; dist2 = hitDepthDown + entity->effectiveSize * 2; if (dist + dist2 < startY || startY < dist - dist2) continue; break; case 2: dist = entity->position.y; dist2 = entity->effectiveSize * 2; if (dist + dist2 < startY || startY < dist - dist2) continue; break; } aabbX = entity->aabb.x / 2; aabbZ = entity->aabb.z / 2; boxVertices[1].x = boxVertices[2].x = boxVertices[5].x = boxVertices[6].x = -aabbX; boxVertices[0].x = boxVertices[3].x = boxVertices[4].x = boxVertices[7].x = aabbX; boxVertices[0].y = boxVertices[1].y = boxVertices[2].y = boxVertices[3].y = 0; boxVertices[4].y = boxVertices[5].y = boxVertices[6].y = boxVertices[7].y = entity->aabb.y; boxVertices[0].z = boxVertices[1].z = boxVertices[4].z = boxVertices[5].z = aabbZ; boxVertices[2].z = boxVertices[3].z = boxVertices[6].z = boxVertices[7].z = -aabbZ; guMtxXFMF(entity->inverseTransformMatrix, dirX, dirY, dirZ, &gCollisionRayDirX, &gCollisionRayDirY, &gCollisionRayDirZ); guMtxXFMF(entity->inverseTransformMatrix, startX - entity->position.x, startY - entity->position.y, startZ - entity->position.z, &gCollisionRayStartX, &gCollisionRayStartY, &gCollisionRayStartZ); for (j = 0; j < 12; j++) { Vec3f* v1 = triangle->v1 = &boxVertices[gEntityColliderFaces[j].i1]; Vec3f* v2 = triangle->v2 = &boxVertices[gEntityColliderFaces[j].i2]; Vec3f* v3 = triangle->v3 = &boxVertices[gEntityColliderFaces[j].i3]; triangle->e13.x = v3->x - v1->x; triangle->e13.y = v3->y - v1->y; triangle->e13.z = v3->z - v1->z; triangle->e21.x = v1->x - v2->x; triangle->e21.y = v1->y - v2->y; triangle->e21.z = v1->z - v2->z; triangle->e32.x = v2->x - v3->x; triangle->e32.y = v2->y - v3->y; triangle->e32.z = v2->z - v3->z; triangle->normal.x = gEntityColliderNormals[j].x; triangle->normal.y = gEntityColliderNormals[j].y; triangle->normal.z = gEntityColliderNormals[j].z; if (hasCollision = test_ray_triangle_general(&entityTriangle, boxVertices)) break; } if (hasCollision && gCollisionRayLength < *hitDepth) { entityIndex = i; *hitDepth = gCollisionRayLength; switch (type) { case 0: hitDepthDown = gCollisionRayLength; hitDepthHoriz = gCollisionRayLength; break; case 1: hitDepthDown = gCollisionRayLength; break; case 2: hitDepthHoriz = gCollisionRayLength; break; } guRotateF(tempMatrix1, entity->rotation.x, 1.0f, 0.0f, 0.0f); guRotateF(tempMatrix2, entity->rotation.z, 0.0f, 0.0f, 1.0f); guMtxCatF(tempMatrix1, tempMatrix2, tempMatrix1); guRotateF(tempMatrix2, entity->rotation.y, 0.0f, 1.0f, 0.0f); guMtxCatF(tempMatrix1, tempMatrix2, tempMatrix1); guTranslateF(tempMatrix2, entity->position.x, entity->position.y, entity->position.z); guMtxCatF(tempMatrix1, tempMatrix2, tempMatrix1); guMtxXFMF(tempMatrix1, gCollisionPointX, gCollisionPointY, gCollisionPointZ, hitX, hitY, hitZ); h = 1.0f / sqrtf(SQ(gCollisionNormalX) + SQ(gCollisionNormalY) + SQ(gCollisionNormalZ)); *hitNx = gCollisionNormalX * h; *hitNy = gCollisionNormalY * h; *hitNz = gCollisionNormalZ * h; } } return entityIndex; } ApiStatus func_8005DB00(Evt* script, s32 isInitialCall) { Npc* npc; f32 targetDir, npcYaw; PlayerStatus* playerStatus; PartnerActionStatus* partnerStatus; npc = get_npc_unsafe(script->owner1.enemy->npcID); playerStatus = &gPlayerStatus; partnerStatus = &gPartnerActionStatus; if (isInitialCall) script->functionTemp[0] = 0; switch(script->functionTemp[0]) { case 0: npc->planarFlyDist = evt_get_float_variable(script, LW(0)); npc->duration = evt_get_variable(script, LW(1)); script->functionTemp[1] = evt_get_variable(script, LW(2)); script->functionTemp[2] = evt_get_variable(script, LW(3)) / 2; npc->currentAnim.w = script->varTable[10]; script->functionTemp[0] = 1; break; case 1: if (partnerStatus->actionState.b[3] != 9) { if (npc->duration != 0 && npc->duration != script->functionTemp[0]) return ApiStatus_BLOCK; if (npc->duration == 0) { if (sqrtf(SQ((playerStatus->position.x - npc->pos.x)) + SQ((playerStatus->position.y - npc->pos.y)) + SQ((playerStatus->position.z - npc->pos.z))) <= npc->planarFlyDist) { targetDir = atan2(npc->pos.x, npc->pos.z, playerStatus->position.x, playerStatus->position.z); npcYaw = script->functionTemp[1] == -1 ? npc->yaw : script->functionTemp[1]; if (fabsf(get_clamped_angle_diff(npcYaw, targetDir)) < script->functionTemp[2]) { script->varTable[0] = 1; return ApiStatus_DONE2; } } } else { if (dist2D(npc->pos.x, npc->pos.z, playerStatus->position.x, playerStatus->position.z) <= npc->planarFlyDist) { targetDir = atan2(npc->pos.x, npc->pos.z, playerStatus->position.x, playerStatus->position.z); npcYaw = script->functionTemp[1] == -1 ? npc->yaw : script->functionTemp[1]; if (fabsf(get_clamped_angle_diff(npcYaw, targetDir)) < script->functionTemp[2]) { script->varTable[0] = 1; return ApiStatus_DONE2; } } } } break; } return ApiStatus_BLOCK; } ApiStatus func_8005DD54(Evt* script, s32 isInitialCall) { f32 angle = clamp_angle(script->varTable[0]); script->varTable[0] = angle; if (angle >= 180.0f) { angle -= 180.0f; } if (angle >= 90.0f) { angle = 180.0f - angle; } script->varTable[2] = (s32) (((90.0f - angle) * 95.0f) / 90.0f) + 160; return ApiStatus_DONE2; } s32 func_8005DDF0(void) { return ~gCurrentEncounter.unk_08 != 0; }