summary refs log tree commit diff
path: root/src/Physics.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/Physics.c')
-rw-r--r--src/Physics.c244
1 files changed, 244 insertions, 0 deletions
diff --git a/src/Physics.c b/src/Physics.c
new file mode 100644
index 0000000..361ee65
--- /dev/null
+++ b/src/Physics.c
@@ -0,0 +1,244 @@
+#include "Physics.h"
+#include "ExtMath.h"
+#include "Block.h"
+#include "World.h"
+#include "Platform.h"
+#include "ExtMath.h"
+#include "Funcs.h"
+#include "Logger.h"
+#include "Entity.h"
+
+
+/*########################################################################################################################*
+*----------------------------------------------------------AABB-----------------------------------------------------------*
+*#########################################################################################################################*/
+void AABB_Make(struct AABB* result, const Vec3* pos, const Vec3* size) {
+	result->Min.x = pos->x - size->x * 0.5f;
+	result->Min.y = pos->y;
+	result->Min.z = pos->z - size->z * 0.5f;
+
+	result->Max.x = pos->x + size->x * 0.5f;
+	result->Max.y = pos->y + size->y;
+	result->Max.z = pos->z + size->z * 0.5f;
+}
+
+void AABB_Offset(struct AABB* result, const struct AABB* bb, const Vec3* amount) {
+	Vec3_Add(&result->Min, &bb->Min, amount);
+	Vec3_Add(&result->Max, &bb->Max, amount);
+}
+
+cc_bool AABB_Intersects(const struct AABB* bb, const struct AABB* other) {
+	return
+		bb->Max.x >= other->Min.x && bb->Min.x <= other->Max.x &&
+		bb->Max.y >= other->Min.y && bb->Min.y <= other->Max.y &&
+		bb->Max.z >= other->Min.z && bb->Min.z <= other->Max.z;
+}
+
+cc_bool AABB_Contains(const struct AABB* parent, const struct AABB* child) {
+	return
+		child->Min.x >= parent->Min.x && child->Min.y >= parent->Min.y && child->Min.z >= parent->Min.z &&
+		child->Max.x <= parent->Max.x && child->Max.y <= parent->Max.y && child->Max.z <= parent->Max.z;
+}
+
+cc_bool AABB_ContainsPoint(const struct AABB* parent, const Vec3* P) {
+	return
+		P->x >= parent->Min.x && P->y >= parent->Min.y && P->z >= parent->Min.z &&
+		P->x <= parent->Max.x && P->y <= parent->Max.y && P->z <= parent->Max.z;
+}
+
+
+/*########################################################################################################################*
+*------------------------------------------------------Intersection-------------------------------------------------------*
+*#########################################################################################################################*/
+static Vec3 Intersection_InverseRotate(Vec3 pos, struct Entity* target) {
+	pos = Vec3_RotateY(pos, -target->RotY * MATH_DEG2RAD);
+	pos = Vec3_RotateZ(pos, -target->RotZ * MATH_DEG2RAD);
+	pos = Vec3_RotateX(pos, -target->RotX * MATH_DEG2RAD);
+	return pos;
+}
+
+cc_bool Intersection_RayIntersectsRotatedBox(Vec3 origin, Vec3 dir, struct Entity* target, float* tMin, float* tMax) {
+	Vec3 delta, invDir;
+	struct AABB bb;
+
+	/* This is the rotated AABB of the model we want to test for intersection
+			  *
+			 / \     we then perform a counter       *---*   and we can then do
+	   ====>* x *    rotation to the rotated AABB    | x |   a standard AABB test
+			 \ /     and ray origin and direction    *---*   with the rotated ray
+			  *                                     /
+												   /                           
+	*/
+	Vec3_Sub(&delta, &origin, &target->Position);   /* delta  = origin - target.Position */
+	delta = Intersection_InverseRotate(delta, target); /* delta  = UndoRotation(delta) */
+	Vec3_Add(&origin, &delta, &target->Position);   /* origin = delta + target.Position */
+
+	dir = Intersection_InverseRotate(dir, target);
+	Entity_GetPickingBounds(target, &bb);
+
+	invDir.x = 1.0f / dir.x;
+	invDir.y = 1.0f / dir.y;
+	invDir.z = 1.0f / dir.z;
+	return Intersection_RayIntersectsBox(origin, invDir, bb.Min, bb.Max, tMin, tMax);
+}
+
+cc_bool Intersection_RayIntersectsBox(Vec3 origin, Vec3 invDir, Vec3 min, Vec3 max, float* t0, float* t1) {
+	float tmin, tmax, tymin, tymax, tzmin, tzmax;
+	*t0 = 0; *t1 = 0;
+	
+	if (invDir.x >= 0) {
+		tmin = (min.x - origin.x) * invDir.x;
+		tmax = (max.x - origin.x) * invDir.x;
+	} else {
+		tmin = (max.x - origin.x) * invDir.x;
+		tmax = (min.x - origin.x) * invDir.x;
+	}
+
+	if (invDir.y >= 0) {
+		tymin = (min.y - origin.y) * invDir.y;
+		tymax = (max.y - origin.y) * invDir.y;
+	} else {
+		tymin = (max.y - origin.y) * invDir.y;
+		tymax = (min.y - origin.y) * invDir.y;
+	}
+
+	if (tmin > tymax || tymin > tmax) return false;
+	if (tymin > tmin) tmin = tymin;
+	if (tymax < tmax) tmax = tymax;
+
+	if (invDir.z >= 0) {
+		tzmin = (min.z - origin.z) * invDir.z;
+		tzmax = (max.z - origin.z) * invDir.z;
+	} else {
+		tzmin = (max.z - origin.z) * invDir.z;
+		tzmax = (min.z - origin.z) * invDir.z;
+	}
+
+	if (tmin > tzmax || tzmin > tmax) return false;
+	if (tzmin > tmin) tmin = tzmin;
+	if (tzmax < tmax) tmax = tzmax;
+
+	*t0 = tmin; *t1 = tmax;
+	return tmin >= 0.0f;
+}
+
+
+/*########################################################################################################################*
+*----------------------------------------------------Collisions finder----------------------------------------------------*
+*#########################################################################################################################*/
+#define SEARCHER_STATES_MIN 64
+static struct SearcherState searcherDefaultStates[SEARCHER_STATES_MIN];
+static cc_uint32 searcherCapacity = SEARCHER_STATES_MIN;
+struct SearcherState* Searcher_States = searcherDefaultStates;
+
+static void Searcher_QuickSort(int left, int right) {
+	struct SearcherState* keys = Searcher_States; struct SearcherState key;
+
+	while (left < right) {
+		int i = left, j = right;
+		float pivot = keys[(i + j) >> 1].tSquared;
+
+		/* partition the list */
+		while (i <= j) {
+			while (pivot > keys[i].tSquared) i++;
+			while (pivot < keys[j].tSquared) j--;
+			QuickSort_Swap_Maybe();
+		}
+		/* recurse into the smaller subset */
+		QuickSort_Recurse(Searcher_QuickSort);
+	}
+}
+
+int Searcher_FindReachableBlocks(struct Entity* entity, struct AABB* entityBB, struct AABB* entityExtentBB) {
+	Vec3 vel = entity->Velocity;
+	IVec3 min, max;
+	cc_uint32 elements;
+	struct SearcherState* curState;
+	int count;
+
+	BlockID block;
+	struct AABB blockBB;
+	float xx, yy, zz, tx, ty, tz;
+	int x, y, z;
+
+	Entity_GetBounds(entity, entityBB);
+	/* Exact maximum extent the entity can reach, and the equivalent map coordinates. */
+	entityExtentBB->Min.x = entityBB->Min.x + (vel.x < 0.0f ? vel.x : 0.0f);
+	entityExtentBB->Min.y = entityBB->Min.y + (vel.y < 0.0f ? vel.y : 0.0f);
+	entityExtentBB->Min.z = entityBB->Min.z + (vel.z < 0.0f ? vel.z : 0.0f);
+
+	entityExtentBB->Max.x = entityBB->Max.x + (vel.x > 0.0f ? vel.x : 0.0f);
+	entityExtentBB->Max.y = entityBB->Max.y + (vel.y > 0.0f ? vel.y : 0.0f);
+	entityExtentBB->Max.z = entityBB->Max.z + (vel.z > 0.0f ? vel.z : 0.0f);
+
+	IVec3_Floor(&min, &entityExtentBB->Min);
+	IVec3_Floor(&max, &entityExtentBB->Max);
+	elements = (max.x - min.x + 1) * (max.y - min.y + 1) * (max.z - min.z + 1);
+
+	if (elements > searcherCapacity) {
+		Searcher_Free();
+		searcherCapacity = elements;
+		Searcher_States  = (struct SearcherState*)Mem_Alloc(elements, sizeof(struct SearcherState), "collision search states");
+	}
+	curState = Searcher_States;
+
+	/* Order loops so that we minimise cache misses */
+	for (y = min.y; y <= max.y; y++) {
+		for (z = min.z; z <= max.z; z++) {
+			for (x = min.x; x <= max.x; x++) {
+				block = World_GetPhysicsBlock(x, y, z);
+				if (Blocks.Collide[block] != COLLIDE_SOLID) continue;
+
+				xx = (float)x; yy = (float)y; zz = (float)z;
+				blockBB.Min = Blocks.MinBB[block];
+				blockBB.Min.x += xx; blockBB.Min.y += yy; blockBB.Min.z += zz;
+				blockBB.Max = Blocks.MaxBB[block];
+				blockBB.Max.x += xx; blockBB.Max.y += yy; blockBB.Max.z += zz;
+
+				if (!AABB_Intersects(entityExtentBB, &blockBB)) continue; /* necessary for non whole blocks. (slabs) */
+				Searcher_CalcTime(&vel, entityBB, &blockBB, &tx, &ty, &tz);
+				if (tx > 1.0f || ty > 1.0f || tz > 1.0f) continue;
+
+				curState->x = (x << 3) | (block  & 0x007);
+				curState->y = (y << 4) | ((block & 0x078) >> 3);
+				curState->z = (z << 3) | ((block & 0x380) >> 7);
+				curState->tSquared = tx * tx + ty * ty + tz * tz;
+				curState++;
+			}
+		}
+	}
+
+	count = (int)(curState - Searcher_States);
+	if (count) Searcher_QuickSort(0, count - 1);
+	return count;
+}
+
+void Searcher_CalcTime(Vec3* vel, struct AABB *entityBB, struct AABB* blockBB, float* tx, float* ty, float* tz) {
+	float dx = vel->x > 0.0f ? blockBB->Min.x - entityBB->Max.x : entityBB->Min.x - blockBB->Max.x;
+	float dy = vel->y > 0.0f ? blockBB->Min.y - entityBB->Max.y : entityBB->Min.y - blockBB->Max.y;
+	float dz = vel->z > 0.0f ? blockBB->Min.z - entityBB->Max.z : entityBB->Min.z - blockBB->Max.z;
+
+	if (entityBB->Max.x >= blockBB->Min.x && entityBB->Min.x <= blockBB->Max.x) {
+		*tx = 0.0f; /* Inlined XIntersects test */
+	} else {
+		*tx = vel->x == 0.0f ? MATH_LARGENUM : Math_AbsF(dx / vel->x);
+	}
+
+	if (entityBB->Max.y >= blockBB->Min.y && entityBB->Min.y <= blockBB->Max.y) {
+		*ty = 0.0f; /* Inlined YIntersects test */
+	} else {
+		*ty = vel->y == 0.0f ? MATH_LARGENUM : Math_AbsF(dy / vel->y);
+	}
+
+	if (entityBB->Max.z >= blockBB->Min.z && entityBB->Min.z <= blockBB->Max.z) {
+		*tz = 0.0f; /* Inlined ZIntersects test */
+	} else {
+		*tz = vel->z == 0.0f ? MATH_LARGENUM : Math_AbsF(dz / vel->z);
+	}
+}
+
+void Searcher_Free(void) {
+	if (Searcher_States != searcherDefaultStates) Mem_Free(Searcher_States);
+	Searcher_States  = searcherDefaultStates;
+	searcherCapacity = SEARCHER_STATES_MIN;
+}