summaryrefslogtreecommitdiffstats
path: root/src/collision
diff options
context:
space:
mode:
Diffstat (limited to 'src/collision')
-rw-r--r--src/collision/ColBox.cpp21
-rw-r--r--src/collision/ColBox.h16
-rw-r--r--src/collision/ColLine.cpp9
-rw-r--r--src/collision/ColLine.h14
-rw-r--r--src/collision/ColModel.cpp184
-rw-r--r--src/collision/ColModel.h37
-rw-r--r--src/collision/ColPoint.cpp16
-rw-r--r--src/collision/ColPoint.h34
-rw-r--r--src/collision/ColSphere.cpp11
-rw-r--r--src/collision/ColSphere.h13
-rw-r--r--src/collision/ColTriangle.cpp41
-rw-r--r--src/collision/ColTriangle.h68
-rw-r--r--src/collision/Collision.cpp2736
-rw-r--r--src/collision/Collision.h70
-rw-r--r--src/collision/CompressedVector.h36
-rw-r--r--src/collision/TempColModels.cpp296
-rw-r--r--src/collision/TempColModels.h23
-rw-r--r--src/collision/VuCollision.cpp282
-rw-r--r--src/collision/VuCollision.h32
-rw-r--r--src/collision/vu0Collision.dsm21
-rw-r--r--src/collision/vu0Collision_1.s610
-rw-r--r--src/collision/vu0Collision_2.s191
22 files changed, 4761 insertions, 0 deletions
diff --git a/src/collision/ColBox.cpp b/src/collision/ColBox.cpp
new file mode 100644
index 00000000..53cba88b
--- /dev/null
+++ b/src/collision/ColBox.cpp
@@ -0,0 +1,21 @@
+#include "common.h"
+#include "ColBox.h"
+
+void
+CColBox::Set(const CVector &min, const CVector &max, uint8 surf, uint8 piece)
+{
+ this->min = min;
+ this->max = max;
+ this->surface = surf;
+ this->piece = piece;
+}
+
+CColBox&
+CColBox::operator=(const CColBox& other)
+{
+ min = other.min;
+ max = other.max;
+ surface = other.surface;
+ piece = other.piece;
+ return *this;
+} \ No newline at end of file
diff --git a/src/collision/ColBox.h b/src/collision/ColBox.h
new file mode 100644
index 00000000..ac2cd675
--- /dev/null
+++ b/src/collision/ColBox.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "SurfaceTable.h"
+
+struct CColBox
+{
+ CVector min;
+ CVector max;
+ uint8 surface;
+ uint8 piece;
+
+ void Set(const CVector &min, const CVector &max, uint8 surf = SURFACE_DEFAULT, uint8 piece = 0);
+ CVector GetSize(void) { return max - min; }
+
+ CColBox& operator=(const CColBox &other);
+}; \ No newline at end of file
diff --git a/src/collision/ColLine.cpp b/src/collision/ColLine.cpp
new file mode 100644
index 00000000..c6247449
--- /dev/null
+++ b/src/collision/ColLine.cpp
@@ -0,0 +1,9 @@
+#include "common.h"
+#include "ColLine.h"
+
+void
+CColLine::Set(const CVector &p0, const CVector &p1)
+{
+ this->p0 = p0;
+ this->p1 = p1;
+} \ No newline at end of file
diff --git a/src/collision/ColLine.h b/src/collision/ColLine.h
new file mode 100644
index 00000000..21587a06
--- /dev/null
+++ b/src/collision/ColLine.h
@@ -0,0 +1,14 @@
+#pragma once
+
+struct CColLine
+{
+ // NB: this has to be compatible with two CVuVectors
+ CVector p0;
+ int pad0;
+ CVector p1;
+ int pad1;
+
+ CColLine(void) { };
+ CColLine(const CVector &p0, const CVector &p1) { this->p0 = p0; this->p1 = p1; };
+ void Set(const CVector &p0, const CVector &p1);
+}; \ No newline at end of file
diff --git a/src/collision/ColModel.cpp b/src/collision/ColModel.cpp
new file mode 100644
index 00000000..650e6958
--- /dev/null
+++ b/src/collision/ColModel.cpp
@@ -0,0 +1,184 @@
+#include "common.h"
+#include "ColModel.h"
+#include "Game.h"
+
+CColModel::CColModel(void)
+{
+ numSpheres = 0;
+ spheres = nil;
+ numLines = 0;
+ lines = nil;
+ numBoxes = 0;
+ boxes = nil;
+ numTriangles = 0;
+ vertices = nil;
+ triangles = nil;
+ trianglePlanes = nil;
+ level = CGame::currLevel;
+ ownsCollisionVolumes = true;
+}
+
+CColModel::~CColModel(void)
+{
+ RemoveCollisionVolumes();
+ RemoveTrianglePlanes();
+}
+
+void
+CColModel::RemoveCollisionVolumes(void)
+{
+ if(ownsCollisionVolumes){
+ RwFree(spheres);
+ RwFree(lines);
+ RwFree(boxes);
+ RwFree(vertices);
+ RwFree(triangles);
+ }
+ numSpheres = 0;
+ numLines = 0;
+ numBoxes = 0;
+ numTriangles = 0;
+ spheres = nil;
+ lines = nil;
+ boxes = nil;
+ vertices = nil;
+ triangles = nil;
+}
+
+void
+CColModel::CalculateTrianglePlanes(void)
+{
+ // HACK: allocate space for one more element to stuff the link pointer into
+ trianglePlanes = (CColTrianglePlane*)RwMalloc(sizeof(CColTrianglePlane) * (numTriangles+1));
+ for(int i = 0; i < numTriangles; i++)
+ trianglePlanes[i].Set(vertices, triangles[i]);
+}
+
+void
+CColModel::RemoveTrianglePlanes(void)
+{
+ RwFree(trianglePlanes);
+ trianglePlanes = nil;
+}
+
+void
+CColModel::SetLinkPtr(CLink<CColModel*> *lptr)
+{
+ assert(trianglePlanes);
+ *(CLink<CColModel*>**)ALIGNPTR(&trianglePlanes[numTriangles]) = lptr;
+}
+
+CLink<CColModel*>*
+CColModel::GetLinkPtr(void)
+{
+ assert(trianglePlanes);
+ return *(CLink<CColModel*>**)ALIGNPTR(&trianglePlanes[numTriangles]);
+}
+
+void
+CColModel::GetTrianglePoint(CVector &v, int i) const
+{
+ v = vertices[i].Get();
+}
+
+CColModel&
+CColModel::operator=(const CColModel &other)
+{
+ int i;
+ int numVerts;
+
+ boundingSphere = other.boundingSphere;
+ boundingBox = other.boundingBox;
+
+ // copy spheres
+ if(other.numSpheres){
+ if(numSpheres != other.numSpheres){
+ numSpheres = other.numSpheres;
+ if(spheres)
+ RwFree(spheres);
+ spheres = (CColSphere*)RwMalloc(numSpheres*sizeof(CColSphere));
+ }
+ for(i = 0; i < numSpheres; i++)
+ spheres[i] = other.spheres[i];
+ }else{
+ numSpheres = 0;
+ if(spheres)
+ RwFree(spheres);
+ spheres = nil;
+ }
+
+ // copy lines
+ if(other.numLines){
+ if(numLines != other.numLines){
+ numLines = other.numLines;
+ if(lines)
+ RwFree(lines);
+ lines = (CColLine*)RwMalloc(numLines*sizeof(CColLine));
+ }
+ for(i = 0; i < numLines; i++)
+ lines[i] = other.lines[i];
+ }else{
+ numLines = 0;
+ if(lines)
+ RwFree(lines);
+ lines = nil;
+ }
+
+ // copy boxes
+ if(other.numBoxes){
+ if(numBoxes != other.numBoxes){
+ numBoxes = other.numBoxes;
+ if(boxes)
+ RwFree(boxes);
+ boxes = (CColBox*)RwMalloc(numBoxes*sizeof(CColBox));
+ }
+ for(i = 0; i < numBoxes; i++)
+ boxes[i] = other.boxes[i];
+ }else{
+ numBoxes = 0;
+ if(boxes)
+ RwFree(boxes);
+ boxes = nil;
+ }
+
+ // copy mesh
+ if(other.numTriangles){
+ // copy vertices
+ numVerts = 0;
+ for(i = 0; i < other.numTriangles; i++){
+ if(other.triangles[i].a > numVerts)
+ numVerts = other.triangles[i].a;
+ if(other.triangles[i].b > numVerts)
+ numVerts = other.triangles[i].b;
+ if(other.triangles[i].c > numVerts)
+ numVerts = other.triangles[i].c;
+ }
+ numVerts++;
+ if(vertices)
+ RwFree(vertices);
+ if(numVerts){
+ vertices = (CompressedVector*)RwMalloc(numVerts*sizeof(CompressedVector));
+ for(i = 0; i < numVerts; i++)
+ vertices[i] = other.vertices[i];
+ }
+
+ // copy triangles
+ if(numTriangles != other.numTriangles){
+ numTriangles = other.numTriangles;
+ if(triangles)
+ RwFree(triangles);
+ triangles = (CColTriangle*)RwMalloc(numTriangles*sizeof(CColTriangle));
+ }
+ for(i = 0; i < numTriangles; i++)
+ triangles[i] = other.triangles[i];
+ }else{
+ numTriangles = 0;
+ if(triangles)
+ RwFree(triangles);
+ triangles = nil;
+ if(vertices)
+ RwFree(vertices);
+ vertices = nil;
+ }
+ return *this;
+}
diff --git a/src/collision/ColModel.h b/src/collision/ColModel.h
new file mode 100644
index 00000000..7dcdfa4d
--- /dev/null
+++ b/src/collision/ColModel.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#include "templates.h"
+#include "ColBox.h"
+#include "ColSphere.h"
+#include "ColLine.h"
+#include "ColPoint.h"
+#include "ColTriangle.h"
+
+struct CColModel
+{
+ CColSphere boundingSphere;
+ CColBox boundingBox;
+ int16 numSpheres;
+ int16 numLines;
+ int16 numBoxes;
+ int16 numTriangles;
+ int32 level;
+ bool ownsCollisionVolumes; // missing on PS2
+ CColSphere *spheres;
+ CColLine *lines;
+ CColBox *boxes;
+ CompressedVector *vertices;
+ CColTriangle *triangles;
+ CColTrianglePlane *trianglePlanes;
+
+ CColModel(void);
+ ~CColModel(void);
+ void RemoveCollisionVolumes(void);
+ void CalculateTrianglePlanes(void);
+ void RemoveTrianglePlanes(void);
+ CLink<CColModel*> *GetLinkPtr(void);
+ void SetLinkPtr(CLink<CColModel*>*);
+ void GetTrianglePoint(CVector &v, int i) const;
+
+ CColModel& operator=(const CColModel& other);
+}; \ No newline at end of file
diff --git a/src/collision/ColPoint.cpp b/src/collision/ColPoint.cpp
new file mode 100644
index 00000000..fbf9e8c3
--- /dev/null
+++ b/src/collision/ColPoint.cpp
@@ -0,0 +1,16 @@
+#include "common.h"
+#include "ColPoint.h"
+
+CColPoint&
+CColPoint::operator=(const CColPoint &other)
+{
+ point = other.point;
+ normal = other.normal;
+ surfaceA = other.surfaceA;
+ pieceA = other.pieceA;
+ surfaceB = other.surfaceB;
+ pieceB = other.pieceB;
+
+ // no depth?
+ return *this;
+}
diff --git a/src/collision/ColPoint.h b/src/collision/ColPoint.h
new file mode 100644
index 00000000..a15b2345
--- /dev/null
+++ b/src/collision/ColPoint.h
@@ -0,0 +1,34 @@
+#pragma once
+
+struct CColPoint
+{
+ CVector point;
+ int pad1;
+ // the surface normal on the surface of point
+ CVector normal;
+ int pad2;
+ uint8 surfaceA;
+ uint8 pieceA;
+ uint8 surfaceB;
+ uint8 pieceB;
+ float depth;
+
+ const CVector &GetNormal() { return normal; }
+ float GetDepth() { return depth; }
+ void Set(float depth, uint8 surfA, uint8 pieceA, uint8 surfB, uint8 pieceB) {
+ this->depth = depth;
+ this->surfaceA = surfA;
+ this->pieceA = pieceA;
+ this->surfaceB = surfB;
+ this->pieceB = pieceB;
+ }
+ void Set(uint8 surfA, uint8 pieceA, uint8 surfB, uint8 pieceB) {
+ this->surfaceA = surfA;
+ this->pieceA = pieceA;
+ this->surfaceB = surfB;
+ this->pieceB = pieceB;
+ }
+
+ CColPoint &operator=(const CColPoint &other);
+};
+
diff --git a/src/collision/ColSphere.cpp b/src/collision/ColSphere.cpp
new file mode 100644
index 00000000..9aac01e0
--- /dev/null
+++ b/src/collision/ColSphere.cpp
@@ -0,0 +1,11 @@
+#include "common.h"
+#include "ColSphere.h"
+
+void
+CColSphere::Set(float radius, const CVector &center, uint8 surf, uint8 piece)
+{
+ this->radius = radius;
+ this->center = center;
+ this->surface = surf;
+ this->piece = piece;
+} \ No newline at end of file
diff --git a/src/collision/ColSphere.h b/src/collision/ColSphere.h
new file mode 100644
index 00000000..70e29763
--- /dev/null
+++ b/src/collision/ColSphere.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "SurfaceTable.h"
+
+struct CColSphere
+{
+ // NB: this has to be compatible with a CVuVector
+ CVector center;
+ float radius;
+ uint8 surface;
+ uint8 piece;
+ void Set(float radius, const CVector &center, uint8 surf = SURFACE_DEFAULT, uint8 piece = 0);
+}; \ No newline at end of file
diff --git a/src/collision/ColTriangle.cpp b/src/collision/ColTriangle.cpp
new file mode 100644
index 00000000..9120fcff
--- /dev/null
+++ b/src/collision/ColTriangle.cpp
@@ -0,0 +1,41 @@
+#include "common.h"
+#include "ColTriangle.h"
+
+void
+CColTriangle::Set(const CompressedVector *, int a, int b, int c, uint8 surf, uint8 piece)
+{
+ this->a = a;
+ this->b = b;
+ this->c = c;
+ this->surface = surf;
+}
+
+#ifdef VU_COLLISION
+void
+CColTrianglePlane::Set(const CVector &va, const CVector &vb, const CVector &vc)
+{
+ CVector norm = CrossProduct(vc-va, vb-va);
+ norm.Normalise();
+ float d = DotProduct(norm, va);
+ normal.x = norm.x*4096.0f;
+ normal.y = norm.y*4096.0f;
+ normal.z = norm.z*4096.0f;
+ dist = d*128.0f;
+}
+#else
+void
+CColTrianglePlane::Set(const CVector &va, const CVector &vb, const CVector &vc)
+{
+ normal = CrossProduct(vc-va, vb-va);
+ normal.Normalise();
+ dist = DotProduct(normal, va);
+ CVector an(Abs(normal.x), Abs(normal.y), Abs(normal.z));
+ // find out largest component and its direction
+ if(an.x > an.y && an.x > an.z)
+ dir = normal.x < 0.0f ? DIR_X_NEG : DIR_X_POS;
+ else if(an.y > an.z)
+ dir = normal.y < 0.0f ? DIR_Y_NEG : DIR_Y_POS;
+ else
+ dir = normal.z < 0.0f ? DIR_Z_NEG : DIR_Z_POS;
+}
+#endif \ No newline at end of file
diff --git a/src/collision/ColTriangle.h b/src/collision/ColTriangle.h
new file mode 100644
index 00000000..9e918e38
--- /dev/null
+++ b/src/collision/ColTriangle.h
@@ -0,0 +1,68 @@
+#pragma once
+
+#include "CompressedVector.h"
+
+enum Direction {
+ DIR_X_POS,
+ DIR_X_NEG,
+ DIR_Y_POS,
+ DIR_Y_NEG,
+ DIR_Z_POS,
+ DIR_Z_NEG,
+};
+
+struct CColTriangle
+{
+ uint16 a;
+ uint16 b;
+ uint16 c;
+ uint8 surface;
+
+ void Set(const CompressedVector *v, int a, int b, int c, uint8 surf, uint8 piece);
+};
+
+struct CColTrianglePlane
+{
+#ifdef VU_COLLISION
+ CompressedVector normal;
+ int16 dist;
+
+ void Set(const CVector &va, const CVector &vb, const CVector &vc);
+ void Set(const CompressedVector *v, CColTriangle &tri) { Set(v[tri.a].Get(), v[tri.b].Get(), v[tri.c].Get()); }
+ void GetNormal(CVector &n) const { n.x = normal.x/4096.0f; n.y = normal.y/4096.0f; n.z = normal.z/4096.0f; }
+ float CalcPoint(const CVector &v) const { CVector n; GetNormal(n); return DotProduct(n, v) - dist/128.0f; };
+#ifdef GTA_PS2
+ void Unpack(uint128 &qword) const {
+ __asm__ volatile (
+ "lh $8, 0(%1)\n"
+ "lh $9, 2(%1)\n"
+ "lh $10, 4(%1)\n"
+ "lh $11, 6(%1)\n"
+ "pextlw $10, $8\n"
+ "pextlw $11, $9\n"
+ "pextlw $2, $11, $10\n"
+ "sq $2, %0\n"
+ : "=m" (qword)
+ : "r" (this)
+ : "$8", "$9", "$10", "$11", "$2"
+ );
+ }
+#else
+ void Unpack(int32 *qword) const {
+ qword[0] = normal.x;
+ qword[1] = normal.y;
+ qword[2] = normal.z;
+ qword[3] = dist;
+ }
+#endif
+#else
+ CVector normal;
+ float dist;
+ uint8 dir;
+
+ void Set(const CVector &va, const CVector &vb, const CVector &vc);
+ void Set(const CompressedVector *v, CColTriangle &tri) { Set(v[tri.a].Get(), v[tri.b].Get(), v[tri.c].Get()); }
+ void GetNormal(CVector &n) const { n = normal; }
+ float CalcPoint(const CVector &v) const { return DotProduct(normal, v) - dist; };
+#endif
+}; \ No newline at end of file
diff --git a/src/collision/Collision.cpp b/src/collision/Collision.cpp
new file mode 100644
index 00000000..41997e32
--- /dev/null
+++ b/src/collision/Collision.cpp
@@ -0,0 +1,2736 @@
+#include "common.h"
+
+#include "VuVector.h"
+#include "main.h"
+#include "Lists.h"
+#include "Game.h"
+#include "Zones.h"
+#include "General.h"
+#include "ZoneCull.h"
+#include "World.h"
+#include "Entity.h"
+#include "Train.h"
+#include "Streaming.h"
+#include "Pad.h"
+#include "DMAudio.h"
+#include "Population.h"
+#include "FileLoader.h"
+#include "Replay.h"
+#include "CutsceneMgr.h"
+#include "RenderBuffer.h"
+#include "SurfaceTable.h"
+#include "Lines.h"
+#include "Collision.h"
+#include "Frontend.h"
+
+#ifdef VU_COLLISION
+#include "VuCollision.h"
+
+inline int
+GetVUresult(void)
+{
+#ifdef GTA_PS2
+ int ret;
+ __asm__ volatile (
+ "cfc2.i %0,vi01\n" // .i important! wait for VU0 to finish
+ : "=r" (ret)
+ );
+ return ret;
+#else
+ return vi01;
+#endif
+}
+
+inline int
+GetVUresult(CVuVector &point, CVuVector &normal, float &dist)
+{
+#ifdef GTA_PS2
+ int ret;
+ __asm__ volatile (
+ "cfc2.i %0,vi01\n" // .i important! wait for VU0 to finish
+ "sqc2 vf01,(%1)\n"
+ "sqc2 vf02,(%2)\n"
+ "qmfc2 $12,vf03\n"
+ "sw $12,(%3)\n"
+ : "=r" (ret)
+ : "r" (&point), "r" (&normal), "r" (&dist)
+ : "$12"
+ );
+ return ret;
+#else
+ point = vf01;
+ normal = vf02;
+ dist = vf03.x;
+ return vi01;
+#endif
+}
+
+#endif
+
+eLevelName CCollision::ms_collisionInMemory;
+CLinkList<CColModel*> CCollision::ms_colModelCache;
+
+void
+CCollision::Init(void)
+{
+ ms_colModelCache.Init(NUMCOLCACHELINKS);
+ ms_collisionInMemory = LEVEL_GENERIC;
+}
+
+void
+CCollision::Shutdown(void)
+{
+ ms_colModelCache.Shutdown();
+}
+
+void
+CCollision::Update(void)
+{
+ CVector playerCoors;
+ playerCoors = FindPlayerCoors();
+ eLevelName level = CTheZones::m_CurrLevel;
+ bool forceLevelChange = false;
+
+ if(CTimer::GetTimeInMilliseconds() < 2000 || CCutsceneMgr::IsCutsceneProcessing())
+ return;
+
+ // hardcode a level if there are no zones
+ if(level == LEVEL_GENERIC){
+ if(CGame::currLevel == LEVEL_INDUSTRIAL &&
+ playerCoors.x < 400.0f){
+ level = LEVEL_COMMERCIAL;
+ forceLevelChange = true;
+ }else if(CGame::currLevel == LEVEL_SUBURBAN &&
+ playerCoors.x > -450.0f && playerCoors.y < -1400.0f){
+ level = LEVEL_COMMERCIAL;
+ forceLevelChange = true;
+ }else{
+ if(playerCoors.x > 800.0f){
+ level = LEVEL_INDUSTRIAL;
+ forceLevelChange = true;
+ }else if(playerCoors.x < -800.0f){
+ level = LEVEL_SUBURBAN;
+ forceLevelChange = true;
+ }
+ }
+ }
+ if(level != LEVEL_GENERIC && level != CGame::currLevel)
+ CGame::currLevel = level;
+ if(ms_collisionInMemory != CGame::currLevel)
+ LoadCollisionWhenINeedIt(forceLevelChange);
+ CStreaming::HaveAllBigBuildingsLoaded(CGame::currLevel);
+}
+
+eLevelName
+GetCollisionInSectorList(CPtrList &list)
+{
+ CPtrNode *node;
+ CEntity *e;
+ int level;
+
+ for(node = list.first; node; node = node->next){
+ e = (CEntity*)node->item;
+ level = CModelInfo::GetModelInfo(e->GetModelIndex())->GetColModel()->level;
+ if(level != LEVEL_GENERIC)
+ return (eLevelName)level;
+ }
+ return LEVEL_GENERIC;
+}
+
+// Get a level this sector is in based on collision models
+eLevelName
+GetCollisionInSector(CSector &sect)
+{
+ int level;
+
+ level = GetCollisionInSectorList(sect.m_lists[ENTITYLIST_BUILDINGS]);
+ if(level == LEVEL_GENERIC)
+ level = GetCollisionInSectorList(sect.m_lists[ENTITYLIST_BUILDINGS_OVERLAP]);
+ if(level == LEVEL_GENERIC)
+ level = GetCollisionInSectorList(sect.m_lists[ENTITYLIST_OBJECTS]);
+ if(level == LEVEL_GENERIC)
+ level = GetCollisionInSectorList(sect.m_lists[ENTITYLIST_OBJECTS_OVERLAP]);
+ if(level == LEVEL_GENERIC)
+ level = GetCollisionInSectorList(sect.m_lists[ENTITYLIST_DUMMIES]);
+ if(level == LEVEL_GENERIC)
+ level = GetCollisionInSectorList(sect.m_lists[ENTITYLIST_DUMMIES_OVERLAP]);
+ return (eLevelName)level;
+}
+
+void
+CCollision::LoadCollisionWhenINeedIt(bool forceChange)
+{
+ eLevelName level, l;
+ bool multipleLevels;
+ CVector playerCoors;
+ CVehicle *veh;
+ CEntryInfoNode *ei;
+ int sx, sy;
+ int xmin, xmax, ymin, ymax;
+ int x, y;
+
+ level = LEVEL_GENERIC;
+
+ playerCoors = FindPlayerCoors();
+ sx = CWorld::GetSectorIndexX(playerCoors.x);
+ sy = CWorld::GetSectorIndexY(playerCoors.y);
+ multipleLevels = false;
+
+ veh = FindPlayerVehicle();
+ if(veh && veh->IsTrain()){
+ if(((CTrain*)veh)->m_nDoorState != TRAIN_DOOR_OPEN)
+ return;
+ }else if(playerCoors.z < -4.0f && !CCullZones::DoINeedToLoadCollision())
+ return;
+
+ // Figure out whose level's collisions we're most likely to be interested in
+ if(!forceChange){
+ if(veh && veh->IsBoat()){
+ // on water we expect to be between levels
+ multipleLevels = true;
+ }else{
+ xmin = Max(sx - 1, 0);
+ xmax = Min(sx + 1, NUMSECTORS_X-1);
+ ymin = Max(sy - 1, 0);
+ ymax = Min(sy + 1, NUMSECTORS_Y-1);
+
+ for(x = xmin; x <= xmax; x++)
+ for(y = ymin; y <= ymax; y++){
+ l = GetCollisionInSector(*CWorld::GetSector(x, y));
+ if(l != LEVEL_GENERIC){
+ if(level == LEVEL_GENERIC)
+ level = l;
+ if(level != l)
+ multipleLevels = true;
+ }
+ }
+ }
+
+ if(multipleLevels && veh && veh->IsBoat())
+ for(ei = veh->m_entryInfoList.first; ei; ei = ei->next){
+ level = GetCollisionInSector(*ei->sector);
+ if(level != LEVEL_GENERIC)
+ break;
+ }
+ }
+
+ if (level == CGame::currLevel || forceChange) {
+#ifdef FIX_BUGS
+ CTimer::Suspend();
+#else
+ CTimer::Stop();
+#endif
+ ISLAND_LOADING_IS(LOW)
+ {
+ DMAudio.SetEffectsFadeVol(0);
+ CPad::StopPadsShaking();
+ LoadCollisionScreen(CGame::currLevel);
+ DMAudio.Service();
+ }
+
+ CPopulation::DealWithZoneChange(ms_collisionInMemory, CGame::currLevel, false);
+
+ ISLAND_LOADING_ISNT(HIGH)
+ {
+ CStreaming::RemoveIslandsNotUsed(LEVEL_INDUSTRIAL);
+ CStreaming::RemoveIslandsNotUsed(LEVEL_COMMERCIAL);
+ CStreaming::RemoveIslandsNotUsed(LEVEL_SUBURBAN);
+ }
+ ISLAND_LOADING_IS(LOW)
+ {
+ CStreaming::RemoveBigBuildings(LEVEL_INDUSTRIAL);
+ CStreaming::RemoveBigBuildings(LEVEL_COMMERCIAL);
+ CStreaming::RemoveBigBuildings(LEVEL_SUBURBAN);
+ CModelInfo::RemoveColModelsFromOtherLevels(CGame::currLevel);
+ CStreaming::RemoveUnusedModelsInLoadedList();
+ CGame::TidyUpMemory(true, true);
+ CFileLoader::LoadCollisionFromDatFile(CGame::currLevel);
+ }
+
+ ms_collisionInMemory = CGame::currLevel;
+ CReplay::EmptyReplayBuffer();
+ ISLAND_LOADING_IS(LOW)
+ {
+ if (CGame::currLevel != LEVEL_GENERIC)
+ LoadSplash(GetLevelSplashScreen(CGame::currLevel));
+ CStreaming::RemoveUnusedBigBuildings(CGame::currLevel);
+ CStreaming::RemoveUnusedBuildings(CGame::currLevel);
+ CStreaming::RequestBigBuildings(CGame::currLevel);
+ }
+#ifdef NO_ISLAND_LOADING
+ else if (CMenuManager::m_PrefsIslandLoading == CMenuManager::ISLAND_LOADING_MEDIUM)
+ CStreaming::RequestIslands(CGame::currLevel);
+#endif
+ CStreaming::LoadAllRequestedModels(true);
+
+ ISLAND_LOADING_IS(LOW)
+ {
+ CStreaming::HaveAllBigBuildingsLoaded(CGame::currLevel);
+
+ CGame::TidyUpMemory(true, true);
+ }
+#ifdef FIX_BUGS
+ CTimer::Resume();
+#else
+ CTimer::Update();
+#endif
+ ISLAND_LOADING_IS(LOW)
+ DMAudio.SetEffectsFadeVol(127);
+ }
+}
+
+#ifdef NO_ISLAND_LOADING
+bool CCollision::bAlreadyLoaded = false;
+#endif
+void
+CCollision::SortOutCollisionAfterLoad(void)
+{
+ if(ms_collisionInMemory == CGame::currLevel)
+ return;
+ ISLAND_LOADING_IS(LOW)
+ CModelInfo::RemoveColModelsFromOtherLevels(CGame::currLevel);
+
+ if (CGame::currLevel != LEVEL_GENERIC) {
+#ifdef NO_ISLAND_LOADING
+ if (CMenuManager::m_PrefsIslandLoading != CMenuManager::ISLAND_LOADING_LOW) {
+ if (bAlreadyLoaded) {
+ ms_collisionInMemory = CGame::currLevel;
+ return;
+ }
+ bAlreadyLoaded = true;
+ CFileLoader::LoadCollisionFromDatFile(LEVEL_INDUSTRIAL);
+ CFileLoader::LoadCollisionFromDatFile(LEVEL_COMMERCIAL);
+ CFileLoader::LoadCollisionFromDatFile(LEVEL_SUBURBAN);
+ } else
+#endif
+ CFileLoader::LoadCollisionFromDatFile(CGame::currLevel);
+ if(!CGame::playingIntro)
+ LoadSplash(GetLevelSplashScreen(CGame::currLevel));
+ }
+ ms_collisionInMemory = CGame::currLevel;
+ CGame::TidyUpMemory(true, false);
+}
+
+void
+CCollision::LoadCollisionScreen(eLevelName level)
+{
+ static Const char *levelNames[4] = {
+ "",
+ "IND_ZON",
+ "COM_ZON",
+ "SUB_ZON"
+ };
+
+ // Why twice?
+ LoadingIslandScreen(levelNames[level]);
+ LoadingIslandScreen(levelNames[level]);
+}
+
+//
+// Test
+//
+
+
+bool
+CCollision::TestSphereSphere(const CColSphere &s1, const CColSphere &s2)
+{
+ float d = s1.radius + s2.radius;
+ return (s1.center - s2.center).MagnitudeSqr() < d*d;
+}
+
+bool
+CCollision::TestSphereBox(const CColSphere &sph, const CColBox &box)
+{
+ if(sph.center.x + sph.radius < box.min.x) return false;
+ if(sph.center.x - sph.radius > box.max.x) return false;
+ if(sph.center.y + sph.radius < box.min.y) return false;
+ if(sph.center.y - sph.radius > box.max.y) return false;
+ if(sph.center.z + sph.radius < box.min.z) return false;
+ if(sph.center.z - sph.radius > box.max.z) return false;
+ return true;
+}
+
+bool
+CCollision::TestLineBox(const CColLine &line, const CColBox &box)
+{
+ float t, x, y, z;
+ // If either line point is in the box, we have a collision
+ if(line.p0.x > box.min.x && line.p0.x < box.max.x &&
+ line.p0.y > box.min.y && line.p0.y < box.max.y &&
+ line.p0.z > box.min.z && line.p0.z < box.max.z)
+ return true;
+ if(line.p1.x > box.min.x && line.p1.x < box.max.x &&
+ line.p1.y > box.min.y && line.p1.y < box.max.y &&
+ line.p1.z > box.min.z && line.p1.z < box.max.z)
+ return true;
+
+ // check if points are on opposite sides of min x plane
+ if((box.min.x - line.p1.x) * (box.min.x - line.p0.x) < 0.0f){
+ // parameter along line where we intersect
+ t = (box.min.x - line.p0.x) / (line.p1.x - line.p0.x);
+ // y of intersection
+ y = line.p0.y + (line.p1.y - line.p0.y)*t;
+ if(y > box.min.y && y < box.max.y){
+ // z of intersection
+ z = line.p0.z + (line.p1.z - line.p0.z)*t;
+ if(z > box.min.z && z < box.max.z)
+ return true;
+ }
+ }
+
+ // same test with max x plane
+ if((line.p1.x - box.max.x) * (line.p0.x - box.max.x) < 0.0f){
+ t = (line.p0.x - box.max.x) / (line.p0.x - line.p1.x);
+ y = line.p0.y + (line.p1.y - line.p0.y)*t;
+ if(y > box.min.y && y < box.max.y){
+ z = line.p0.z + (line.p1.z - line.p0.z)*t;
+ if(z > box.min.z && z < box.max.z)
+ return true;
+ }
+ }
+
+ // min y plne
+ if((box.min.y - line.p0.y) * (box.min.y - line.p1.y) < 0.0f){
+ t = (box.min.y - line.p0.y) / (line.p1.y - line.p0.y);
+ x = line.p0.x + (line.p1.x - line.p0.x)*t;
+ if(x > box.min.x && x < box.max.x){
+ z = line.p0.z + (line.p1.z - line.p0.z)*t;
+ if(z > box.min.z && z < box.max.z)
+ return true;
+ }
+ }
+
+ // max y plane
+ if((line.p0.y - box.max.y) * (line.p1.y - box.max.y) < 0.0f){
+ t = (line.p0.y - box.max.y) / (line.p0.y - line.p1.y);
+ x = line.p0.x + (line.p1.x - line.p0.x)*t;
+ if(x > box.min.x && x < box.max.x){
+ z = line.p0.z + (line.p1.z - line.p0.z)*t;
+ if(z > box.min.z && z < box.max.z)
+ return true;
+ }
+ }
+
+ // min z plne
+ if((box.min.z - line.p0.z) * (box.min.z - line.p1.z) < 0.0f){
+ t = (box.min.z - line.p0.z) / (line.p1.z - line.p0.z);
+ x = line.p0.x + (line.p1.x - line.p0.x)*t;
+ if(x > box.min.x && x < box.max.x){
+ y = line.p0.y + (line.p1.y - line.p0.y)*t;
+ if(y > box.min.y && y < box.max.y)
+ return true;
+ }
+ }
+
+ // max z plane
+ if((line.p0.z - box.max.z) * (line.p1.z - box.max.z) < 0.0f){
+ t = (line.p0.z - box.max.z) / (line.p0.z - line.p1.z);
+ x = line.p0.x + (line.p1.x - line.p0.x)*t;
+ if(x > box.min.x && x < box.max.x){
+ y = line.p0.y + (line.p1.y - line.p0.y)*t;
+ if(y > box.min.y && y < box.max.y)
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+CCollision::TestVerticalLineBox(const CColLine &line, const CColBox &box)
+{
+ if(line.p0.x <= box.min.x) return false;
+ if(line.p0.y <= box.min.y) return false;
+ if(line.p0.x >= box.max.x) return false;
+ if(line.p0.y >= box.max.y) return false;
+ if(line.p0.z < line.p1.z){
+ if(line.p0.z > box.max.z) return false;
+ if(line.p1.z < box.min.z) return false;
+ }else{
+ if(line.p1.z > box.max.z) return false;
+ if(line.p0.z < box.min.z) return false;
+ }
+ return true;
+}
+
+bool
+CCollision::TestLineTriangle(const CColLine &line, const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane)
+{
+#ifdef VU_COLLISION
+ // not used in favour of optimized loops
+ VuTriangle vutri;
+ verts[tri.a].Unpack(vutri.v0);
+ verts[tri.b].Unpack(vutri.v1);
+ verts[tri.c].Unpack(vutri.v2);
+ plane.Unpack(vutri.plane);
+
+ LineToTriangleCollisionCompressed(*(CVuVector*)&line.p0, *(CVuVector*)&line.p1, vutri);
+
+ if(GetVUresult())
+ return true;
+ return false;
+#else
+ float t;
+ CVector normal;
+ plane.GetNormal(normal);
+
+ // if points are on the same side, no collision
+ if(plane.CalcPoint(line.p0) * plane.CalcPoint(line.p1) > 0.0f)
+ return false;
+
+ // intersection parameter on line
+ t = -plane.CalcPoint(line.p0) / DotProduct(line.p1 - line.p0, normal);
+ // find point of intersection
+ CVector p = line.p0 + (line.p1-line.p0)*t;
+
+ const CVector &va = verts[tri.a].Get();
+ const CVector &vb = verts[tri.b].Get();
+ const CVector &vc = verts[tri.c].Get();
+ CVector2D vec1, vec2, vec3, vect;
+
+ // We do the test in 2D. With the plane direction we
+ // can figure out how to project the vectors.
+ // normal = (c-a) x (b-a)
+ switch(plane.dir){
+ case DIR_X_POS:
+ vec1.x = va.y; vec1.y = va.z;
+ vec2.x = vc.y; vec2.y = vc.z;
+ vec3.x = vb.y; vec3.y = vb.z;
+ vect.x = p.y; vect.y = p.z;
+ break;
+ case DIR_X_NEG:
+ vec1.x = va.y; vec1.y = va.z;
+ vec2.x = vb.y; vec2.y = vb.z;
+ vec3.x = vc.y; vec3.y = vc.z;
+ vect.x = p.y; vect.y = p.z;
+ break;
+ case DIR_Y_POS:
+ vec1.x = va.z; vec1.y = va.x;
+ vec2.x = vc.z; vec2.y = vc.x;
+ vec3.x = vb.z; vec3.y = vb.x;
+ vect.x = p.z; vect.y = p.x;
+ break;
+ case DIR_Y_NEG:
+ vec1.x = va.z; vec1.y = va.x;
+ vec2.x = vb.z; vec2.y = vb.x;
+ vec3.x = vc.z; vec3.y = vc.x;
+ vect.x = p.z; vect.y = p.x;
+ break;
+ case DIR_Z_POS:
+ vec1.x = va.x; vec1.y = va.y;
+ vec2.x = vc.x; vec2.y = vc.y;
+ vec3.x = vb.x; vec3.y = vb.y;
+ vect.x = p.x; vect.y = p.y;
+ break;
+ case DIR_Z_NEG:
+ vec1.x = va.x; vec1.y = va.y;
+ vec2.x = vb.x; vec2.y = vb.y;
+ vec3.x = vc.x; vec3.y = vc.y;
+ vect.x = p.x; vect.y = p.y;
+ break;
+ default:
+ assert(0);
+ }
+ // This is our triangle:
+ // 3-------2
+ // \ P /
+ // \ /
+ // \ /
+ // 1
+ // We can use the "2d cross product" to check on which side
+ // a vector is of another. Test is true if point is inside of all edges.
+ if(CrossProduct2D(vec2-vec1, vect-vec1) < 0.0f) return false;
+ if(CrossProduct2D(vec3-vec1, vect-vec1) > 0.0f) return false;
+ if(CrossProduct2D(vec3-vec2, vect-vec2) < 0.0f) return false;
+ return true;
+#endif
+}
+
+// Test if line segment intersects with sphere.
+// If the first point is inside the sphere this test does not register a collision!
+// The code is reversed from the original code and rather ugly, see Process for a clear version.
+// TODO: actually rewrite this mess
+bool
+CCollision::TestLineSphere(const CColLine &line, const CColSphere &sph)
+{
+ CVector v01 = line.p1 - line.p0; // vector from p0 to p1
+ CVector v0c = sph.center - line.p0; // vector from p0 to center
+ float linesq = v01.MagnitudeSqr();
+ // I leave in the strange -2 factors even though they serve no real purpose
+ float projline = -2.0f * DotProduct(v01, v0c); // project v0c onto line
+ // Square of tangent from p0 multiplied by line length so we can compare with projline.
+ // The length of the tangent would be this: Sqrt((c-p0)^2 - r^2).
+ // Negative if p0 is inside the sphere! This breaks the test!
+ float tansq = 4.0f * linesq *
+ (sph.center.MagnitudeSqr() - 2.0f*DotProduct(sph.center, line.p0) + line.p0.MagnitudeSqr() - sph.radius*sph.radius);
+ float diffsq = projline*projline - tansq;
+ // if diffsq < 0 that means the line is a passant, so no intersection
+ if(diffsq < 0.0f)
+ return false;
+ // projline (negative in GTA for some reason) is the point on the line
+ // in the middle of the two intersection points (startin from p0).
+ // Sqrt(diffsq) somehow works out to be the distance from that
+ // midpoint to the intersection points.
+ // So subtract that and get rid of the awkward scaling:
+ float f = (-projline - Sqrt(diffsq)) / (2.0f*linesq);
+ // f should now be in range [0, 1] for [p0, p1]
+ return f >= 0.0f && f <= 1.0f;
+}
+
+bool
+CCollision::TestSphereTriangle(const CColSphere &sphere,
+ const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane)
+{
+#ifdef VU_COLLISION
+ // not used in favour of optimized loops
+ VuTriangle vutri;
+ verts[tri.a].Unpack(vutri.v0);
+ verts[tri.b].Unpack(vutri.v1);
+ verts[tri.c].Unpack(vutri.v2);
+ plane.Unpack(vutri.plane);
+
+ SphereToTriangleCollisionCompressed(*(CVuVector*)&sphere, vutri);
+
+ if(GetVUresult())
+ return true;
+ return false;
+#else
+ // If sphere and plane don't intersect, no collision
+ float planedist = plane.CalcPoint(sphere.center);
+ if(Abs(planedist) > sphere.radius)
+ return false;
+
+ const CVector &va = verts[tri.a].Get();
+ const CVector &vb = verts[tri.b].Get();
+ const CVector &vc = verts[tri.c].Get();
+
+ // calculate two orthogonal basis vectors for the triangle
+ CVector vec2 = vb - va;
+ float len = vec2.Magnitude();
+ vec2 = vec2 * (1.0f/len);
+ CVector normal;
+ plane.GetNormal(normal);
+ CVector vec1 = CrossProduct(vec2, normal);
+
+ // We know A has local coordinate [0,0] and B has [0,len].
+ // Now calculate coordinates on triangle for these two vectors:
+ CVector vac = vc - va;
+ CVector vas = sphere.center - va;
+ CVector2D b(0.0f, len);
+ CVector2D c(DotProduct(vec1, vac), DotProduct(vec2, vac));
+ CVector2D s(DotProduct(vec1, vas), DotProduct(vec2, vas));
+
+ // The three triangle lines partition the space into 6 sectors,
+ // find out in which the center lies.
+ int insideAB = CrossProduct2D(s, b) >= 0.0f;
+ int insideAC = CrossProduct2D(c, s) >= 0.0f;
+ int insideBC = CrossProduct2D(s-b, c-b) >= 0.0f;
+
+ int testcase = insideAB + insideAC + insideBC;
+ float dist = 0.0f;
+ switch(testcase){
+ case 1:
+ // closest to a vertex
+ if(insideAB) dist = (sphere.center - vc).Magnitude();
+ else if(insideAC) dist = (sphere.center - vb).Magnitude();
+ else if(insideBC) dist = (sphere.center - va).Magnitude();
+ else assert(0);
+ break;
+ case 2:
+ // closest to an edge
+ // looks like original game as DistToLine manually inlined
+ if(!insideAB) dist = DistToLine(&va, &vb, &sphere.center);
+ else if(!insideAC) dist = DistToLine(&va, &vc, &sphere.center);
+ else if(!insideBC) dist = DistToLine(&vb, &vc, &sphere.center);
+ else assert(0);
+ break;
+ case 3:
+ // center is in triangle
+ dist = Abs(planedist);
+ break;
+ default:
+ assert(0);
+ }
+
+ return dist < sphere.radius;
+#endif
+}
+
+bool
+CCollision::TestLineOfSight(const CColLine &line, const CMatrix &matrix, CColModel &model, bool ignoreSeeThrough)
+{
+#ifdef VU_COLLISION
+ CMatrix matTransform;
+ int i;
+
+ // transform line to model space
+ Invert(matrix, matTransform);
+ CVuVector newline[2];
+ TransformPoints(newline, 2, matTransform, (RwV3d*)&line.p0, sizeof(CColLine)/2);
+
+ // If we don't intersect with the bounding box, no chance on the rest
+ if(!TestLineBox(*(CColLine*)newline, model.boundingBox))
+ return false;
+
+ for(i = 0; i < model.numSpheres; i++){
+ if(ignoreSeeThrough && IsSeeThrough(model.spheres[i].surface)) continue;
+ if(TestLineSphere(*(CColLine*)newline, model.spheres[i]))
+ return true;
+ }
+
+ for(i = 0; i < model.numBoxes; i++){
+ if(ignoreSeeThrough && IsSeeThrough(model.boxes[i].surface)) continue;
+ if(TestLineBox(*(CColLine*)newline, model.boxes[i]))
+ return true;
+ }
+
+ CalculateTrianglePlanes(&model);
+ int lastTest = -1;
+ VuTriangle vutri;
+ for(i = 0; i < model.numTriangles; i++){
+ if(ignoreSeeThrough && IsSeeThrough(model.triangles[i].surface)) continue;
+
+ CColTriangle *tri = &model.triangles[i];
+ model.vertices[tri->a].Unpack(vutri.v0);
+ model.vertices[tri->b].Unpack(vutri.v1);
+ model.vertices[tri->c].Unpack(vutri.v2);
+ model.trianglePlanes[i].Unpack(vutri.plane);
+
+ LineToTriangleCollisionCompressed(newline[0], newline[1], vutri);
+ lastTest = i;
+ break;
+ }
+#ifdef FIX_BUGS
+ // no need to check first again
+ i++;
+#endif
+ for(; i < model.numTriangles; i++){
+ if(ignoreSeeThrough && IsSeeThrough(model.triangles[i].surface)) continue;
+
+ CColTriangle *tri = &model.triangles[i];
+ model.vertices[tri->a].Unpack(vutri.v0);
+ model.vertices[tri->b].Unpack(vutri.v1);
+ model.vertices[tri->c].Unpack(vutri.v2);
+ model.trianglePlanes[i].Unpack(vutri.plane);
+
+ if(GetVUresult())
+ return true;
+
+ LineToTriangleCollisionCompressed(newline[0], newline[1], vutri);
+ lastTest = i;
+
+ }
+ if(lastTest != -1 && GetVUresult())
+ return true;
+
+ return false;
+#else
+ static CMatrix matTransform;
+ int i;
+
+ // transform line to model space
+ Invert(matrix, matTransform);
+ CColLine newline(matTransform * line.p0, matTransform * line.p1);
+
+ // If we don't intersect with the bounding box, no chance on the rest
+ if(!TestLineBox(newline, model.boundingBox))
+ return false;
+
+ for(i = 0; i < model.numSpheres; i++){
+ if(ignoreSeeThrough && IsSeeThrough(model.spheres[i].surface)) continue;
+ if(TestLineSphere(newline, model.spheres[i]))
+ return true;
+ }
+
+ for(i = 0; i < model.numBoxes; i++){
+ if(ignoreSeeThrough && IsSeeThrough(model.boxes[i].surface)) continue;
+ if(TestLineBox(newline, model.boxes[i]))
+ return true;
+ }
+
+ CalculateTrianglePlanes(&model);
+ for(i = 0; i < model.numTriangles; i++){
+ if(ignoreSeeThrough && IsSeeThrough(model.triangles[i].surface)) continue;
+ if(TestLineTriangle(newline, model.vertices, model.triangles[i], model.trianglePlanes[i]))
+ return true;
+ }
+
+ return false;
+#endif
+}
+
+
+//
+// Process
+//
+
+// For Spheres mindist is the squared distance to its center
+// For Lines mindist is between [0,1]
+
+bool
+CCollision::ProcessSphereSphere(const CColSphere &s1, const CColSphere &s2, CColPoint &point, float &mindistsq)
+{
+ CVector dist = s1.center - s2.center;
+ float d = dist.Magnitude() - s2.radius; // distance from s1's center to s2
+ float depth = s1.radius - d; // sphere overlap
+ if(d < 0.0f) d = 0.0f; // clamp to zero, i.e. if s1's center is inside s2
+ // no collision if sphere is not close enough
+ if(d*d < mindistsq && d < s1.radius){
+ dist.Normalise();
+ point.point = s1.center - dist*d;
+ point.normal = dist;
+#ifndef VU_COLLISION
+ point.surfaceA = s1.surface;
+ point.pieceA = s1.piece;
+ point.surfaceB = s2.surface;
+ point.pieceB = s2.piece;
+#endif
+ point.depth = depth;
+ mindistsq = d*d; // collision radius
+ return true;
+ }
+ return false;
+}
+
+bool
+CCollision::ProcessSphereBox(const CColSphere &sph, const CColBox &box, CColPoint &point, float &mindistsq)
+{
+ CVector p;
+ CVector dist;
+
+ // GTA's code is too complicated, uses a huge 3x3x3 if statement
+ // we can simplify the structure a lot
+
+ // first make sure we have a collision at all
+ if(sph.center.x + sph.radius < box.min.x) return false;
+ if(sph.center.x - sph.radius > box.max.x) return false;
+ if(sph.center.y + sph.radius < box.min.y) return false;
+ if(sph.center.y - sph.radius > box.max.y) return false;
+ if(sph.center.z + sph.radius < box.min.z) return false;
+ if(sph.center.z - sph.radius > box.max.z) return false;
+
+ // Now find out where the sphere center lies in relation to all the sides
+ int xpos = sph.center.x < box.min.x ? 1 :
+ sph.center.x > box.max.x ? 2 :
+ 0;
+ int ypos = sph.center.y < box.min.y ? 1 :
+ sph.center.y > box.max.y ? 2 :
+ 0;
+ int zpos = sph.center.z < box.min.z ? 1 :
+ sph.center.z > box.max.z ? 2 :
+ 0;
+
+ if(xpos == 0 && ypos == 0 && zpos == 0){
+ // sphere is inside the box
+ p = (box.min + box.max)*0.5f;
+
+ dist = sph.center - p;
+ float lensq = dist.MagnitudeSqr();
+ if(lensq < mindistsq){
+ point.normal = dist * (1.0f/Sqrt(lensq));
+ point.point = sph.center - point.normal;
+#ifndef VU_COLLISION
+ point.surfaceA = sph.surface;
+ point.pieceA = sph.piece;
+ point.surfaceB = box.surface;
+ point.pieceB = box.piece;
+#endif
+
+ // find absolute distance to the closer side in each dimension
+ float dx = dist.x > 0.0f ?
+ box.max.x - sph.center.x :
+ sph.center.x - box.min.x;
+ float dy = dist.y > 0.0f ?
+ box.max.y - sph.center.y :
+ sph.center.y - box.min.y;
+ float dz = dist.z > 0.0f ?
+ box.max.z - sph.center.z :
+ sph.center.z - box.min.z;
+ // collision depth is maximum of that:
+ if(dx > dy && dx > dz)
+ point.depth = dx;
+ else if(dy > dz)
+ point.depth = dy;
+ else
+ point.depth = dz;
+ return true;
+ }
+ }else{
+ // sphere is outside.
+ // closest point on box:
+ p.x = xpos == 1 ? box.min.x :
+ xpos == 2 ? box.max.x :
+ sph.center.x;
+ p.y = ypos == 1 ? box.min.y :
+ ypos == 2 ? box.max.y :
+ sph.center.y;
+ p.z = zpos == 1 ? box.min.z :
+ zpos == 2 ? box.max.z :
+ sph.center.z;
+
+ dist = sph.center - p;
+ float lensq = dist.MagnitudeSqr();
+ if(lensq < mindistsq){
+ float len = Sqrt(lensq);
+ point.point = p;
+ point.normal = dist * (1.0f/len);
+#ifndef VU_COLLISION
+ point.surfaceA = sph.surface;
+ point.pieceA = sph.piece;
+ point.surfaceB = box.surface;
+ point.pieceB = box.piece;
+#endif
+ point.depth = sph.radius - len;
+ mindistsq = lensq;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+CCollision::ProcessLineBox(const CColLine &line, const CColBox &box, CColPoint &point, float &mindist)
+{
+ float mint, t, x, y, z;
+ CVector normal;
+ CVector p;
+
+ mint = 1.0f;
+ // check if points are on opposite sides of min x plane
+ if((box.min.x - line.p1.x) * (box.min.x - line.p0.x) < 0.0f){
+ // parameter along line where we intersect
+ t = (box.min.x - line.p0.x) / (line.p1.x - line.p0.x);
+ // y of intersection
+ y = line.p0.y + (line.p1.y - line.p0.y)*t;
+ if(y > box.min.y && y < box.max.y){
+ // z of intersection
+ z = line.p0.z + (line.p1.z - line.p0.z)*t;
+ if(z > box.min.z && z < box.max.z)
+ if(t < mint){
+ mint = t;
+ p = CVector(box.min.x, y, z);
+ normal = CVector(-1.0f, 0.0f, 0.0f);
+ }
+ }
+ }
+
+ // max x plane
+ if((line.p1.x - box.max.x) * (line.p0.x - box.max.x) < 0.0f){
+ t = (line.p0.x - box.max.x) / (line.p0.x - line.p1.x);
+ y = line.p0.y + (line.p1.y - line.p0.y)*t;
+ if(y > box.min.y && y < box.max.y){
+ z = line.p0.z + (line.p1.z - line.p0.z)*t;
+ if(z > box.min.z && z < box.max.z)
+ if(t < mint){
+ mint = t;
+ p = CVector(box.max.x, y, z);
+ normal = CVector(1.0f, 0.0f, 0.0f);
+ }
+ }
+ }
+
+ // min y plne
+ if((box.min.y - line.p0.y) * (box.min.y - line.p1.y) < 0.0f){
+ t = (box.min.y - line.p0.y) / (line.p1.y - line.p0.y);
+ x = line.p0.x + (line.p1.x - line.p0.x)*t;
+ if(x > box.min.x && x < box.max.x){
+ z = line.p0.z + (line.p1.z - line.p0.z)*t;
+ if(z > box.min.z && z < box.max.z)
+ if(t < mint){
+ mint = t;
+ p = CVector(x, box.min.y, z);
+ normal = CVector(0.0f, -1.0f, 0.0f);
+ }
+ }
+ }
+
+ // max y plane
+ if((line.p0.y - box.max.y) * (line.p1.y - box.max.y) < 0.0f){
+ t = (line.p0.y - box.max.y) / (line.p0.y - line.p1.y);
+ x = line.p0.x + (line.p1.x - line.p0.x)*t;
+ if(x > box.min.x && x < box.max.x){
+ z = line.p0.z + (line.p1.z - line.p0.z)*t;
+ if(z > box.min.z && z < box.max.z)
+ if(t < mint){
+ mint = t;
+ p = CVector(x, box.max.y, z);
+ normal = CVector(0.0f, 1.0f, 0.0f);
+ }
+ }
+ }
+
+ // min z plne
+ if((box.min.z - line.p0.z) * (box.min.z - line.p1.z) < 0.0f){
+ t = (box.min.z - line.p0.z) / (line.p1.z - line.p0.z);
+ x = line.p0.x + (line.p1.x - line.p0.x)*t;
+ if(x > box.min.x && x < box.max.x){
+ y = line.p0.y + (line.p1.y - line.p0.y)*t;
+ if(y > box.min.y && y < box.max.y)
+ if(t < mint){
+ mint = t;
+ p = CVector(x, y, box.min.z);
+ normal = CVector(0.0f, 0.0f, -1.0f);
+ }
+ }
+ }
+
+ // max z plane
+ if((line.p0.z - box.max.z) * (line.p1.z - box.max.z) < 0.0f){
+ t = (line.p0.z - box.max.z) / (line.p0.z - line.p1.z);
+ x = line.p0.x + (line.p1.x - line.p0.x)*t;
+ if(x > box.min.x && x < box.max.x){
+ y = line.p0.y + (line.p1.y - line.p0.y)*t;
+ if(y > box.min.y && y < box.max.y)
+ if(t < mint){
+ mint = t;
+ p = CVector(x, y, box.max.z);
+ normal = CVector(0.0f, 0.0f, 1.0f);
+ }
+ }
+ }
+
+ if(mint >= mindist)
+ return false;
+
+ point.point = p;
+ point.normal = normal;
+#ifndef VU_COLLISION
+ point.surfaceA = 0;
+ point.pieceA = 0;
+ point.surfaceB = box.surface;
+ point.pieceB = box.piece;
+#endif
+ mindist = mint;
+
+ return true;
+}
+
+// If line.p0 lies inside sphere, no collision is registered.
+bool
+CCollision::ProcessLineSphere(const CColLine &line, const CColSphere &sphere, CColPoint &point, float &mindist)
+{
+ CVector v01 = line.p1 - line.p0;
+ CVector v0c = sphere.center - line.p0;
+ float linesq = v01.MagnitudeSqr();
+ // project v0c onto v01, scaled by |v01| this is the midpoint of the two intersections
+ float projline = DotProduct(v01, v0c);
+ // tangent of p0 to sphere, scaled by linesq just like projline^2
+ float tansq = (v0c.MagnitudeSqr() - sphere.radius*sphere.radius) * linesq;
+ // this works out to be the square of the distance between the midpoint and the intersections
+ float diffsq = projline*projline - tansq;
+ // no intersection
+ if(diffsq < 0.0f)
+ return false;
+ // point of first intersection, in range [0,1] between p0 and p1
+ float t = (projline - Sqrt(diffsq)) / linesq;
+ // if not on line or beyond mindist, no intersection
+ if(t < 0.0f || t > 1.0f || t >= mindist)
+ return false;
+ point.point = line.p0 + v01*t;
+ point.normal = point.point - sphere.center;
+ point.normal.Normalise();
+#ifndef VU_COLLISION
+ point.surfaceA = 0;
+ point.pieceA = 0;
+ point.surfaceB = sphere.surface;
+ point.pieceB = sphere.piece;
+#endif
+ mindist = t;
+ return true;
+}
+
+bool
+CCollision::ProcessVerticalLineTriangle(const CColLine &line,
+ const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane,
+ CColPoint &point, float &mindist, CStoredCollPoly *poly)
+{
+#ifdef VU_COLLISION
+ // not used in favour of optimized loops
+ bool res = ProcessLineTriangle(line, verts, tri, plane, point, mindist);
+ if(res && poly){
+ poly->verts[0] = verts[tri.a].Get();
+ poly->verts[1] = verts[tri.b].Get();
+ poly->verts[2] = verts[tri.c].Get();
+ poly->valid = true;
+ }
+ return res;
+#else
+ float t;
+ CVector normal;
+
+ const CVector &p0 = line.p0;
+ const CVector &va = verts[tri.a].Get();
+ const CVector &vb = verts[tri.b].Get();
+ const CVector &vc = verts[tri.c].Get();
+
+ // early out bound rect test
+ if(p0.x < va.x && p0.x < vb.x && p0.x < vc.x) return false;
+ if(p0.x > va.x && p0.x > vb.x && p0.x > vc.x) return false;
+ if(p0.y < va.y && p0.y < vb.y && p0.y < vc.y) return false;
+ if(p0.y > va.y && p0.y > vb.y && p0.y > vc.y) return false;
+
+ plane.GetNormal(normal);
+ // if points are on the same side, no collision
+ if(plane.CalcPoint(p0) * plane.CalcPoint(line.p1) > 0.0f)
+ return false;
+
+ // intersection parameter on line
+ float h = (line.p1 - p0).z;
+ t = -plane.CalcPoint(p0) / (h * normal.z);
+ // early out if we're beyond the mindist
+ if(t >= mindist)
+ return false;
+ CVector p(p0.x, p0.y, p0.z + h*t);
+
+ CVector2D vec1, vec2, vec3, vect;
+ switch(plane.dir){
+ case DIR_X_POS:
+ vec1.x = va.y; vec1.y = va.z;
+ vec2.x = vc.y; vec2.y = vc.z;
+ vec3.x = vb.y; vec3.y = vb.z;
+ vect.x = p.y; vect.y = p.z;
+ break;
+ case DIR_X_NEG:
+ vec1.x = va.y; vec1.y = va.z;
+ vec2.x = vb.y; vec2.y = vb.z;
+ vec3.x = vc.y; vec3.y = vc.z;
+ vect.x = p.y; vect.y = p.z;
+ break;
+ case DIR_Y_POS:
+ vec1.x = va.z; vec1.y = va.x;
+ vec2.x = vc.z; vec2.y = vc.x;
+ vec3.x = vb.z; vec3.y = vb.x;
+ vect.x = p.z; vect.y = p.x;
+ break;
+ case DIR_Y_NEG:
+ vec1.x = va.z; vec1.y = va.x;
+ vec2.x = vb.z; vec2.y = vb.x;
+ vec3.x = vc.z; vec3.y = vc.x;
+ vect.x = p.z; vect.y = p.x;
+ break;
+ case DIR_Z_POS:
+ vec1.x = va.x; vec1.y = va.y;
+ vec2.x = vc.x; vec2.y = vc.y;
+ vec3.x = vb.x; vec3.y = vb.y;
+ vect.x = p.x; vect.y = p.y;
+ break;
+ case DIR_Z_NEG:
+ vec1.x = va.x; vec1.y = va.y;
+ vec2.x = vb.x; vec2.y = vb.y;
+ vec3.x = vc.x; vec3.y = vc.y;
+ vect.x = p.x; vect.y = p.y;
+ break;
+ default:
+ assert(0);
+ }
+ if(CrossProduct2D(vec2-vec1, vect-vec1) < 0.0f) return false;
+ if(CrossProduct2D(vec3-vec1, vect-vec1) > 0.0f) return false;
+ if(CrossProduct2D(vec3-vec2, vect-vec2) < 0.0f) return false;
+ if(t >= mindist) return false;
+ point.point = p;
+ point.normal = normal;
+ point.surfaceA = 0;
+ point.pieceA = 0;
+ point.surfaceB = tri.surface;
+ point.pieceB = 0;
+ if(poly){
+ poly->verts[0] = va;
+ poly->verts[1] = vb;
+ poly->verts[2] = vc;
+ poly->valid = true;
+ }
+ mindist = t;
+ return true;
+#endif
+}
+
+bool
+CCollision::IsStoredPolyStillValidVerticalLine(const CVector &pos, float z, CColPoint &point, CStoredCollPoly *poly)
+{
+#ifdef VU_COLLISION
+ if(!poly->valid)
+ return false;
+
+ CVuVector p0 = pos;
+ CVuVector p1 = pos;
+ p1.z = z;
+
+ CVector v01 = poly->verts[1] - poly->verts[0];
+ CVector v02 = poly->verts[2] - poly->verts[0];
+ CVuVector plane = CrossProduct(v02, v01);
+ plane.Normalise();
+ plane.w = DotProduct(plane, poly->verts[0]);
+
+ LineToTriangleCollision(p0, p1, poly->verts[0], poly->verts[1], poly->verts[2], plane);
+
+ CVuVector pnt;
+ float dist;
+ if(!GetVUresult(pnt, plane, dist))
+#ifdef FIX_BUGS
+ // perhaps not needed but be safe
+ return poly->valid = false;
+#else
+ return false;
+#endif
+ point.point = pnt;
+ return true;
+#else
+ float t;
+
+ if(!poly->valid)
+ return false;
+
+ // maybe inlined?
+ CColTrianglePlane plane;
+ plane.Set(poly->verts[0], poly->verts[1], poly->verts[2]);
+
+ const CVector &va = poly->verts[0];
+ const CVector &vb = poly->verts[1];
+ const CVector &vc = poly->verts[2];
+ CVector p0 = pos;
+ CVector p1(pos.x, pos.y, z);
+
+ // The rest is pretty much CCollision::ProcessLineTriangle
+
+ // if points are on the same side, no collision
+ if(plane.CalcPoint(p0) * plane.CalcPoint(p1) > 0.0f)
+ return poly->valid = false;
+
+ // intersection parameter on line
+ CVector normal;
+ plane.GetNormal(normal);
+ t = -plane.CalcPoint(p0) / DotProduct(p1 - p0, normal);
+ // find point of intersection
+ CVector p = p0 + (p1-p0)*t;
+
+ CVector2D vec1, vec2, vec3, vect;
+ switch(plane.dir){
+ case DIR_X_POS:
+ vec1.x = va.y; vec1.y = va.z;
+ vec2.x = vc.y; vec2.y = vc.z;
+ vec3.x = vb.y; vec3.y = vb.z;
+ vect.x = p.y; vect.y = p.z;
+ break;
+ case DIR_X_NEG:
+ vec1.x = va.y; vec1.y = va.z;
+ vec2.x = vb.y; vec2.y = vb.z;
+ vec3.x = vc.y; vec3.y = vc.z;
+ vect.x = p.y; vect.y = p.z;
+ break;
+ case DIR_Y_POS:
+ vec1.x = va.z; vec1.y = va.x;
+ vec2.x = vc.z; vec2.y = vc.x;
+ vec3.x = vb.z; vec3.y = vb.x;
+ vect.x = p.z; vect.y = p.x;
+ break;
+ case DIR_Y_NEG:
+ vec1.x = va.z; vec1.y = va.x;
+ vec2.x = vb.z; vec2.y = vb.x;
+ vec3.x = vc.z; vec3.y = vc.x;
+ vect.x = p.z; vect.y = p.x;
+ break;
+ case DIR_Z_POS:
+ vec1.x = va.x; vec1.y = va.y;
+ vec2.x = vc.x; vec2.y = vc.y;
+ vec3.x = vb.x; vec3.y = vb.y;
+ vect.x = p.x; vect.y = p.y;
+ break;
+ case DIR_Z_NEG:
+ vec1.x = va.x; vec1.y = va.y;
+ vec2.x = vb.x; vec2.y = vb.y;
+ vec3.x = vc.x; vec3.y = vc.y;
+ vect.x = p.x; vect.y = p.y;
+ break;
+ default:
+ assert(0);
+ }
+ if(CrossProduct2D(vec2-vec1, vect-vec1) < 0.0f) return poly->valid = false;
+ if(CrossProduct2D(vec3-vec1, vect-vec1) > 0.0f) return poly->valid = false;
+ if(CrossProduct2D(vec3-vec2, vect-vec2) < 0.0f) return poly->valid = false;
+ point.point = p;
+ return poly->valid = true;
+#endif
+}
+
+bool
+CCollision::ProcessLineTriangle(const CColLine &line,
+ const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane,
+ CColPoint &point, float &mindist)
+{
+#ifdef VU_COLLISION
+ // not used in favour of optimized loops
+ VuTriangle vutri;
+ verts[tri.a].Unpack(vutri.v0);
+ verts[tri.b].Unpack(vutri.v1);
+ verts[tri.c].Unpack(vutri.v2);
+ plane.Unpack(vutri.plane);
+
+ LineToTriangleCollisionCompressed(*(CVuVector*)&line.p0, *(CVuVector*)&line.p1, vutri);
+
+ CVuVector pnt, normal;
+ float dist;
+ if(GetVUresult(pnt, normal, dist)){
+ if(dist < mindist){
+ point.point = pnt;
+ point.normal = normal;
+ mindist = dist;
+ return true;
+ }
+ }
+ return false;
+#else
+ float t;
+ CVector normal;
+ plane.GetNormal(normal);
+
+ // if points are on the same side, no collision
+ if(plane.CalcPoint(line.p0) * plane.CalcPoint(line.p1) > 0.0f)
+ return false;
+
+ // intersection parameter on line
+ t = -plane.CalcPoint(line.p0) / DotProduct(line.p1 - line.p0, normal);
+ // early out if we're beyond the mindist
+ if(t >= mindist)
+ return false;
+ // find point of intersection
+ CVector p = line.p0 + (line.p1-line.p0)*t;
+
+ const CVector &va = verts[tri.a].Get();
+ const CVector &vb = verts[tri.b].Get();
+ const CVector &vc = verts[tri.c].Get();
+ CVector2D vec1, vec2, vec3, vect;
+
+ switch(plane.dir){
+ case DIR_X_POS:
+ vec1.x = va.y; vec1.y = va.z;
+ vec2.x = vc.y; vec2.y = vc.z;
+ vec3.x = vb.y; vec3.y = vb.z;
+ vect.x = p.y; vect.y = p.z;
+ break;
+ case DIR_X_NEG:
+ vec1.x = va.y; vec1.y = va.z;
+ vec2.x = vb.y; vec2.y = vb.z;
+ vec3.x = vc.y; vec3.y = vc.z;
+ vect.x = p.y; vect.y = p.z;
+ break;
+ case DIR_Y_POS:
+ vec1.x = va.z; vec1.y = va.x;
+ vec2.x = vc.z; vec2.y = vc.x;
+ vec3.x = vb.z; vec3.y = vb.x;
+ vect.x = p.z; vect.y = p.x;
+ break;
+ case DIR_Y_NEG:
+ vec1.x = va.z; vec1.y = va.x;
+ vec2.x = vb.z; vec2.y = vb.x;
+ vec3.x = vc.z; vec3.y = vc.x;
+ vect.x = p.z; vect.y = p.x;
+ break;
+ case DIR_Z_POS:
+ vec1.x = va.x; vec1.y = va.y;
+ vec2.x = vc.x; vec2.y = vc.y;
+ vec3.x = vb.x; vec3.y = vb.y;
+ vect.x = p.x; vect.y = p.y;
+ break;
+ case DIR_Z_NEG:
+ vec1.x = va.x; vec1.y = va.y;
+ vec2.x = vb.x; vec2.y = vb.y;
+ vec3.x = vc.x; vec3.y = vc.y;
+ vect.x = p.x; vect.y = p.y;
+ break;
+ default:
+ assert(0);
+ }
+ if(CrossProduct2D(vec2-vec1, vect-vec1) < 0.0f) return false;
+ if(CrossProduct2D(vec3-vec1, vect-vec1) > 0.0f) return false;
+ if(CrossProduct2D(vec3-vec2, vect-vec2) < 0.0f) return false;
+ if(t >= mindist) return false;
+ point.point = p;
+ point.normal = normal;
+ point.surfaceA = 0;
+ point.pieceA = 0;
+ point.surfaceB = tri.surface;
+ point.pieceB = 0;
+ mindist = t;
+ return true;
+#endif
+}
+
+bool
+CCollision::ProcessSphereTriangle(const CColSphere &sphere,
+ const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane,
+ CColPoint &point, float &mindistsq)
+{
+#ifdef VU_COLLISION
+ // not used in favour of optimized loops
+ VuTriangle vutri;
+ verts[tri.a].Unpack(vutri.v0);
+ verts[tri.b].Unpack(vutri.v1);
+ verts[tri.c].Unpack(vutri.v2);
+ plane.Unpack(vutri.plane);
+
+ SphereToTriangleCollisionCompressed(*(CVuVector*)&sphere, vutri);
+
+ CVuVector pnt, normal;
+ float dist;
+ if(GetVUresult(pnt, normal, dist) && dist*dist < mindistsq){
+ float depth = sphere.radius - dist;
+ if(depth > point.depth){
+ point.point = pnt;
+ point.normal = normal;
+ point.depth = depth;
+ mindistsq = dist*dist;
+ return true;
+ }
+ }
+ return false;
+#else
+ // If sphere and plane don't intersect, no collision
+ float planedist = plane.CalcPoint(sphere.center);
+ float distsq = planedist*planedist;
+ if(Abs(planedist) > sphere.radius || distsq > mindistsq)
+ return false;
+
+ const CVector &va = verts[tri.a].Get();
+ const CVector &vb = verts[tri.b].Get();
+ const CVector &vc = verts[tri.c].Get();
+
+ // calculate two orthogonal basis vectors for the triangle
+ CVector normal;
+ plane.GetNormal(normal);
+ CVector vec2 = vb - va;
+ float len = vec2.Magnitude();
+ vec2 = vec2 * (1.0f/len);
+ CVector vec1 = CrossProduct(vec2, normal);
+
+ // We know A has local coordinate [0,0] and B has [0,len].
+ // Now calculate coordinates on triangle for these two vectors:
+ CVector vac = vc - va;
+ CVector vas = sphere.center - va;
+ CVector2D b(0.0f, len);
+ CVector2D c(DotProduct(vec1, vac), DotProduct(vec2, vac));
+ CVector2D s(DotProduct(vec1, vas), DotProduct(vec2, vas));
+
+ // The three triangle lines partition the space into 6 sectors,
+ // find out in which the center lies.
+ int insideAB = CrossProduct2D(s, b) >= 0.0f;
+ int insideAC = CrossProduct2D(c, s) >= 0.0f;
+ int insideBC = CrossProduct2D(s-b, c-b) >= 0.0f;
+
+ int testcase = insideAB + insideAC + insideBC;
+ float dist = 0.0f;
+ CVector p;
+ switch(testcase){
+ case 1:
+ // closest to a vertex
+ if(insideAB) p = vc;
+ else if(insideAC) p = vb;
+ else if(insideBC) p = va;
+ else assert(0);
+ dist = (sphere.center - p).Magnitude();
+ break;
+ case 2:
+ // closest to an edge
+ // looks like original game as DistToLine manually inlined
+ if(!insideAB) dist = DistToLine(&va, &vb, &sphere.center, p);
+ else if(!insideAC) dist = DistToLine(&va, &vc, &sphere.center, p);
+ else if(!insideBC) dist = DistToLine(&vb, &vc, &sphere.center, p);
+ else assert(0);
+ break;
+ case 3:
+ // center is in triangle
+ dist = Abs(planedist);
+ p = sphere.center - normal*planedist;
+ break;
+ default:
+ assert(0);
+ }
+
+ if(dist >= sphere.radius || dist*dist >= mindistsq)
+ return false;
+
+ point.point = p;
+ point.normal = sphere.center - p;
+ point.normal.Normalise();
+#ifndef VU_COLLISION
+ point.surfaceA = sphere.surface;
+ point.pieceA = sphere.piece;
+ point.surfaceB = tri.surface;
+ point.pieceB = 0;
+#endif
+ point.depth = sphere.radius - dist;
+ mindistsq = dist*dist;
+ return true;
+#endif
+}
+
+bool
+CCollision::ProcessLineOfSight(const CColLine &line,
+ const CMatrix &matrix, CColModel &model,
+ CColPoint &point, float &mindist, bool ignoreSeeThrough)
+{
+#ifdef VU_COLLISION
+ CMatrix matTransform;
+ int i;
+
+ // transform line to model space
+ Invert(matrix, matTransform);
+ CVuVector newline[2];
+ TransformPoints(newline, 2, matTransform, (RwV3d*)&line.p0, sizeof(CColLine)/2);
+
+ if(mindist < 1.0f)
+ newline[1] = newline[0] + (newline[1] - newline[0])*mindist;
+
+ // If we don't intersect with the bounding box, no chance on the rest
+ if(!TestLineBox(*(CColLine*)newline, model.boundingBox))
+ return false;
+
+ float coldist = 1.0f;
+ for(i = 0; i < model.numSpheres; i++){
+ if(ignoreSeeThrough && IsSeeThrough(model.spheres[i].surface)) continue;
+ if(ProcessLineSphere(*(CColLine*)newline, model.spheres[i], point, coldist))
+ point.Set(0, 0, model.spheres[i].surface, model.spheres[i].piece);
+ }
+
+ for(i = 0; i < model.numBoxes; i++){
+ if(ignoreSeeThrough && IsSeeThrough(model.boxes[i].surface)) continue;
+ if(ProcessLineBox(*(CColLine*)newline, model.boxes[i], point, coldist))
+ point.Set(0, 0, model.boxes[i].surface, model.boxes[i].piece);
+ }
+
+ CalculateTrianglePlanes(&model);
+ VuTriangle vutri;
+ CColTriangle *lasttri = nil;
+ for(i = 0; i < model.numTriangles; i++){
+ if(ignoreSeeThrough && IsSeeThrough(model.triangles[i].surface)) continue;
+
+ CColTriangle *tri = &model.triangles[i];
+ model.vertices[tri->a].Unpack(vutri.v0);
+ model.vertices[tri->b].Unpack(vutri.v1);
+ model.vertices[tri->c].Unpack(vutri.v2);
+ model.trianglePlanes[i].Unpack(vutri.plane);
+
+ LineToTriangleCollisionCompressed(newline[0], newline[1], vutri);
+ lasttri = tri;
+ break;
+ }
+#ifdef FIX_BUGS
+ // no need to check first again
+ i++;
+#endif
+ CVuVector pnt, normal;
+ float dist;
+ for(; i < model.numTriangles; i++){
+ if(ignoreSeeThrough && IsSeeThrough(model.triangles[i].surface)) continue;
+
+ CColTriangle *tri = &model.triangles[i];
+ model.vertices[tri->a].Unpack(vutri.v0);
+ model.vertices[tri->b].Unpack(vutri.v1);
+ model.vertices[tri->c].Unpack(vutri.v2);
+ model.trianglePlanes[i].Unpack(vutri.plane);
+
+ if(GetVUresult(pnt, normal, dist))
+ if(dist < coldist){
+ point.point = pnt;
+ point.normal = normal;
+ point.Set(0, 0, lasttri->surface, 0);
+ coldist = dist;
+ }
+
+ LineToTriangleCollisionCompressed(newline[0], newline[1], vutri);
+ lasttri = tri;
+ }
+ if(lasttri && GetVUresult(pnt, normal, dist))
+ if(dist < coldist){
+ point.point = pnt;
+ point.normal = normal;
+ point.Set(0, 0, lasttri->surface, 0);
+ coldist = dist;
+ }
+
+
+ if(coldist < 1.0f){
+ point.point = matrix * point.point;
+ point.normal = Multiply3x3(matrix, point.normal);
+ mindist *= coldist;
+ return true;
+ }
+ return false;
+#else
+ static CMatrix matTransform;
+ int i;
+
+ // transform line to model space
+ Invert(matrix, matTransform);
+ CColLine newline(matTransform * line.p0, matTransform * line.p1);
+
+ // If we don't intersect with the bounding box, no chance on the rest
+ if(!TestLineBox(newline, model.boundingBox))
+ return false;
+
+ float coldist = mindist;
+ for(i = 0; i < model.numSpheres; i++){
+ if(ignoreSeeThrough && IsSeeThrough(model.spheres[i].surface)) continue;
+ ProcessLineSphere(newline, model.spheres[i], point, coldist);
+ }
+
+ for(i = 0; i < model.numBoxes; i++){
+ if(ignoreSeeThrough && IsSeeThrough(model.boxes[i].surface)) continue;
+ ProcessLineBox(newline, model.boxes[i], point, coldist);
+ }
+
+ CalculateTrianglePlanes(&model);
+ for(i = 0; i < model.numTriangles; i++){
+ if(ignoreSeeThrough && IsSeeThrough(model.triangles[i].surface)) continue;
+ ProcessLineTriangle(newline, model.vertices, model.triangles[i], model.trianglePlanes[i], point, coldist);
+ }
+
+ if(coldist < mindist){
+ point.point = matrix * point.point;
+ point.normal = Multiply3x3(matrix, point.normal);
+ mindist = coldist;
+ return true;
+ }
+ return false;
+#endif
+}
+
+bool
+CCollision::ProcessVerticalLine(const CColLine &line,
+ const CMatrix &matrix, CColModel &model,
+ CColPoint &point, float &mindist, bool ignoreSeeThrough, CStoredCollPoly *poly)
+{
+#ifdef VU_COLLISION
+ static CStoredCollPoly TempStoredPoly;
+ CMatrix matTransform;
+ int i;
+
+ // transform line to model space
+ Invert(matrix, matTransform);
+ CVuVector newline[2];
+ TransformPoints(newline, 2, matTransform, (RwV3d*)&line.p0, sizeof(CColLine)/2);
+
+ if(mindist < 1.0f)
+ newline[1] = newline[0] + (newline[1] - newline[0])*mindist;
+
+ if(!TestLineBox(*(CColLine*)newline, model.boundingBox))
+ return false;
+
+ float coldist = 1.0f;
+ for(i = 0; i < model.numSpheres; i++){
+ if(ignoreSeeThrough && IsSeeThrough(model.spheres[i].surface)) continue;
+ if(ProcessLineSphere(*(CColLine*)newline, model.spheres[i], point, coldist))
+ point.Set(0, 0, model.spheres[i].surface, model.spheres[i].piece);
+ }
+
+ for(i = 0; i < model.numBoxes; i++){
+ if(ignoreSeeThrough && IsSeeThrough(model.boxes[i].surface)) continue;
+ if(ProcessLineBox(*(CColLine*)newline, model.boxes[i], point, coldist))
+ point.Set(0, 0, model.boxes[i].surface, model.boxes[i].piece);
+ }
+
+ CalculateTrianglePlanes(&model);
+ TempStoredPoly.valid = false;
+ if(model.numTriangles){
+ bool registeredCol;
+ CColTriangle *lasttri = nil;
+ VuTriangle vutri;
+ for(i = 0; i < model.numTriangles; i++){
+ if(ignoreSeeThrough && IsSeeThrough(model.triangles[i].surface)) continue;
+
+ CColTriangle *tri = &model.triangles[i];
+ model.vertices[tri->a].Unpack(vutri.v0);
+ model.vertices[tri->b].Unpack(vutri.v1);
+ model.vertices[tri->c].Unpack(vutri.v2);
+ model.trianglePlanes[i].Unpack(vutri.plane);
+
+ LineToTriangleCollisionCompressed(newline[0], newline[1], vutri);
+ lasttri = tri;
+ break;
+ }
+#ifdef FIX_BUGS
+ // no need to check first again
+ i++;
+#endif
+ CVuVector pnt, normal;
+ float dist;
+ for(; i < model.numTriangles; i++){
+ if(ignoreSeeThrough && IsSeeThrough(model.triangles[i].surface)) continue;
+
+ CColTriangle *tri = &model.triangles[i];
+ model.vertices[tri->a].Unpack(vutri.v0);
+ model.vertices[tri->b].Unpack(vutri.v1);
+ model.vertices[tri->c].Unpack(vutri.v2);
+ model.trianglePlanes[i].Unpack(vutri.plane);
+
+ if(GetVUresult(pnt, normal, dist)){
+ if(dist < coldist){
+ point.point = pnt;
+ point.normal = normal;
+ point.Set(0, 0, lasttri->surface, 0);
+ coldist = dist;
+ registeredCol = true;
+ }else
+ registeredCol = false;
+ }else
+ registeredCol = false;
+
+ if(registeredCol){
+ TempStoredPoly.verts[0] = model.vertices[lasttri->a].Get();
+ TempStoredPoly.verts[1] = model.vertices[lasttri->b].Get();
+ TempStoredPoly.verts[2] = model.vertices[lasttri->c].Get();
+ TempStoredPoly.valid = true;
+ }
+
+ LineToTriangleCollisionCompressed(newline[0], newline[1], vutri);
+ lasttri = tri;
+ }
+ if(lasttri && GetVUresult(pnt, normal, dist)){
+ if(dist < coldist){
+ point.point = pnt;
+ point.normal = normal;
+ point.Set(0, 0, lasttri->surface, 0);
+ coldist = dist;
+ registeredCol = true;
+ }else
+ registeredCol = false;
+ }else
+ registeredCol = false;
+
+ if(registeredCol){
+ TempStoredPoly.verts[0] = model.vertices[lasttri->a].Get();
+ TempStoredPoly.verts[1] = model.vertices[lasttri->b].Get();
+ TempStoredPoly.verts[2] = model.vertices[lasttri->c].Get();
+ TempStoredPoly.valid = true;
+ }
+ }
+
+ if(coldist < 1.0f){
+ point.point = matrix * point.point;
+ point.normal = Multiply3x3(matrix, point.normal);
+ if(TempStoredPoly.valid && poly){
+ *poly = TempStoredPoly;
+ poly->verts[0] = matrix * CVector(poly->verts[0]);
+ poly->verts[1] = matrix * CVector(poly->verts[1]);
+ poly->verts[2] = matrix * CVector(poly->verts[2]);
+ }
+ mindist *= coldist;
+ return true;
+ }
+ return false;
+#else
+ static CStoredCollPoly TempStoredPoly;
+ int i;
+
+ // transform line to model space
+ // Why does the game seem to do this differently than above?
+ CColLine newline(MultiplyInverse(matrix, line.p0), MultiplyInverse(matrix, line.p1));
+ newline.p1.x = newline.p0.x;
+ newline.p1.y = newline.p0.y;
+
+ if(!TestVerticalLineBox(newline, model.boundingBox))
+ return false;
+
+ float coldist = mindist;
+ for(i = 0; i < model.numSpheres; i++){
+ if(ignoreSeeThrough && IsSeeThrough(model.spheres[i].surface)) continue;
+ ProcessLineSphere(newline, model.spheres[i], point, coldist);
+ }
+
+ for(i = 0; i < model.numBoxes; i++){
+ if(ignoreSeeThrough && IsSeeThrough(model.boxes[i].surface)) continue;
+ ProcessLineBox(newline, model.boxes[i], point, coldist);
+ }
+
+ CalculateTrianglePlanes(&model);
+ TempStoredPoly.valid = false;
+ for(i = 0; i < model.numTriangles; i++){
+ if(ignoreSeeThrough && IsSeeThrough(model.triangles[i].surface)) continue;
+ ProcessVerticalLineTriangle(newline, model.vertices, model.triangles[i], model.trianglePlanes[i], point, coldist, &TempStoredPoly);
+ }
+
+ if(coldist < mindist){
+ point.point = matrix * point.point;
+ point.normal = Multiply3x3(matrix, point.normal);
+ if(TempStoredPoly.valid && poly){
+ *poly = TempStoredPoly;
+ poly->verts[0] = matrix * poly->verts[0];
+ poly->verts[1] = matrix * poly->verts[1];
+ poly->verts[2] = matrix * poly->verts[2];
+ }
+ mindist = coldist;
+ return true;
+ }
+ return false;
+#endif
+}
+
+enum {
+ MAXNUMSPHERES = 128,
+ MAXNUMBOXES = 32,
+ MAXNUMLINES = 16,
+ MAXNUMTRIS = 600
+};
+
+#ifdef VU_COLLISION
+#ifdef GTA_PS2
+#define SPR(off) ((uint8*)(0x70000000 + (off)))
+#else
+static uint8 fakeSPR[16*1024];
+#define SPR(off) ((uint8*)(fakeSPR + (off)))
+#endif
+#endif
+
+// This checks model A's spheres and lines against model B's spheres, boxes and triangles.
+// Returns the number of A's spheres that collide.
+// Returned ColPoints are in world space.
+// NB: only vehicles can have col models with lines, exactly 4, one for each wheel
+int32
+CCollision::ProcessColModels(const CMatrix &matrixA, CColModel &modelA,
+ const CMatrix &matrixB, CColModel &modelB,
+ CColPoint *spherepoints, CColPoint *linepoints, float *linedists)
+{
+#ifdef VU_COLLISION
+ CVuVector *aSpheresA = (CVuVector*)SPR(0x0000);
+ CVuVector *aSpheresB = (CVuVector*)SPR(0x0800);
+ CVuVector *aLinesA = (CVuVector*)SPR(0x1000);
+ int32 *aSphereIndicesA = (int32*)SPR(0x1200);
+ int32 *aSphereIndicesB = (int32*)SPR(0x1400);
+ int32 *aBoxIndicesB = (int32*)SPR(0x1600);
+ int32 *aTriangleIndicesB = (int32*)SPR(0x1680);
+ bool *aCollided = (bool*)SPR(0x1FE0);
+ CMatrix &matAB = *(CMatrix*)SPR(0x1FF0);
+ CMatrix &matBA = *(CMatrix*)SPR(0x2040);
+ int i, j, k;
+
+ // From model A space to model B space
+ Invert(matrixB, matAB);
+ matAB *= matrixA;
+
+ CVuVector bsphereAB; // bounding sphere of A in B space
+ TransformPoint(bsphereAB, matAB, *(RwV3d*)modelA.boundingSphere.center); // inlined
+ bsphereAB.w = modelA.boundingSphere.radius;
+ if(!TestSphereBox(*(CColSphere*)&bsphereAB, modelB.boundingBox))
+ return 0;
+
+ // transform modelA's spheres and lines to B space
+ TransformPoints(aSpheresA, modelA.numSpheres, matAB, (RwV3d*)&modelA.spheres->center, sizeof(CColSphere));
+ for(i = 0; i < modelA.numSpheres; i++)
+ aSpheresA[i].w = modelA.spheres[i].radius;
+ TransformPoints(aLinesA, modelA.numLines*2, matAB, (RwV3d*)&modelA.lines->p0, sizeof(CColLine)/2);
+
+ // Test them against model B's bounding volumes
+ int numSpheresA = 0;
+ for(i = 0; i < modelA.numSpheres; i++)
+ if(TestSphereBox(*(CColSphere*)&aSpheresA[i], modelB.boundingBox))
+ aSphereIndicesA[numSpheresA++] = i;
+ // No collision
+ if(numSpheresA == 0 && modelA.numLines == 0)
+ return 0;
+
+
+ // B to A space
+ Invert(matrixA, matBA);
+ matBA *= matrixB;
+
+ // transform modelB's spheres to A space
+ TransformPoints(aSpheresB, modelB.numSpheres, matBA, (RwV3d*)&modelB.spheres->center, sizeof(CColSphere));
+ for(i = 0; i < modelB.numSpheres; i++)
+ aSpheresB[i].w = modelB.spheres[i].radius;
+
+ // Check model B against A's bounding volumes
+ int numSpheresB = 0;
+ int numBoxesB = 0;
+ int numTrianglesB = 0;
+ for(i = 0; i < modelB.numSpheres; i++)
+ if(TestSphereBox(*(CColSphere*)&aSpheresB[i], modelA.boundingBox))
+ aSphereIndicesB[numSpheresB++] = i;
+ for(i = 0; i < modelB.numBoxes; i++)
+ if(TestSphereBox(*(CColSphere*)&bsphereAB, modelB.boxes[i]))
+ aBoxIndicesB[numBoxesB++] = i;
+ CalculateTrianglePlanes(&modelB);
+ if(modelB.numTriangles){
+ VuTriangle vutri;
+ // process the first triangle
+ CColTriangle *tri = &modelB.triangles[0];
+ modelB.vertices[tri->a].Unpack(vutri.v0);
+ modelB.vertices[tri->b].Unpack(vutri.v1);
+ modelB.vertices[tri->c].Unpack(vutri.v2);
+ modelB.trianglePlanes[0].Unpack(vutri.plane);
+
+ SphereToTriangleCollisionCompressed(bsphereAB, vutri);
+
+ for(i = 1; i < modelB.numTriangles; i++){
+ // set up the next triangle while VU0 is running
+ tri = &modelB.triangles[i];
+ modelB.vertices[tri->a].Unpack(vutri.v0);
+ modelB.vertices[tri->b].Unpack(vutri.v1);
+ modelB.vertices[tri->c].Unpack(vutri.v2);
+ modelB.trianglePlanes[i].Unpack(vutri.plane);
+
+ // check previous result
+ if(GetVUresult())
+ aTriangleIndicesB[numTrianglesB++] = i-1;
+
+ // kick off this one
+ SphereToTriangleCollisionCompressed(bsphereAB, vutri);
+ }
+
+ // check last result
+ if(GetVUresult())
+ aTriangleIndicesB[numTrianglesB++] = i-1;
+ }
+ // No collision
+ if(numSpheresB == 0 && numBoxesB == 0 && numTrianglesB == 0)
+ return 0;
+
+ // We now have the collision volumes in A and B that are worth processing.
+
+ // Process A's spheres against B's collision volumes
+ int numCollisions = 0;
+ spherepoints[numCollisions].depth = -1.0f;
+ for(i = 0; i < numSpheresA; i++){
+ float coldist = 1.0e24f;
+ bool hasCollided = false;
+ CColSphere *sphA = &modelA.spheres[aSphereIndicesA[i]];
+ CVuVector *vusphA = &aSpheresA[aSphereIndicesA[i]];
+
+ for(j = 0; j < numSpheresB; j++)
+ // This actually looks like something was inlined here
+ if(ProcessSphereSphere(*(CColSphere*)vusphA, modelB.spheres[aSphereIndicesB[j]],
+ spherepoints[numCollisions], coldist)){
+ spherepoints[numCollisions].Set(
+ sphA->surface, sphA->piece,
+ modelB.spheres[aSphereIndicesB[j]].surface, modelB.spheres[aSphereIndicesB[j]].piece);
+ hasCollided = true;
+ }
+ for(j = 0; j < numBoxesB; j++)
+ if(ProcessSphereBox(*(CColSphere*)vusphA, modelB.boxes[aBoxIndicesB[j]],
+ spherepoints[numCollisions], coldist)){
+ spherepoints[numCollisions].Set(
+ sphA->surface, sphA->piece,
+ modelB.boxes[aBoxIndicesB[j]].surface, modelB.boxes[aBoxIndicesB[j]].piece);
+ hasCollided = true;
+ }
+ if(numTrianglesB){
+ CVuVector point, normal;
+ float depth;
+ bool registeredCol;
+ CColTriangle *lasttri;
+
+ VuTriangle vutri;
+ // process the first triangle
+ k = aTriangleIndicesB[0];
+ CColTriangle *tri = &modelB.triangles[k];
+ modelB.vertices[tri->a].Unpack(vutri.v0);
+ modelB.vertices[tri->b].Unpack(vutri.v1);
+ modelB.vertices[tri->c].Unpack(vutri.v2);
+ modelB.trianglePlanes[k].Unpack(vutri.plane);
+
+ SphereToTriangleCollisionCompressed(*vusphA, vutri);
+ lasttri = tri;
+
+ for(j = 1; j < numTrianglesB; j++){
+ k = aTriangleIndicesB[j];
+ // set up the next triangle while VU0 is running
+ tri = &modelB.triangles[k];
+ modelB.vertices[tri->a].Unpack(vutri.v0);
+ modelB.vertices[tri->b].Unpack(vutri.v1);
+ modelB.vertices[tri->c].Unpack(vutri.v2);
+ modelB.trianglePlanes[k].Unpack(vutri.plane);
+
+ // check previous result
+ // TODO: this looks inlined but spherepoints[numCollisions] does not...
+ if(GetVUresult(point, normal, depth)){
+ depth = sphA->radius - depth;
+ if(depth > spherepoints[numCollisions].depth){
+ spherepoints[numCollisions].point = point;
+ spherepoints[numCollisions].normal = normal;
+ spherepoints[numCollisions].Set(depth,
+ sphA->surface, sphA->piece, lasttri->surface, 0);
+ registeredCol = true;
+ }else
+ registeredCol = false;
+ }else
+ registeredCol = false;
+
+ if(registeredCol)
+ hasCollided = true;
+
+ // kick off this one
+ SphereToTriangleCollisionCompressed(*vusphA, vutri);
+ lasttri = tri;
+ }
+
+ // check last result
+ // TODO: this looks inlined but spherepoints[numCollisions] does not...
+ if(GetVUresult(point, normal, depth)){
+ depth = sphA->radius - depth;
+ if(depth > spherepoints[numCollisions].depth){
+ spherepoints[numCollisions].point = point;
+ spherepoints[numCollisions].normal = normal;
+ spherepoints[numCollisions].Set(depth,
+ sphA->surface, sphA->piece, lasttri->surface, 0);
+ registeredCol = true;
+ }else
+ registeredCol = false;
+ }else
+ registeredCol = false;
+
+ if(registeredCol)
+ hasCollided = true;
+ }
+
+ if(hasCollided){
+ numCollisions++;
+ if(numCollisions == MAX_COLLISION_POINTS)
+ break;
+ spherepoints[numCollisions].depth = -1.0f;
+ }
+ }
+ for(i = 0; i < numCollisions; i++){
+ // TODO: both VU0 macros
+ spherepoints[i].point = matrixB * spherepoints[i].point;
+ spherepoints[i].normal = Multiply3x3(matrixB, spherepoints[i].normal);
+ }
+
+ // And the same thing for the lines in A
+ for(i = 0; i < modelA.numLines; i++){
+ aCollided[i] = false;
+ CVuVector *lineA = &aLinesA[i*2];
+
+ for(j = 0; j < numSpheresB; j++)
+ if(ProcessLineSphere(*(CColLine*)lineA, modelB.spheres[aSphereIndicesB[j]],
+ linepoints[i], linedists[i])){
+ linepoints[i].Set(0, 0,
+#ifdef FIX_BUGS
+ modelB.spheres[aSphereIndicesB[j]].surface, modelB.spheres[aSphereIndicesB[j]].piece);
+#else
+ modelB.spheres[j].surface, modelB.spheres[j].piece);
+#endif
+ aCollided[i] = true;
+ }
+ for(j = 0; j < numBoxesB; j++)
+ if(ProcessLineBox(*(CColLine*)lineA, modelB.boxes[aBoxIndicesB[j]],
+ linepoints[i], linedists[i])){
+ linepoints[i].Set(0, 0,
+ modelB.boxes[aBoxIndicesB[j]].surface, modelB.boxes[aBoxIndicesB[j]].piece);
+ aCollided[i] = true;
+ }
+ if(numTrianglesB){
+ CVuVector point, normal;
+ float dist;
+ bool registeredCol;
+ CColTriangle *lasttri;
+
+ VuTriangle vutri;
+ // process the first triangle
+ k = aTriangleIndicesB[0];
+ CColTriangle *tri = &modelB.triangles[k];
+ modelB.vertices[tri->a].Unpack(vutri.v0);
+ modelB.vertices[tri->b].Unpack(vutri.v1);
+ modelB.vertices[tri->c].Unpack(vutri.v2);
+ modelB.trianglePlanes[k].Unpack(vutri.plane);
+
+ LineToTriangleCollisionCompressed(lineA[0], lineA[1], vutri);
+ lasttri = tri;
+
+ for(j = 1; j < numTrianglesB; j++){
+ k = aTriangleIndicesB[j];
+ // set up the next triangle while VU0 is running
+ CColTriangle *tri = &modelB.triangles[k];
+ modelB.vertices[tri->a].Unpack(vutri.v0);
+ modelB.vertices[tri->b].Unpack(vutri.v1);
+ modelB.vertices[tri->c].Unpack(vutri.v2);
+ modelB.trianglePlanes[k].Unpack(vutri.plane);
+
+ // check previous result
+ // TODO: this again somewhat looks inlined
+ if(GetVUresult(point, normal, dist)){
+ if(dist < linedists[i]){
+ linepoints[i].point = point;
+ linepoints[i].normal = normal;
+ linedists[i] = dist;
+ linepoints[i].Set(0, 0, lasttri->surface, 0);
+ registeredCol = true;
+ }else
+ registeredCol = false;
+ }else
+ registeredCol = false;
+
+ if(registeredCol)
+ aCollided[i] = true;
+
+ // kick of this one
+ LineToTriangleCollisionCompressed(lineA[0], lineA[1], vutri);
+ lasttri = tri;
+ }
+
+ // check last result
+ if(GetVUresult(point, normal, dist)){
+ if(dist < linedists[i]){
+ linepoints[i].point = point;
+ linepoints[i].normal = normal;
+ linedists[i] = dist;
+ linepoints[i].Set(0, 0, lasttri->surface, 0);
+ registeredCol = true;
+ }else
+ registeredCol = false;
+ }else
+ registeredCol = false;
+
+ if(registeredCol)
+ aCollided[i] = true;
+ }
+
+ if(aCollided[i]){
+ // TODO: both VU0 macros
+ linepoints[i].point = matrixB * linepoints[i].point;
+ linepoints[i].normal = Multiply3x3(matrixB, linepoints[i].normal);
+ }
+ }
+
+ return numCollisions; // sphere collisions
+#else
+ static int aSphereIndicesA[MAXNUMSPHERES];
+ static int aLineIndicesA[MAXNUMLINES];
+ static int aSphereIndicesB[MAXNUMSPHERES];
+ static int aBoxIndicesB[MAXNUMBOXES];
+ static int aTriangleIndicesB[MAXNUMTRIS];
+ static bool aCollided[MAXNUMLINES];
+ static CColSphere aSpheresA[MAXNUMSPHERES];
+ static CColLine aLinesA[MAXNUMLINES];
+ static CMatrix matAB, matBA;
+ CColSphere s;
+ int i, j;
+
+ assert(modelA.numSpheres <= MAXNUMSPHERES);
+ assert(modelA.numLines <= MAXNUMLINES);
+
+ // From model A space to model B space
+ matAB = Invert(matrixB, matAB);
+ matAB *= matrixA;
+
+ CColSphere bsphereAB; // bounding sphere of A in B space
+ bsphereAB.radius = modelA.boundingSphere.radius;
+ bsphereAB.center = matAB * modelA.boundingSphere.center;
+ if(!TestSphereBox(bsphereAB, modelB.boundingBox))
+ return 0;
+ // B to A space
+ matBA = Invert(matrixA, matBA);
+ matBA *= matrixB;
+
+ // transform modelA's spheres and lines to B space
+ for(i = 0; i < modelA.numSpheres; i++){
+ CColSphere &s = modelA.spheres[i];
+ aSpheresA[i].Set(s.radius, matAB * s.center, s.surface, s.piece);
+ }
+ for(i = 0; i < modelA.numLines; i++)
+ aLinesA[i].Set(matAB * modelA.lines[i].p0, matAB * modelA.lines[i].p1);
+
+ // Test them against model B's bounding volumes
+ int numSpheresA = 0;
+ int numLinesA = 0;
+ for(i = 0; i < modelA.numSpheres; i++)
+ if(TestSphereBox(aSpheresA[i], modelB.boundingBox))
+ aSphereIndicesA[numSpheresA++] = i;
+ // no actual check???
+ for(i = 0; i < modelA.numLines; i++)
+ aLineIndicesA[numLinesA++] = i;
+ // No collision
+ if(numSpheresA == 0 && numLinesA == 0)
+ return 0;
+
+ // Check model B against A's bounding volumes
+ int numSpheresB = 0;
+ int numBoxesB = 0;
+ int numTrianglesB = 0;
+ for(i = 0; i < modelB.numSpheres; i++){
+ s.radius = modelB.spheres[i].radius;
+ s.center = matBA * modelB.spheres[i].center;
+ if(TestSphereBox(s, modelA.boundingBox))
+ aSphereIndicesB[numSpheresB++] = i;
+ }
+ for(i = 0; i < modelB.numBoxes; i++)
+ if(TestSphereBox(bsphereAB, modelB.boxes[i]))
+ aBoxIndicesB[numBoxesB++] = i;
+ CalculateTrianglePlanes(&modelB);
+ for(i = 0; i < modelB.numTriangles; i++)
+ if(TestSphereTriangle(bsphereAB, modelB.vertices, modelB.triangles[i], modelB.trianglePlanes[i]))
+ aTriangleIndicesB[numTrianglesB++] = i;
+ assert(numSpheresB <= MAXNUMSPHERES);
+ assert(numBoxesB <= MAXNUMBOXES);
+ assert(numTrianglesB <= MAXNUMTRIS);
+ // No collision
+ if(numSpheresB == 0 && numBoxesB == 0 && numTrianglesB == 0)
+ return 0;
+
+ // We now have the collision volumes in A and B that are worth processing.
+
+ // Process A's spheres against B's collision volumes
+ int numCollisions = 0;
+ for(i = 0; i < numSpheresA; i++){
+ float coldist = 1.0e24f;
+ bool hasCollided = false;
+
+ for(j = 0; j < numSpheresB; j++)
+ hasCollided |= ProcessSphereSphere(
+ aSpheresA[aSphereIndicesA[i]],
+ modelB.spheres[aSphereIndicesB[j]],
+ spherepoints[numCollisions], coldist);
+ for(j = 0; j < numBoxesB; j++)
+ hasCollided |= ProcessSphereBox(
+ aSpheresA[aSphereIndicesA[i]],
+ modelB.boxes[aBoxIndicesB[j]],
+ spherepoints[numCollisions], coldist);
+ for(j = 0; j < numTrianglesB; j++)
+ hasCollided |= ProcessSphereTriangle(
+ aSpheresA[aSphereIndicesA[i]],
+ modelB.vertices,
+ modelB.triangles[aTriangleIndicesB[j]],
+ modelB.trianglePlanes[aTriangleIndicesB[j]],
+ spherepoints[numCollisions], coldist);
+
+ if(hasCollided)
+ numCollisions++;
+ }
+ for(i = 0; i < numCollisions; i++){
+ spherepoints[i].point = matrixB * spherepoints[i].point;
+ spherepoints[i].normal = Multiply3x3(matrixB, spherepoints[i].normal);
+ }
+
+ // And the same thing for the lines in A
+ for(i = 0; i < numLinesA; i++){
+ aCollided[i] = false;
+
+ for(j = 0; j < numSpheresB; j++)
+ aCollided[i] |= ProcessLineSphere(
+ aLinesA[aLineIndicesA[i]],
+ modelB.spheres[aSphereIndicesB[j]],
+ linepoints[aLineIndicesA[i]],
+ linedists[aLineIndicesA[i]]);
+ for(j = 0; j < numBoxesB; j++)
+ aCollided[i] |= ProcessLineBox(
+ aLinesA[aLineIndicesA[i]],
+ modelB.boxes[aBoxIndicesB[j]],
+ linepoints[aLineIndicesA[i]],
+ linedists[aLineIndicesA[i]]);
+ for(j = 0; j < numTrianglesB; j++)
+ aCollided[i] |= ProcessLineTriangle(
+ aLinesA[aLineIndicesA[i]],
+ modelB.vertices,
+ modelB.triangles[aTriangleIndicesB[j]],
+ modelB.trianglePlanes[aTriangleIndicesB[j]],
+ linepoints[aLineIndicesA[i]],
+ linedists[aLineIndicesA[i]]);
+ }
+ for(i = 0; i < numLinesA; i++)
+ if(aCollided[i]){
+ j = aLineIndicesA[i];
+ linepoints[j].point = matrixB * linepoints[j].point;
+ linepoints[j].normal = Multiply3x3(matrixB, linepoints[j].normal);
+ }
+
+ return numCollisions; // sphere collisions
+#endif
+}
+
+
+//
+// Misc
+//
+
+float
+CCollision::DistToLine(const CVector *l0, const CVector *l1, const CVector *point)
+{
+ float lensq = (*l1 - *l0).MagnitudeSqr();
+ float dot = DotProduct(*point - *l0, *l1 - *l0);
+ // Between 0 and len we're above the line.
+ // if not, calculate distance to endpoint
+ if(dot <= 0.0f)
+ return (*point - *l0).Magnitude();
+ if(dot >= lensq)
+ return (*point - *l1).Magnitude();
+ // distance to line
+ return Sqrt((*point - *l0).MagnitudeSqr() - dot*dot/lensq);
+}
+
+// same as above but also return the point on the line
+float
+CCollision::DistToLine(const CVector *l0, const CVector *l1, const CVector *point, CVector &closest)
+{
+ float lensq = (*l1 - *l0).MagnitudeSqr();
+ float dot = DotProduct(*point - *l0, *l1 - *l0);
+ // find out which point we're closest to
+ if(dot <= 0.0f)
+ closest = *l0;
+ else if(dot >= lensq)
+ closest = *l1;
+ else
+ closest = *l0 + (*l1 - *l0)*(dot/lensq);
+ // this is the distance
+ return (*point - closest).Magnitude();
+}
+
+void
+CCollision::CalculateTrianglePlanes(CColModel *model)
+{
+ assert(model);
+ if(model->numTriangles == 0)
+ return;
+
+ CLink<CColModel*> *lptr;
+ if(model->trianglePlanes){
+ // re-insert at front so it's not removed again soon
+ lptr = model->GetLinkPtr();
+ lptr->Remove();
+ ms_colModelCache.head.Insert(lptr);
+ }else{
+ lptr = ms_colModelCache.Insert(model);
+ if(lptr == nil){
+ // make room if we have to, remove last in list
+ lptr = ms_colModelCache.tail.prev;
+ assert(lptr);
+ assert(lptr->item);
+ lptr->item->RemoveTrianglePlanes();
+ ms_colModelCache.Remove(lptr);
+ // now this cannot fail
+ lptr = ms_colModelCache.Insert(model);
+ assert(lptr);
+ }
+ model->CalculateTrianglePlanes();
+ model->SetLinkPtr(lptr);
+ }
+}
+
+void
+CCollision::DrawColModel(const CMatrix &mat, const CColModel &colModel)
+{
+ int i;
+ CVector min, max;
+ CVector verts[8];
+ CVector c;
+ float r;
+
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil);
+
+ min = colModel.boundingBox.min;
+ max = colModel.boundingBox.max;
+
+ verts[0] = mat * CVector(min.x, min.y, min.z);
+ verts[1] = mat * CVector(min.x, min.y, max.z);
+ verts[2] = mat * CVector(min.x, max.y, min.z);
+ verts[3] = mat * CVector(min.x, max.y, max.z);
+ verts[4] = mat * CVector(max.x, min.y, min.z);
+ verts[5] = mat * CVector(max.x, min.y, max.z);
+ verts[6] = mat * CVector(max.x, max.y, min.z);
+ verts[7] = mat * CVector(max.x, max.y, max.z);
+
+ CLines::RenderLineWithClipping(
+ verts[0].x, verts[0].y, verts[0].z,
+ verts[1].x, verts[1].y, verts[1].z,
+ 0xFF0000FF, 0xFF0000FF);
+ CLines::RenderLineWithClipping(
+ verts[1].x, verts[1].y, verts[1].z,
+ verts[3].x, verts[3].y, verts[3].z,
+ 0xFF0000FF, 0xFF0000FF);
+ CLines::RenderLineWithClipping(
+ verts[3].x, verts[3].y, verts[3].z,
+ verts[2].x, verts[2].y, verts[2].z,
+ 0xFF0000FF, 0xFF0000FF);
+ CLines::RenderLineWithClipping(
+ verts[2].x, verts[2].y, verts[2].z,
+ verts[0].x, verts[0].y, verts[0].z,
+ 0xFF0000FF, 0xFF0000FF);
+
+ CLines::RenderLineWithClipping(
+ verts[4].x, verts[4].y, verts[4].z,
+ verts[5].x, verts[5].y, verts[5].z,
+ 0xFF0000FF, 0xFF0000FF);
+ CLines::RenderLineWithClipping(
+ verts[5].x, verts[5].y, verts[5].z,
+ verts[7].x, verts[7].y, verts[7].z,
+ 0xFF0000FF, 0xFF0000FF);
+ CLines::RenderLineWithClipping(
+ verts[7].x, verts[7].y, verts[7].z,
+ verts[6].x, verts[6].y, verts[6].z,
+ 0xFF0000FF, 0xFF0000FF);
+ CLines::RenderLineWithClipping(
+ verts[6].x, verts[6].y, verts[6].z,
+ verts[4].x, verts[4].y, verts[4].z,
+ 0xFF0000FF, 0xFF0000FF);
+
+ CLines::RenderLineWithClipping(
+ verts[0].x, verts[0].y, verts[0].z,
+ verts[4].x, verts[4].y, verts[4].z,
+ 0xFF0000FF, 0xFF0000FF);
+ CLines::RenderLineWithClipping(
+ verts[1].x, verts[1].y, verts[1].z,
+ verts[5].x, verts[5].y, verts[5].z,
+ 0xFF0000FF, 0xFF0000FF);
+ CLines::RenderLineWithClipping(
+ verts[2].x, verts[2].y, verts[2].z,
+ verts[6].x, verts[6].y, verts[6].z,
+ 0xFF0000FF, 0xFF0000FF);
+ CLines::RenderLineWithClipping(
+ verts[3].x, verts[3].y, verts[3].z,
+ verts[7].x, verts[7].y, verts[7].z,
+ 0xFF0000FF, 0xFF0000FF);
+
+ for(i = 0; i < colModel.numSpheres; i++){
+ c = mat * colModel.spheres[i].center;
+ r = colModel.spheres[i].radius;
+
+ CLines::RenderLineWithClipping(
+ c.x, c.y, c.z-r,
+ c.x-r, c.y-r, c.z,
+ 0xFF00FFFF, 0xFF00FFFF);
+ CLines::RenderLineWithClipping(
+ c.x, c.y, c.z-r,
+ c.x-r, c.y+r, c.z,
+ 0xFF00FFFF, 0xFF00FFFF);
+ CLines::RenderLineWithClipping(
+ c.x, c.y, c.z-r,
+ c.x+r, c.y-r, c.z,
+ 0xFF00FFFF, 0xFF00FFFF);
+ CLines::RenderLineWithClipping(
+ c.x, c.y, c.z-r,
+ c.x+r, c.y+r, c.z,
+ 0xFF00FFFF, 0xFF00FFFF);
+ CLines::RenderLineWithClipping(
+ c.x-r, c.y-r, c.z,
+ c.x, c.y, c.z+r,
+ 0xFF00FFFF, 0xFF00FFFF);
+ CLines::RenderLineWithClipping(
+ c.x-r, c.y+r, c.z,
+ c.x, c.y, c.z+r,
+ 0xFF00FFFF, 0xFF00FFFF);
+ CLines::RenderLineWithClipping(
+ c.x+r, c.y-r, c.z,
+ c.x, c.y, c.z+r,
+ 0xFF00FFFF, 0xFF00FFFF);
+ CLines::RenderLineWithClipping(
+ c.x+r, c.y+r, c.z,
+ c.x, c.y, c.z+r,
+ 0xFF00FFFF, 0xFF00FFFF);
+ }
+
+ for(i = 0; i < colModel.numLines; i++){
+ verts[0] = colModel.lines[i].p0;
+ verts[1] = colModel.lines[i].p1;
+
+ verts[0] = mat * verts[0];
+ verts[1] = mat * verts[1];
+
+ CLines::RenderLineWithClipping(
+ verts[0].x, verts[0].y, verts[0].z,
+ verts[1].x, verts[1].y, verts[1].z,
+ 0x00FFFFFF, 0x00FFFFFF);
+ }
+
+ for(i = 0; i < colModel.numBoxes; i++){
+ min = colModel.boxes[i].min;
+ max = colModel.boxes[i].max;
+
+ verts[0] = mat * CVector(min.x, min.y, min.z);
+ verts[1] = mat * CVector(min.x, min.y, max.z);
+ verts[2] = mat * CVector(min.x, max.y, min.z);
+ verts[3] = mat * CVector(min.x, max.y, max.z);
+ verts[4] = mat * CVector(max.x, min.y, min.z);
+ verts[5] = mat * CVector(max.x, min.y, max.z);
+ verts[6] = mat * CVector(max.x, max.y, min.z);
+ verts[7] = mat * CVector(max.x, max.y, max.z);
+
+ CLines::RenderLineWithClipping(
+ verts[0].x, verts[0].y, verts[0].z,
+ verts[1].x, verts[1].y, verts[1].z,
+ 0xFFFFFFFF, 0xFFFFFFFF);
+ CLines::RenderLineWithClipping(
+ verts[1].x, verts[1].y, verts[1].z,
+ verts[3].x, verts[3].y, verts[3].z,
+ 0xFFFFFFFF, 0xFFFFFFFF);
+ CLines::RenderLineWithClipping(
+ verts[3].x, verts[3].y, verts[3].z,
+ verts[2].x, verts[2].y, verts[2].z,
+ 0xFFFFFFFF, 0xFFFFFFFF);
+ CLines::RenderLineWithClipping(
+ verts[2].x, verts[2].y, verts[2].z,
+ verts[0].x, verts[0].y, verts[0].z,
+ 0xFFFFFFFF, 0xFFFFFFFF);
+
+ CLines::RenderLineWithClipping(
+ verts[4].x, verts[4].y, verts[4].z,
+ verts[5].x, verts[5].y, verts[5].z,
+ 0xFFFFFFFF, 0xFFFFFFFF);
+ CLines::RenderLineWithClipping(
+ verts[5].x, verts[5].y, verts[5].z,
+ verts[7].x, verts[7].y, verts[7].z,
+ 0xFFFFFFFF, 0xFFFFFFFF);
+ CLines::RenderLineWithClipping(
+ verts[7].x, verts[7].y, verts[7].z,
+ verts[6].x, verts[6].y, verts[6].z,
+ 0xFFFFFFFF, 0xFFFFFFFF);
+ CLines::RenderLineWithClipping(
+ verts[6].x, verts[6].y, verts[6].z,
+ verts[4].x, verts[4].y, verts[4].z,
+ 0xFFFFFFFF, 0xFFFFFFFF);
+
+ CLines::RenderLineWithClipping(
+ verts[0].x, verts[0].y, verts[0].z,
+ verts[4].x, verts[4].y, verts[4].z,
+ 0xFFFFFFFF, 0xFFFFFFFF);
+ CLines::RenderLineWithClipping(
+ verts[1].x, verts[1].y, verts[1].z,
+ verts[5].x, verts[5].y, verts[5].z,
+ 0xFFFFFFFF, 0xFFFFFFFF);
+ CLines::RenderLineWithClipping(
+ verts[2].x, verts[2].y, verts[2].z,
+ verts[6].x, verts[6].y, verts[6].z,
+ 0xFFFFFFFF, 0xFFFFFFFF);
+ CLines::RenderLineWithClipping(
+ verts[3].x, verts[3].y, verts[3].z,
+ verts[7].x, verts[7].y, verts[7].z,
+ 0xFFFFFFFF, 0xFFFFFFFF);
+ }
+
+ for(i = 0; i < colModel.numTriangles; i++){
+ colModel.GetTrianglePoint(verts[0], colModel.triangles[i].a);
+ colModel.GetTrianglePoint(verts[1], colModel.triangles[i].b);
+ colModel.GetTrianglePoint(verts[2], colModel.triangles[i].c);
+ verts[0] = mat * verts[0];
+ verts[1] = mat * verts[1];
+ verts[2] = mat * verts[2];
+ CLines::RenderLineWithClipping(
+ verts[0].x, verts[0].y, verts[0].z,
+ verts[1].x, verts[1].y, verts[1].z,
+ 0x00FF00FF, 0x00FF00FF);
+ CLines::RenderLineWithClipping(
+ verts[0].x, verts[0].y, verts[0].z,
+ verts[2].x, verts[2].y, verts[2].z,
+ 0x00FF00FF, 0x00FF00FF);
+ CLines::RenderLineWithClipping(
+ verts[1].x, verts[1].y, verts[1].z,
+ verts[2].x, verts[2].y, verts[2].z,
+ 0x00FF00FF, 0x00FF00FF);
+ }
+
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
+}
+
+void
+CCollision::DrawColModel_Coloured(const CMatrix &mat, const CColModel &colModel, int32 id)
+{
+ int i;
+ int s;
+ float f;
+ CVector verts[8];
+ CVector min, max;
+ int r, g, b;
+ RwImVertexIndex *iptr;
+ RwIm3DVertex *vptr;
+
+ RenderBuffer::ClearRenderBuffer();
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil);
+
+ for(i = 0; i < colModel.numTriangles; i++){
+ colModel.GetTrianglePoint(verts[0], colModel.triangles[i].a);
+ colModel.GetTrianglePoint(verts[1], colModel.triangles[i].b);
+ colModel.GetTrianglePoint(verts[2], colModel.triangles[i].c);
+ verts[0] = mat * verts[0];
+ verts[1] = mat * verts[1];
+ verts[2] = mat * verts[2];
+
+ // game doesn't do this
+ r = 255;
+ g = 128;
+ b = 0;
+
+ s = colModel.triangles[i].surface;
+ f = (s & 0xF)/32.0f + 0.5f;
+ switch(CSurfaceTable::GetAdhesionGroup(s)){
+ case ADHESIVE_RUBBER:
+ r = f * 255.0f;
+ g = 0;
+ b = 0;
+ break;
+ case ADHESIVE_HARD:
+ r = f*255.0f;
+ g = f*255.0f;
+ b = f*128.0f;
+ break;
+ case ADHESIVE_ROAD:
+ r = f*128.0f;
+ g = f*128.0f;
+ b = f*128.0f;
+ break;
+ case ADHESIVE_LOOSE:
+ r = 0;
+ g = f * 255.0f;
+ b = 0;
+ break;
+ case ADHESIVE_WET:
+ r = 0;
+ g = 0;
+ b = f * 255.0f;
+ break;
+ default:
+ // this doesn't make much sense
+ r *= f;
+ g *= f;
+ b *= f;
+ }
+
+ if(s == SURFACE_TRANSPARENT_CLOTH || s == SURFACE_METAL_CHAIN_FENCE ||
+ s == SURFACE_TRANSPARENT_STONE || s == SURFACE_SCAFFOLD_POLE)
+ if(CTimer::GetFrameCounter() & 1){
+ r = 0;
+ g = 0;
+ b = 0;
+ }
+
+ if(s > SURFACE_METAL_GATE){
+ r = CGeneral::GetRandomNumber();
+ g = CGeneral::GetRandomNumber();
+ b = CGeneral::GetRandomNumber();
+ printf("Illegal surfacetype:%d on MI:%d\n", s, id);
+ }
+
+ RenderBuffer::StartStoring(6, 3, &iptr, &vptr);
+ RwIm3DVertexSetRGBA(&vptr[0], r, g, b, 255);
+ RwIm3DVertexSetRGBA(&vptr[1], r, g, b, 255);
+ RwIm3DVertexSetRGBA(&vptr[2], r, g, b, 255);
+ RwIm3DVertexSetU(&vptr[0], 0.0f);
+ RwIm3DVertexSetV(&vptr[0], 0.0f);
+ RwIm3DVertexSetU(&vptr[1], 0.0f);
+ RwIm3DVertexSetV(&vptr[1], 1.0f);
+ RwIm3DVertexSetU(&vptr[2], 1.0f);
+ RwIm3DVertexSetV(&vptr[2], 1.0f);
+ RwIm3DVertexSetPos(&vptr[0], verts[0].x, verts[0].y, verts[0].z);
+ RwIm3DVertexSetPos(&vptr[1], verts[1].x, verts[1].y, verts[1].z);
+ RwIm3DVertexSetPos(&vptr[2], verts[2].x, verts[2].y, verts[2].z);
+ iptr[0] = 0; iptr[1] = 1; iptr[2] = 2;
+ iptr[3] = 0; iptr[4] = 2; iptr[5] = 1;
+ RenderBuffer::StopStoring();
+ }
+
+ for(i = 0; i < colModel.numBoxes; i++){
+ min = colModel.boxes[i].min;
+ max = colModel.boxes[i].max;
+
+ verts[0] = mat * CVector(min.x, min.y, min.z);
+ verts[1] = mat * CVector(min.x, min.y, max.z);
+ verts[2] = mat * CVector(min.x, max.y, min.z);
+ verts[3] = mat * CVector(min.x, max.y, max.z);
+ verts[4] = mat * CVector(max.x, min.y, min.z);
+ verts[5] = mat * CVector(max.x, min.y, max.z);
+ verts[6] = mat * CVector(max.x, max.y, min.z);
+ verts[7] = mat * CVector(max.x, max.y, max.z);
+
+ s = colModel.boxes[i].surface;
+ f = (s & 0xF)/32.0f + 0.5f;
+ switch(CSurfaceTable::GetAdhesionGroup(s)){
+ case ADHESIVE_RUBBER:
+ r = f * 255.0f;
+ g = 0;
+ b = 0;
+ break;
+ case ADHESIVE_HARD:
+ r = f*255.0f;
+ g = f*255.0f;
+ b = f*128.0f;
+ break;
+ case ADHESIVE_ROAD:
+ r = f*128.0f;
+ g = f*128.0f;
+ b = f*128.0f;
+ break;
+ case ADHESIVE_LOOSE:
+ r = 0;
+ g = f * 255.0f;
+ b = 0;
+ break;
+ case ADHESIVE_WET:
+ r = 0;
+ g = 0;
+ b = f * 255.0f;
+ break;
+ default:
+ // this doesn't make much sense
+ r *= f;
+ g *= f;
+ b *= f;
+ }
+
+ if(s == SURFACE_TRANSPARENT_CLOTH || s == SURFACE_METAL_CHAIN_FENCE ||
+ s == SURFACE_TRANSPARENT_STONE || s == SURFACE_SCAFFOLD_POLE)
+ if(CTimer::GetFrameCounter() & 1){
+ r = 0;
+ g = 0;
+ b = 0;
+ }
+
+ RenderBuffer::StartStoring(36, 8, &iptr, &vptr);
+ RwIm3DVertexSetRGBA(&vptr[0], r, g, b, 255);
+ RwIm3DVertexSetRGBA(&vptr[1], r, g, b, 255);
+ RwIm3DVertexSetRGBA(&vptr[2], r, g, b, 255);
+ RwIm3DVertexSetRGBA(&vptr[3], r, g, b, 255);
+ RwIm3DVertexSetRGBA(&vptr[4], r, g, b, 255);
+ RwIm3DVertexSetRGBA(&vptr[5], r, g, b, 255);
+ RwIm3DVertexSetRGBA(&vptr[6], r, g, b, 255);
+ RwIm3DVertexSetRGBA(&vptr[7], r, g, b, 255);
+ RwIm3DVertexSetU(&vptr[0], 0.0f);
+ RwIm3DVertexSetV(&vptr[0], 0.0f);
+ RwIm3DVertexSetU(&vptr[1], 0.0f);
+ RwIm3DVertexSetV(&vptr[1], 1.0f);
+ RwIm3DVertexSetU(&vptr[2], 1.0f);
+ RwIm3DVertexSetV(&vptr[2], 1.0f);
+ RwIm3DVertexSetU(&vptr[3], 0.0f);
+ RwIm3DVertexSetV(&vptr[3], 0.0f);
+ RwIm3DVertexSetU(&vptr[4], 0.0f);
+ RwIm3DVertexSetV(&vptr[4], 1.0f);
+ RwIm3DVertexSetU(&vptr[5], 1.0f);
+ RwIm3DVertexSetV(&vptr[5], 1.0f);
+ RwIm3DVertexSetU(&vptr[6], 0.0f);
+ RwIm3DVertexSetV(&vptr[6], 1.0f);
+ RwIm3DVertexSetU(&vptr[7], 1.0f);
+ RwIm3DVertexSetV(&vptr[7], 1.0f);
+ RwIm3DVertexSetPos(&vptr[0], verts[0].x, verts[0].y, verts[0].z);
+ RwIm3DVertexSetPos(&vptr[1], verts[1].x, verts[1].y, verts[1].z);
+ RwIm3DVertexSetPos(&vptr[2], verts[2].x, verts[2].y, verts[2].z);
+ RwIm3DVertexSetPos(&vptr[3], verts[3].x, verts[3].y, verts[3].z);
+ RwIm3DVertexSetPos(&vptr[4], verts[4].x, verts[4].y, verts[4].z);
+ RwIm3DVertexSetPos(&vptr[5], verts[5].x, verts[5].y, verts[5].z);
+ RwIm3DVertexSetPos(&vptr[6], verts[6].x, verts[6].y, verts[6].z);
+ RwIm3DVertexSetPos(&vptr[7], verts[7].x, verts[7].y, verts[7].z);
+ iptr[0] = 0; iptr[1] = 1; iptr[2] = 2;
+ iptr[3] = 1; iptr[4] = 3; iptr[5] = 2;
+ iptr[6] = 1; iptr[7] = 5; iptr[8] = 7;
+ iptr[9] = 1; iptr[10] = 7; iptr[11] = 3;
+ iptr[12] = 2; iptr[13] = 3; iptr[14] = 7;
+ iptr[15] = 2; iptr[16] = 7; iptr[17] = 6;
+ iptr[18] = 0; iptr[19] = 5; iptr[20] = 1;
+ iptr[21] = 0; iptr[22] = 4; iptr[23] = 5;
+ iptr[24] = 0; iptr[25] = 2; iptr[26] = 4;
+ iptr[27] = 2; iptr[28] = 6; iptr[29] = 4;
+ iptr[30] = 4; iptr[31] = 6; iptr[32] = 7;
+ iptr[33] = 4; iptr[34] = 7; iptr[35] = 5;
+ RenderBuffer::StopStoring();
+ }
+
+ RenderBuffer::RenderStuffInBuffer();
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
+} \ No newline at end of file
diff --git a/src/collision/Collision.h b/src/collision/Collision.h
new file mode 100644
index 00000000..f4270bc5
--- /dev/null
+++ b/src/collision/Collision.h
@@ -0,0 +1,70 @@
+#pragma once
+
+#include "ColModel.h"
+#include "Game.h" // for eLevelName
+#ifdef VU_COLLISION
+#include "VuVector.h"
+#endif
+
+struct CStoredCollPoly
+{
+#ifdef VU_COLLISION
+ CVuVector verts[3];
+#else
+ CVector verts[3];
+#endif
+ bool valid;
+};
+
+// If you spawn many tanks at once, you will see that collisions of two entity exceeds 32.
+#if defined(FIX_BUGS) && !defined(SQUEEZE_PERFORMANCE)
+#define MAX_COLLISION_POINTS 64
+#else
+#define MAX_COLLISION_POINTS 32
+#endif
+
+class CCollision
+{
+public:
+ static eLevelName ms_collisionInMemory;
+ static CLinkList<CColModel*> ms_colModelCache;
+#ifdef NO_ISLAND_LOADING
+ static bool bAlreadyLoaded;
+#endif
+
+ static void Init(void);
+ static void Shutdown(void);
+ static void Update(void);
+ static void LoadCollisionWhenINeedIt(bool changeLevel);
+ static void SortOutCollisionAfterLoad(void);
+ static void LoadCollisionScreen(eLevelName level);
+ static void DrawColModel(const CMatrix &mat, const CColModel &colModel);
+ static void DrawColModel_Coloured(const CMatrix &mat, const CColModel &colModel, int32 id);
+
+ static void CalculateTrianglePlanes(CColModel *model);
+
+ // all these return true if there's a collision
+ static bool TestSphereSphere(const CColSphere &s1, const CColSphere &s2);
+ static bool TestSphereBox(const CColSphere &sph, const CColBox &box);
+ static bool TestLineBox(const CColLine &line, const CColBox &box);
+ static bool TestVerticalLineBox(const CColLine &line, const CColBox &box);
+ static bool TestLineTriangle(const CColLine &line, const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane);
+ static bool TestLineSphere(const CColLine &line, const CColSphere &sph);
+ static bool TestSphereTriangle(const CColSphere &sphere, const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane);
+ static bool TestLineOfSight(const CColLine &line, const CMatrix &matrix, CColModel &model, bool ignoreSeeThrough);
+
+ static bool ProcessSphereSphere(const CColSphere &s1, const CColSphere &s2, CColPoint &point, float &mindistsq);
+ static bool ProcessSphereBox(const CColSphere &sph, const CColBox &box, CColPoint &point, float &mindistsq);
+ static bool ProcessLineBox(const CColLine &line, const CColBox &box, CColPoint &point, float &mindist);
+ static bool ProcessVerticalLineTriangle(const CColLine &line, const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane, CColPoint &point, float &mindist, CStoredCollPoly *poly);
+ static bool ProcessLineTriangle(const CColLine &line , const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane, CColPoint &point, float &mindist);
+ static bool ProcessLineSphere(const CColLine &line, const CColSphere &sphere, CColPoint &point, float &mindist);
+ static bool ProcessSphereTriangle(const CColSphere &sph, const CompressedVector *verts, const CColTriangle &tri, const CColTrianglePlane &plane, CColPoint &point, float &mindistsq);
+ static bool ProcessLineOfSight(const CColLine &line, const CMatrix &matrix, CColModel &model, CColPoint &point, float &mindist, bool ignoreSeeThrough);
+ static bool ProcessVerticalLine(const CColLine &line, const CMatrix &matrix, CColModel &model, CColPoint &point, float &mindist, bool ignoreSeeThrough, CStoredCollPoly *poly);
+ static int32 ProcessColModels(const CMatrix &matrixA, CColModel &modelA, const CMatrix &matrixB, CColModel &modelB, CColPoint *spherepoints, CColPoint *linepoints, float *linedists);
+ static bool IsStoredPolyStillValidVerticalLine(const CVector &pos, float z, CColPoint &point, CStoredCollPoly *poly);
+
+ static float DistToLine(const CVector *l0, const CVector *l1, const CVector *point);
+ static float DistToLine(const CVector *l0, const CVector *l1, const CVector *point, CVector &closest);
+};
diff --git a/src/collision/CompressedVector.h b/src/collision/CompressedVector.h
new file mode 100644
index 00000000..d54e49b1
--- /dev/null
+++ b/src/collision/CompressedVector.h
@@ -0,0 +1,36 @@
+#pragma once
+
+struct CompressedVector
+{
+#ifdef COMPRESSED_COL_VECTORS
+ int16 x, y, z;
+ CVector Get(void) const { return CVector(x, y, z)/128.0f; };
+ void Set(float x, float y, float z) { this->x = x*128.0f; this->y = y*128.0f; this->z = z*128.0f; };
+#ifdef GTA_PS2
+ void Unpack(uint128 &qword) const {
+ __asm__ volatile (
+ "lh $8, 0(%1)\n"
+ "lh $9, 2(%1)\n"
+ "lh $10, 4(%1)\n"
+ "pextlw $10, $8\n"
+ "pextlw $2, $9, $10\n"
+ "sq $2, %0\n"
+ : "=m" (qword)
+ : "r" (this)
+ : "$8", "$9", "$10", "$2"
+ );
+ }
+#else
+ void Unpack(int32 *qword) const {
+ qword[0] = x;
+ qword[1] = y;
+ qword[2] = z;
+ qword[3] = 0; // junk
+ }
+#endif
+#else
+ float x, y, z;
+ CVector Get(void) const { return CVector(x, y, z); };
+ void Set(float x, float y, float z) { this->x = x; this->y = y; this->z = z; };
+#endif
+}; \ No newline at end of file
diff --git a/src/collision/TempColModels.cpp b/src/collision/TempColModels.cpp
new file mode 100644
index 00000000..849eb01e
--- /dev/null
+++ b/src/collision/TempColModels.cpp
@@ -0,0 +1,296 @@
+#include "common.h"
+
+#include "TempColModels.h"
+
+CColModel CTempColModels::ms_colModelPed1;
+CColModel CTempColModels::ms_colModelPed2;
+CColModel CTempColModels::ms_colModelBBox;
+CColModel CTempColModels::ms_colModelBumper1;
+CColModel CTempColModels::ms_colModelWheel1;
+CColModel CTempColModels::ms_colModelPanel1;
+CColModel CTempColModels::ms_colModelBodyPart2;
+CColModel CTempColModels::ms_colModelBodyPart1;
+CColModel CTempColModels::ms_colModelCutObj[5];
+CColModel CTempColModels::ms_colModelPedGroundHit;
+CColModel CTempColModels::ms_colModelBoot1;
+CColModel CTempColModels::ms_colModelDoor1;
+CColModel CTempColModels::ms_colModelBonnet1;
+
+
+CColSphere s_aPedSpheres[3];
+CColSphere s_aPed2Spheres[3];
+CColSphere s_aPedGSpheres[4];
+#ifdef FIX_BUGS
+CColSphere s_aDoorSpheres[3];
+#else
+CColSphere s_aDoorSpheres[4];
+#endif
+CColSphere s_aBumperSpheres[4];
+CColSphere s_aPanelSpheres[4];
+CColSphere s_aBonnetSpheres[4];
+CColSphere s_aBootSpheres[4];
+CColSphere s_aWheelSpheres[2];
+CColSphere s_aBodyPartSpheres1[2];
+CColSphere s_aBodyPartSpheres2[2];
+
+void
+CTempColModels::Initialise(void)
+{
+#define SET_COLMODEL_SPHERES(colmodel, sphrs)\
+ colmodel.numSpheres = ARRAY_SIZE(sphrs);\
+ colmodel.spheres = sphrs;\
+ colmodel.level = LEVEL_GENERIC;\
+ colmodel.ownsCollisionVolumes = false;\
+
+ int i;
+
+ ms_colModelBBox.boundingSphere.Set(2.0f, CVector(0.0f, 0.0f, 0.0f));
+ ms_colModelBBox.boundingBox.Set(CVector(-2.0f, -2.0f, -2.0f), CVector(2.0f, 2.0f, 2.0f));
+ ms_colModelBBox.level = LEVEL_GENERIC;
+
+ for (i = 0; i < ARRAY_SIZE(ms_colModelCutObj); i++) {
+ ms_colModelCutObj[i].boundingSphere.Set(2.0f, CVector(0.0f, 0.0f, 0.0f));
+ ms_colModelCutObj[i].boundingBox.Set(CVector(-2.0f, -2.0f, -2.0f), CVector(2.0f, 2.0f, 2.0f));
+ ms_colModelCutObj[i].level = LEVEL_GENERIC;
+ }
+
+ // Ped Spheres
+
+ for (i = 0; i < ARRAY_SIZE(s_aPedSpheres); i++)
+ s_aPedSpheres[i].radius = 0.35f;
+
+ s_aPedSpheres[0].center = CVector(0.0f, 0.0f, -0.25f);
+ s_aPedSpheres[1].center = CVector(0.0f, 0.0f, 0.15f);
+ s_aPedSpheres[2].center = CVector(0.0f, 0.0f, 0.55f);
+
+#ifdef FIX_BUGS
+ for (i = 0; i < ARRAY_SIZE(s_aPedSpheres); i++) {
+#else
+ for (i = 0; i < ARRAY_SIZE(s_aPedGSpheres); i++) {
+#endif
+ s_aPedSpheres[i].surface = SURFACE_PED;
+ s_aPedSpheres[i].piece = 0;
+ }
+
+ ms_colModelPed1.boundingSphere.Set(1.25f, CVector(0.0f, 0.0f, 0.0f));
+ ms_colModelPed1.boundingBox.Set(CVector(-0.35f, -0.35f, -1.0f), CVector(0.35f, 0.35f, 0.9f));
+ SET_COLMODEL_SPHERES(ms_colModelPed1, s_aPedSpheres);
+
+ // Ped 2 Spheres
+
+ s_aPed2Spheres[0].radius = 0.3f;
+ s_aPed2Spheres[1].radius = 0.4f;
+ s_aPed2Spheres[2].radius = 0.3f;
+
+ s_aPed2Spheres[0].center = CVector(0.0f, 0.35f, -0.9f);
+ s_aPed2Spheres[1].center = CVector(0.0f, 0.0f, -0.9f);
+ s_aPed2Spheres[2].center = CVector(0.0f, -0.35f, -0.9f);
+
+ for (i = 0; i < ARRAY_SIZE(s_aPed2Spheres); i++) {
+ s_aPed2Spheres[i].surface = SURFACE_PED;
+ s_aPed2Spheres[i].piece = 0;
+ }
+
+ ms_colModelPed2.boundingSphere.Set(2.0f, CVector(0.0f, 0.0f, 0.0f));
+ ms_colModelPed2.boundingBox.Set(CVector(-0.7f, -0.7f, -1.2f), CVector(0.7f, 0.7f, 0.0f));
+
+ SET_COLMODEL_SPHERES(ms_colModelPed2, s_aPed2Spheres);
+
+ // Ped ground collision
+
+ s_aPedGSpheres[0].radius = 0.35f;
+ s_aPedGSpheres[1].radius = 0.35f;
+ s_aPedGSpheres[2].radius = 0.35f;
+ s_aPedGSpheres[3].radius = 0.3f;
+
+ s_aPedGSpheres[0].center = CVector(0.0f, -0.4f, -0.9f);
+ s_aPedGSpheres[1].center = CVector(0.0f, -0.1f, -0.9f);
+ s_aPedGSpheres[2].center = CVector(0.0f, 0.25f, -0.9f);
+ s_aPedGSpheres[3].center = CVector(0.0f, 0.65f, -0.9f);
+
+ s_aPedGSpheres[0].surface = SURFACE_PED;
+ s_aPedGSpheres[1].surface = SURFACE_PED;
+ s_aPedGSpheres[2].surface = SURFACE_PED;
+ s_aPedGSpheres[3].surface = SURFACE_PED;
+ s_aPedGSpheres[0].piece = 4;
+ s_aPedGSpheres[1].piece = 1;
+ s_aPedGSpheres[2].piece = 0;
+ s_aPedGSpheres[3].piece = 6;
+
+ ms_colModelPedGroundHit.boundingSphere.Set(2.0f, CVector(0.0f, 0.0f, 0.0f));
+ ms_colModelPedGroundHit.boundingBox.Set(CVector(-0.4f, -1.0f, -1.25f), CVector(0.4f, 1.2f, -0.5f));
+
+ SET_COLMODEL_SPHERES(ms_colModelPedGroundHit, s_aPedGSpheres);
+
+ // Door Spheres
+
+ s_aDoorSpheres[0].radius = 0.15f;
+ s_aDoorSpheres[1].radius = 0.15f;
+ s_aDoorSpheres[2].radius = 0.25f;
+
+ s_aDoorSpheres[0].center = CVector(0.0f, -0.25f, -0.35f);
+ s_aDoorSpheres[1].center = CVector(0.0f, -0.95f, -0.35f);
+ s_aDoorSpheres[2].center = CVector(0.0f, -0.6f, 0.25f);
+
+#ifdef FIX_BUGS
+ for (i = 0; i < ARRAY_SIZE(s_aDoorSpheres); i++) {
+#else
+ for (i = 0; i < ARRAY_SIZE(s_aPed2Spheres); i++) {
+#endif
+ s_aDoorSpheres[i].surface = SURFACE_CAR_PANEL;
+ s_aDoorSpheres[i].piece = 0;
+ }
+
+ ms_colModelDoor1.boundingSphere.Set(1.5f, CVector(0.0f, -0.6f, 0.0f));
+ ms_colModelDoor1.boundingBox.Set(CVector(-0.3f, 0.0f, -0.6f), CVector(0.3f, -1.2f, 0.6f));
+
+ SET_COLMODEL_SPHERES(ms_colModelDoor1, s_aDoorSpheres);
+
+ // Bumper Spheres
+
+ for (i = 0; i < ARRAY_SIZE(s_aBumperSpheres); i++)
+ s_aBumperSpheres[i].radius = 0.15f;
+
+ s_aBumperSpheres[0].center = CVector(0.85f, -0.05f, 0.0f);
+ s_aBumperSpheres[1].center = CVector(0.4f, 0.05f, 0.0f);
+ s_aBumperSpheres[2].center = CVector(-0.4f, 0.05f, 0.0f);
+ s_aBumperSpheres[3].center = CVector(-0.85f, -0.05f, 0.0f);
+
+ for (i = 0; i < ARRAY_SIZE(s_aBumperSpheres); i++) {
+ s_aBumperSpheres[i].surface = SURFACE_CAR_PANEL;
+ s_aBumperSpheres[i].piece = 0;
+ }
+
+ ms_colModelBumper1.boundingSphere.Set(2.2f, CVector(0.0f, -0.6f, 0.0f));
+ ms_colModelBumper1.boundingBox.Set(CVector(-1.2f, -0.3f, -0.2f), CVector(1.2f, 0.3f, 0.2f));
+
+ SET_COLMODEL_SPHERES(ms_colModelBumper1, s_aBumperSpheres);
+
+ // Panel Spheres
+
+ for (i = 0; i < ARRAY_SIZE(s_aPanelSpheres); i++)
+ s_aPanelSpheres[i].radius = 0.15f;
+
+ s_aPanelSpheres[0].center = CVector(0.15f, 0.45f, 0.0f);
+ s_aPanelSpheres[1].center = CVector(0.15f, -0.45f, 0.0f);
+ s_aPanelSpheres[2].center = CVector(-0.15f, -0.45f, 0.0f);
+ s_aPanelSpheres[3].center = CVector(-0.15f, 0.45f, 0.0f);
+
+ for (i = 0; i < ARRAY_SIZE(s_aPanelSpheres); i++) {
+ s_aPanelSpheres[i].surface = SURFACE_CAR_PANEL;
+ s_aPanelSpheres[i].piece = 0;
+ }
+
+ ms_colModelPanel1.boundingSphere.Set(1.4f, CVector(0.0f, 0.0f, 0.0f));
+ ms_colModelPanel1.boundingBox.Set(CVector(-0.3f, -0.6f, -0.15f), CVector(0.3f, 0.6f, 0.15f));
+
+ SET_COLMODEL_SPHERES(ms_colModelPanel1, s_aPanelSpheres);
+
+ // Bonnet Spheres
+
+ for (i = 0; i < ARRAY_SIZE(s_aBonnetSpheres); i++)
+ s_aBonnetSpheres[i].radius = 0.2f;
+
+ s_aBonnetSpheres[0].center = CVector(-0.4f, 0.1f, 0.0f);
+ s_aBonnetSpheres[1].center = CVector(-0.4f, 0.9f, 0.0f);
+ s_aBonnetSpheres[2].center = CVector(0.4f, 0.1f, 0.0f);
+ s_aBonnetSpheres[3].center = CVector(0.4f, 0.9f, 0.0f);
+
+ for (i = 0; i < ARRAY_SIZE(s_aBonnetSpheres); i++) {
+ s_aBonnetSpheres[i].surface = SURFACE_CAR_PANEL;
+ s_aBonnetSpheres[i].piece = 0;
+ }
+
+ ms_colModelBonnet1.boundingSphere.Set(1.7f, CVector(0.0f, 0.5f, 0.0f));
+ ms_colModelBonnet1.boundingBox.Set(CVector(-0.7f, -0.2f, -0.3f), CVector(0.7f, 1.2f, 0.3f));
+
+ SET_COLMODEL_SPHERES(ms_colModelBonnet1, s_aBonnetSpheres);
+
+ // Boot Spheres
+
+ for (i = 0; i < ARRAY_SIZE(s_aBootSpheres); i++)
+ s_aBootSpheres[i].radius = 0.2f;
+
+ s_aBootSpheres[0].center = CVector(-0.4f, -0.1f, 0.0f);
+ s_aBootSpheres[1].center = CVector(-0.4f, -0.6f, 0.0f);
+ s_aBootSpheres[2].center = CVector(0.4f, -0.1f, 0.0f);
+ s_aBootSpheres[3].center = CVector(0.4f, -0.6f, 0.0f);
+
+ for (i = 0; i < ARRAY_SIZE(s_aBootSpheres); i++) {
+ s_aBootSpheres[i].surface = SURFACE_CAR_PANEL;
+ s_aBootSpheres[i].piece = 0;
+ }
+
+ ms_colModelBoot1.boundingSphere.Set(1.4f, CVector(0.0f, -0.4f, 0.0f));
+ ms_colModelBoot1.boundingBox.Set(CVector(-0.7f, -0.9f, -0.3f), CVector(0.7f, 0.2f, 0.3f));
+
+ SET_COLMODEL_SPHERES(ms_colModelBoot1, s_aBootSpheres);
+
+ // Wheel Spheres
+
+ s_aWheelSpheres[0].radius = 0.35f;
+ s_aWheelSpheres[1].radius = 0.35f;
+
+ s_aWheelSpheres[0].center = CVector(-0.3f, 0.0f, 0.0f);
+ s_aWheelSpheres[1].center = CVector(0.3f, 0.0f, 0.0f);
+
+#ifdef FIX_BUGS
+ for (i = 0; i < ARRAY_SIZE(s_aWheelSpheres); i++) {
+#else
+ for (i = 0; i < ARRAY_SIZE(s_aBootSpheres); i++) {
+#endif
+ s_aWheelSpheres[i].surface = SURFACE_WHEELBASE;
+ s_aWheelSpheres[i].piece = 0;
+ }
+
+ ms_colModelWheel1.boundingSphere.Set(1.4f, CVector(0.0f, 0.0f, 0.0f));
+ ms_colModelWheel1.boundingBox.Set(CVector(-0.7f, -0.4f, -0.4f), CVector(0.7f, 0.4f, 0.4f));
+
+ SET_COLMODEL_SPHERES(ms_colModelWheel1, s_aWheelSpheres);
+
+ // Body Part Spheres 1
+
+ s_aBodyPartSpheres1[0].radius = 0.2f;
+ s_aBodyPartSpheres1[1].radius = 0.2f;
+
+ s_aBodyPartSpheres1[0].center = CVector(0.0f, 0.0f, 0.0f);
+ s_aBodyPartSpheres1[1].center = CVector(0.8f, 0.0f, 0.0f);
+
+#ifdef FIX_BUGS
+ for (i = 0; i < ARRAY_SIZE(s_aBodyPartSpheres1); i++) {
+#else
+ for (i = 0; i < ARRAY_SIZE(s_aBootSpheres); i++) {
+#endif
+ s_aBodyPartSpheres1[i].surface = SURFACE_PED;
+ s_aBodyPartSpheres1[i].piece = 0;
+ }
+
+ ms_colModelBodyPart1.boundingSphere.Set(0.7f, CVector(0.4f, 0.0f, 0.0f));
+ ms_colModelBodyPart1.boundingBox.Set(CVector(-0.3f, -0.3f, -0.3f), CVector(1.1f, 0.3f, 0.3f));
+
+ SET_COLMODEL_SPHERES(ms_colModelBodyPart1, s_aBodyPartSpheres1);
+
+ // Body Part Spheres 2
+
+ s_aBodyPartSpheres2[0].radius = 0.15f;
+ s_aBodyPartSpheres2[1].radius = 0.15f;
+
+ s_aBodyPartSpheres2[0].center = CVector(0.0f, 0.0f, 0.0f);
+ s_aBodyPartSpheres2[1].center = CVector(0.5f, 0.0f, 0.0f);
+
+#ifdef FIX_BUGS
+ for (i = 0; i < ARRAY_SIZE(s_aBodyPartSpheres2); i++) {
+#else
+ for (i = 0; i < ARRAY_SIZE(s_aBootSpheres); i++) {
+#endif
+ s_aBodyPartSpheres2[i].surface = SURFACE_PED;
+ s_aBodyPartSpheres2[i].piece = 0;
+ }
+
+ ms_colModelBodyPart2.boundingSphere.Set(0.5f, CVector(0.25f, 0.0f, 0.0f));
+ ms_colModelBodyPart2.boundingBox.Set(CVector(-0.2f, -0.2f, -0.2f), CVector(0.7f, 0.2f, 0.2f));
+
+ SET_COLMODEL_SPHERES(ms_colModelBodyPart2, s_aBodyPartSpheres2);
+
+#undef SET_COLMODEL_SPHERES
+}
diff --git a/src/collision/TempColModels.h b/src/collision/TempColModels.h
new file mode 100644
index 00000000..3e1dd5e1
--- /dev/null
+++ b/src/collision/TempColModels.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "Collision.h"
+
+class CTempColModels
+{
+public:
+ static CColModel ms_colModelPed1;
+ static CColModel ms_colModelPed2;
+ static CColModel ms_colModelBBox;
+ static CColModel ms_colModelBumper1;
+ static CColModel ms_colModelWheel1;
+ static CColModel ms_colModelPanel1;
+ static CColModel ms_colModelBodyPart2;
+ static CColModel ms_colModelBodyPart1;
+ static CColModel ms_colModelCutObj[5];
+ static CColModel ms_colModelPedGroundHit;
+ static CColModel ms_colModelBoot1;
+ static CColModel ms_colModelDoor1;
+ static CColModel ms_colModelBonnet1;
+
+ static void Initialise(void);
+};
diff --git a/src/collision/VuCollision.cpp b/src/collision/VuCollision.cpp
new file mode 100644
index 00000000..8828d2e1
--- /dev/null
+++ b/src/collision/VuCollision.cpp
@@ -0,0 +1,282 @@
+#include "common.h"
+#ifdef VU_COLLISION
+#include "VuVector.h"
+#include "VuCollision.h"
+
+#ifndef GTA_PS2
+int16 vi01;
+CVuVector vf01;
+CVuVector vf02;
+CVuVector vf03;
+
+CVuVector
+DistanceBetweenSphereAndLine(const CVuVector &center, const CVuVector &p0, const CVuVector &line)
+{
+ // center VF12
+ // p0 VF14
+ // line VF15
+ CVuVector ret; // VF16
+ CVuVector p1 = p0+line;
+ CVuVector dist0 = center - p0; // VF20
+ CVuVector dist1 = center - p1; // VF25
+ float lenSq = line.MagnitudeSqr(); // VF21
+ float distSq0 = dist0.MagnitudeSqr(); // VF22
+ float distSq1 = dist1.MagnitudeSqr();
+ float dot = DotProduct(dist0, line); // VF23
+ if(dot < 0.0f){
+ // not above line, closest to p0
+ ret = p0;
+ ret.w = distSq0;
+ return ret;
+ }
+ float t = dot/lenSq; // param of nearest point on infinite line
+ if(t > 1.0f){
+ // not above line, closest to p1
+ ret = p1;
+ ret.w = distSq1;
+ return ret;
+ }
+ // closest to line
+ ret = p0 + line*t;
+ ret.w = (ret - center).MagnitudeSqr();
+ return ret;
+}
+inline int SignFlags(const CVector &v)
+{
+ int f = 0;
+ if(v.x < 0.0f) f |= 1;
+ if(v.y < 0.0f) f |= 2;
+ if(v.z < 0.0f) f |= 4;
+ return f;
+}
+#endif
+
+extern "C" void
+LineToTriangleCollision(const CVuVector &p0, const CVuVector &p1,
+ const CVuVector &v0, const CVuVector &v1, const CVuVector &v2,
+ const CVuVector &plane)
+{
+#ifdef GTA_PS2
+ __asm__ volatile (
+ ".set noreorder\n"
+ "lqc2 vf12, 0x0(%0)\n"
+ "lqc2 vf13, 0x0(%1)\n"
+ "lqc2 vf14, 0x0(%2)\n"
+ "lqc2 vf15, 0x0(%3)\n"
+ "lqc2 vf16, 0x0(%4)\n"
+ "lqc2 vf17, 0x0(%5)\n"
+ "vcallms Vu0LineToTriangleCollisionStart\n"
+ ".set reorder\n"
+ :
+ : "r" (&p0), "r" (&p1), "r" (&v0), "r" (&v1), "r" (&v2), "r" (&plane)
+ );
+#else
+ float dot0 = DotProduct(plane, p0);
+ float dot1 = DotProduct(plane, p1);
+ float dist0 = plane.w - dot0;
+ float dist1 = plane.w - dot1;
+
+ // if points are on the same side, no collision
+ if(dist0 * dist1 > 0.0f){
+ vi01 = 0;
+ return;
+ }
+
+ CVuVector diff = p1 - p0;
+ float t = dist0/(dot1 - dot0);
+ CVuVector p = p0 + diff*t;
+ p.w = 0.0f;
+ vf01 = p;
+ vf03.x = t;
+
+ // Check if point is inside
+ CVector cross1 = CrossProduct(p-v0, v1-v0);
+ CVector cross2 = CrossProduct(p-v1, v2-v1);
+ CVector cross3 = CrossProduct(p-v2, v0-v2);
+ // Only check relevant directions
+ int flagmask = 0;
+ if(Abs(plane.x) > 0.5f) flagmask |= 1;
+ if(Abs(plane.y) > 0.5f) flagmask |= 2;
+ if(Abs(plane.z) > 0.5f) flagmask |= 4;
+ int flags1 = SignFlags(cross1) & flagmask;
+ int flags2 = SignFlags(cross2) & flagmask;
+ int flags3 = SignFlags(cross3) & flagmask;
+ // inside if on the same side of all edges
+ if(flags1 != flags2 || flags1 != flags3){
+ vi01 = 0;
+ return;
+ }
+ vi01 = 1;
+ vf02 = plane;
+ return;
+#endif
+}
+
+extern "C" void
+LineToTriangleCollisionCompressed(const CVuVector &p0, const CVuVector &p1, VuTriangle &tri)
+{
+#ifdef GTA_PS2
+ __asm__ volatile (
+ ".set noreorder\n"
+ "lqc2 vf12, 0x0(%0)\n"
+ "lqc2 vf13, 0x0(%1)\n"
+ "lqc2 vf14, 0x0(%2)\n"
+ "lqc2 vf15, 0x10(%2)\n"
+ "lqc2 vf16, 0x20(%2)\n"
+ "lqc2 vf17, 0x30(%2)\n"
+ "vcallms Vu0LineToTriangleCollisionCompressedStart\n"
+ ".set reorder\n"
+ :
+ : "r" (&p0), "r" (&p1), "r" (&tri)
+ );
+#else
+ CVuVector v0, v1, v2, plane;
+ v0.x = tri.v0[0]/128.0f;
+ v0.y = tri.v0[1]/128.0f;
+ v0.z = tri.v0[2]/128.0f;
+ v0.w = tri.v0[3]/128.0f;
+ v1.x = tri.v1[0]/128.0f;
+ v1.y = tri.v1[1]/128.0f;
+ v1.z = tri.v1[2]/128.0f;
+ v1.w = tri.v1[3]/128.0f;
+ v2.x = tri.v2[0]/128.0f;
+ v2.y = tri.v2[1]/128.0f;
+ v2.z = tri.v2[2]/128.0f;
+ v2.w = tri.v2[3]/128.0f;
+ plane.x = tri.plane[0]/4096.0f;
+ plane.y = tri.plane[1]/4096.0f;
+ plane.z = tri.plane[2]/4096.0f;
+ plane.w = tri.plane[3]/128.0f;
+ LineToTriangleCollision(p0, p1, v0, v1, v2, plane);
+#endif
+}
+
+extern "C" void
+SphereToTriangleCollision(const CVuVector &sph,
+ const CVuVector &v0, const CVuVector &v1, const CVuVector &v2,
+ const CVuVector &plane)
+{
+#ifdef GTA_PS2
+ __asm__ volatile (
+ ".set noreorder\n"
+ "lqc2 vf12, 0x0(%0)\n"
+ "lqc2 vf14, 0x0(%1)\n"
+ "lqc2 vf15, 0x0(%2)\n"
+ "lqc2 vf16, 0x0(%3)\n"
+ "lqc2 vf17, 0x0(%4)\n"
+ "vcallms Vu0SphereToTriangleCollisionStart\n"
+ ".set reorder\n"
+ :
+ : "r" (&sph), "r" (&v0), "r" (&v1), "r" (&v2), "r" (&plane)
+ );
+#else
+ float planedist = DotProduct(plane, sph) - plane.w; // VF02
+ if(Abs(planedist) > sph.w){
+ vi01 = 0;
+ return;
+ }
+ // point on plane
+ CVuVector p = sph - planedist*plane;
+ p.w = 0.0f;
+ vf01 = p;
+ planedist = Abs(planedist);
+ // edges
+ CVuVector v01 = v1 - v0;
+ CVuVector v12 = v2 - v1;
+ CVuVector v20 = v0 - v2;
+ // VU code calculates normal again for some weird reason...
+ // Check sides of point
+ CVector cross1 = CrossProduct(p-v0, v01);
+ CVector cross2 = CrossProduct(p-v1, v12);
+ CVector cross3 = CrossProduct(p-v2, v20);
+ // Only check relevant directions
+ int flagmask = 0;
+ if(Abs(plane.x) > 0.1f) flagmask |= 1;
+ if(Abs(plane.y) > 0.1f) flagmask |= 2;
+ if(Abs(plane.z) > 0.1f) flagmask |= 4;
+ int nflags = SignFlags(plane) & flagmask;
+ int flags1 = SignFlags(cross1) & flagmask;
+ int flags2 = SignFlags(cross2) & flagmask;
+ int flags3 = SignFlags(cross3) & flagmask;
+ int testcase = 0;
+ CVuVector closest(0.0f, 0.0f, 0.0f); // VF04
+ if(flags1 == nflags){
+ closest += v2;
+ testcase++;
+ }
+ if(flags2 == nflags){
+ closest += v0;
+ testcase++;
+ }
+ if(flags3 == nflags){
+ closest += v1;
+ testcase++;
+ }
+ if(testcase == 3){
+ // inside triangle - dist to plane already checked
+ vf02 = plane;
+ vf02.w = vf03.x = planedist;
+ vi01 = 1;
+ }else if(testcase == 1){
+ // outside two sides - closest to point opposide inside edge
+ vf01 = closest;
+ vf02 = sph - closest;
+ float distSq = vf02.MagnitudeSqr();
+ vi01 = sph.w*sph.w > distSq;
+ vf03.x = Sqrt(distSq);
+ vf02 *= 1.0f/vf03.x;
+ }else{
+ // inside two sides - closest to third edge
+ if(flags1 != nflags)
+ closest = DistanceBetweenSphereAndLine(sph, v0, v01);
+ else if(flags2 != nflags)
+ closest = DistanceBetweenSphereAndLine(sph, v1, v12);
+ else
+ closest = DistanceBetweenSphereAndLine(sph, v2, v20);
+ vi01 = sph.w*sph.w > closest.w;
+ vf01 = closest;
+ vf02 = sph - closest;
+ vf03.x = Sqrt(closest.w);
+ vf02 *= 1.0f/vf03.x;
+ }
+#endif
+}
+
+extern "C" void
+SphereToTriangleCollisionCompressed(const CVuVector &sph, VuTriangle &tri)
+{
+#ifdef GTA_PS2
+ __asm__ volatile (
+ ".set noreorder\n"
+ "lqc2 vf12, 0x0(%0)\n"
+ "lqc2 vf14, 0x0(%1)\n"
+ "lqc2 vf15, 0x10(%1)\n"
+ "lqc2 vf16, 0x20(%1)\n"
+ "lqc2 vf17, 0x30(%1)\n"
+ "vcallms Vu0SphereToTriangleCollisionCompressedStart\n"
+ ".set reorder\n"
+ :
+ : "r" (&sph), "r" (&tri)
+ );
+#else
+ CVuVector v0, v1, v2, plane;
+ v0.x = tri.v0[0]/128.0f;
+ v0.y = tri.v0[1]/128.0f;
+ v0.z = tri.v0[2]/128.0f;
+ v0.w = tri.v0[3]/128.0f;
+ v1.x = tri.v1[0]/128.0f;
+ v1.y = tri.v1[1]/128.0f;
+ v1.z = tri.v1[2]/128.0f;
+ v1.w = tri.v1[3]/128.0f;
+ v2.x = tri.v2[0]/128.0f;
+ v2.y = tri.v2[1]/128.0f;
+ v2.z = tri.v2[2]/128.0f;
+ v2.w = tri.v2[3]/128.0f;
+ plane.x = tri.plane[0]/4096.0f;
+ plane.y = tri.plane[1]/4096.0f;
+ plane.z = tri.plane[2]/4096.0f;
+ plane.w = tri.plane[3]/128.0f;
+ SphereToTriangleCollision(sph, v0, v1, v2, plane);
+#endif
+}
+#endif \ No newline at end of file
diff --git a/src/collision/VuCollision.h b/src/collision/VuCollision.h
new file mode 100644
index 00000000..29ca4cbf
--- /dev/null
+++ b/src/collision/VuCollision.h
@@ -0,0 +1,32 @@
+#pragma once
+
+
+struct VuTriangle
+{
+ // Compressed int16 but unpacked
+#ifdef GTA_PS2
+ uint128 v0;
+ uint128 v1;
+ uint128 v2;
+ uint128 plane;
+#else
+ int32 v0[4];
+ int32 v1[4];
+ int32 v2[4];
+ int32 plane[4];
+#endif
+};
+
+#ifndef GTA_PS2
+extern int16 vi01;
+extern CVuVector vf01;
+extern CVuVector vf02;
+extern CVuVector vf03;
+#endif
+
+extern "C" {
+void LineToTriangleCollision(const CVuVector &p0, const CVuVector &p1, const CVuVector &v0, const CVuVector &v1, const CVuVector &v2, const CVuVector &plane);
+void LineToTriangleCollisionCompressed(const CVuVector &p0, const CVuVector &p1, VuTriangle &tri);
+void SphereToTriangleCollision(const CVuVector &sph, const CVuVector &v0, const CVuVector &v1, const CVuVector &v2, const CVuVector &plane);
+void SphereToTriangleCollisionCompressed(const CVuVector &sph, VuTriangle &tri);
+}
diff --git a/src/collision/vu0Collision.dsm b/src/collision/vu0Collision.dsm
new file mode 100644
index 00000000..657c8b81
--- /dev/null
+++ b/src/collision/vu0Collision.dsm
@@ -0,0 +1,21 @@
+.align 4
+.global Vu0CollisionDmaTag
+Vu0CollisionDmaTag:
+DMAcnt *
+MPG 0, *
+.vu
+.include "vu0Collision_1.s"
+.EndMPG
+.EndDmaData
+DMAend
+
+.global Vu0Collision2DmaTag
+Vu0Collision2DmaTag:
+DMAcnt *
+MPG 0, *
+.vu
+.include "vu0Collision_2.s"
+.EndMPG
+.EndDmaData
+DMAend
+.end
diff --git a/src/collision/vu0Collision_1.s b/src/collision/vu0Collision_1.s
new file mode 100644
index 00000000..055c8640
--- /dev/null
+++ b/src/collision/vu0Collision_1.s
@@ -0,0 +1,610 @@
+QuitAndFail:
+ NOP[E] IADDIU VI01, VI00, 0
+ NOP NOP
+
+
+QuitAndSucceed:
+ NOP[E] IADDIU VI01, VI00, 1
+ NOP NOP
+
+
+; 20 -- unused
+; VF12, VF13 xyz: sphere centers
+; VF14, VF15 x: sphere radii
+; out:
+; VI01: set when collision
+; VF01: supposed to be intersection point?
+; VF02: normal (pointing towards s1, not normalized)
+.globl Vu0SphereToSphereCollision
+Vu0SphereToSphereCollision:
+ SUB.xyz VF02, VF13, VF12 NOP ; dist of centers
+ ADD.x VF04, VF14, VF15 NOP ; s = sum of radii
+ MUL.xyzw VF03, VF02, VF02 NOP ;
+ MUL.x VF04, VF04, VF04 DIV Q, VF14x, VF04x ; square s
+ NOP NOP ;
+ NOP NOP ;
+ MULAx.w ACC, VF00, VF03 NOP ;
+ MADDAy.w ACC, VF00, VF03 NOP ;
+ MADDz.w VF03, VF00, VF03 NOP ; d = DistSq of centers
+ NOP NOP ;
+ MULAw.xyz ACC, VF12, VF00 NOP ;
+ MADDq.xyz VF01, VF02, Q NOP ; intersection, but wrong
+ CLIPw.xyz VF04, VF03 NOP ; compare s and d
+ SUB.xyz VF02, VF00, VF02 NOP ; compute normal
+ NOP NOP ;
+ NOP NOP ;
+ NOP FCAND VI01, 0x3 ; 0x2 cannot be set here
+ NOP[E] NOP ;
+ NOP NOP ;
+
+
+; B8 -- unused
+; VF12:
+; VF13: radius
+; VF14:
+; VF15: box dimensions (?)
+.globl Vu0SphereToAABBCollision
+Vu0SphereToAABBCollision:
+ SUB.xyz VF03, VF12, VF14 LOI 0.5
+ MULi.xyz VF15, VF15, I NOP
+ MUL.x VF13, VF13, VF13 NOP
+ SUB.xyz VF04, VF03, VF15 NOP
+ ADD.xyz VF05, VF03, VF15 MR32.xyzw VF16, VF15
+ CLIPw.xyz VF03, VF16 MR32.xyzw VF17, VF16
+ MUL.xyz VF04, VF04, VF04 NOP
+ MUL.xyz VF05, VF05, VF05 NOP
+ CLIPw.xyz VF03, VF17 MR32.xyzw VF16, VF17
+ NOP FCAND VI01, 0x1
+ MINI.xyz VF04, VF04, VF05 MFIR.x VF09, VI01
+ NOP NOP
+ CLIPw.xyz VF03, VF16 FCAND VI01, 0x4
+ NOP MFIR.y VF09, VI01
+ NOP NOP
+ MULAx.w ACC, VF00, VF00 NOP
+ ADD.xyz VF01, VF00, VF03 FCAND VI01, 0x10
+ NOP MFIR.z VF09, VI01
+ NOP LOI 2
+ NOP FCAND VI01, 0x30
+ SUBAw.xyz ACC, VF00, VF00 IADD VI04, VI00, VI01
+ ITOF0.xyz VF09, VF09 FCAND VI01, 0x300
+ NOP IADD VI03, VI00, VI01
+ NOP FCAND VI01, 0x3000
+ NOP IADD VI02, VI00, VI01
+ MADDi.xyzw VF09, VF09, I NOP
+ NOP IBEQ VI04, VI00, IgnoreZValue
+ NOP NOP
+ MADDAz.w ACC, VF00, VF04 NOP
+ MUL.z VF01, VF09, VF15 NOP
+IgnoreZValue:
+ NOP IBEQ VI03, VI00, IgnoreYValue
+ NOP NOP
+ MADDAy.w ACC, VF00, VF04 NOP
+ MUL.y VF01, VF09, VF15 NOP
+IgnoreYValue:
+ NOP IBEQ VI02, VI00, IgnoreXValue
+ NOP NOP
+ MADDAx.w ACC, VF00, VF04 NOP
+ MUL.x VF01, VF09, VF15 NOP
+IgnoreXValue:
+ MADDx.w VF06, VF00, VF00 NOP
+ SUB.xyz VF02, VF03, VF01 NOP
+ ADD.xyz VF01, VF01, VF14 NOP
+ MULx.w VF01, VF00, VF00 NOP
+ CLIPw.xyz VF13, VF06 NOP
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ NOP FCAND VI01, 0x1
+QuitMicrocode:
+ NOP[E] NOP
+ NOP NOP
+
+
+; 240
+.globl Vu0LineToSphereCollision
+Vu0LineToSphereCollision:
+ SUB.xyzw VF01, VF13, VF12 NOP
+ SUB.xyzw VF02, VF14, VF12 NOP
+ MUL.xyz VF03, VF01, VF02 NOP
+ MUL.xyz VF04, VF01, VF01 NOP
+ MUL.x VF15, VF15, VF15 NOP
+ MUL.xyz VF02, VF02, VF02 NOP
+ MULAx.w ACC, VF00, VF03 NOP
+ MADDAy.w ACC, VF00, VF03 NOP
+ MADDz.w VF03, VF00, VF03 NOP
+ MULAx.w ACC, VF00, VF04 NOP
+ MADDAy.w ACC, VF00, VF04 NOP
+ MADDz.w VF01, VF00, VF04 NOP
+ MULAx.w ACC, VF00, VF02 NOP
+ MADDAy.w ACC, VF00, VF02 NOP
+ MADDz.w VF02, VF00, VF02 NOP
+ MULA.w ACC, VF03, VF03 NOP
+ MADDAx.w ACC, VF01, VF15 NOP
+ MSUB.w VF05, VF01, VF02 NOP
+ NOP NOP
+ NOP NOP
+ NOP IADDIU VI02, VI00, 0x10
+ NOP FMAND VI01, VI02
+ NOP IBNE VI01, VI00, QuitAndFail
+ NOP NOP
+ CLIPw.xyz VF15, VF02 SQRT Q, VF05w
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ NOP FCAND VI01, 0x1
+ NOP IBNE VI00, VI01, LineStartInsideSphere
+ NOP NOP
+ SUBq.w VF05, VF03, Q NOP
+ SUB.w VF05, VF05, VF01 DIV Q, VF05w, VF01w
+ NOP FMAND VI01, VI02
+ NOP IBNE VI01, VI00, QuitAndFail
+ NOP NOP
+ NOP FMAND VI01, VI02
+ NOP IBEQ VI01, VI00, QuitAndFail
+ NOP NOP
+ ADDA.xyz ACC, VF12, VF00 NOP
+ MADDq.xyz VF01, VF01, Q NOP
+ MULx.w VF01, VF00, VF00 NOP
+ SUB.xyz VF02, VF01, VF14 NOP
+ NOP[E] NOP
+ NOP NOP
+LineStartInsideSphere:
+ NOP MOVE.xyzw VF01, VF12
+ NOP[E] IADDIU VI01, VI00, 0x1
+ NOP NOP
+
+
+; 3C0
+.globl Vu0LineToAABBCollision
+Vu0LineToAABBCollision:
+ SUB.xyzw VF08, VF13, VF12 LOI 0.5
+ MULi.xyz VF15, VF15, I IADDIU VI08, VI00, 0x0
+ SUB.xyzw VF12, VF12, VF14 NOP
+ SUB.xyzw VF13, VF13, VF14 NOP
+ NOP DIV Q, VF00w, VF08x
+ NOP MR32.xyzw VF03, VF15
+ SUB.xyz VF06, VF15, VF12 NOP
+ ADD.xyz VF07, VF15, VF12 NOP
+ NOP NOP
+ CLIPw.xyz VF12, VF03 MR32.xyzw VF04, VF03
+ NOP NOP
+ ADDq.x VF09, VF00, Q DIV Q, VF00w, VF08y
+ NOP NOP
+ CLIPw.xyz VF12, VF04 MR32.xyzw VF05, VF04
+ SUB.xyz VF07, VF00, VF07 IADDIU VI06, VI00, 0xCC
+ NOP IADDIU VI07, VI00, 0x30
+ NOP NOP
+ CLIPw.xyz VF12, VF05 FCGET VI02
+ NOP IAND VI02, VI02, VI06
+ ADDq.y VF09, VF00, Q DIV Q, VF00w, VF08z
+ SUB.xyz VF10, VF00, VF10 NOP
+ CLIPw.xyz VF13, VF03 FCGET VI03
+ CLIPw.xyz VF13, VF04 IAND VI03, VI03, VI07
+ CLIPw.xyz VF13, VF05 FCAND VI01, 0x3330
+ NOP IBEQ VI01, VI00, StartPointInsideAABB
+ NOP NOP
+ ADDq.z VF09, VF00, Q FCGET VI04
+ NOP FCGET VI05
+ NOP IAND VI04, VI04, VI06
+ NOP IAND VI05, VI05, VI07
+ MULx.xyz VF17, VF08, VF09 NOP
+ MULy.xyz VF18, VF08, VF09 IADDIU VI07, VI00, 0x80
+ MULz.xyz VF19, VF08, VF09 IAND VI06, VI02, VI07
+ MUL.w VF10, VF00, VF00 IAND VI07, VI04, VI07
+ NOP NOP
+ NOP IBEQ VI06, VI07, CheckMaxXSide
+ NOP NOP
+ MULAx.xyz ACC, VF17, VF07 NOP
+ MADDw.xyz VF16, VF12, VF00 NOP
+ MUL.x VF10, VF07, VF09 NOP
+ CLIPw.xyz VF16, VF04 NOP
+ CLIPw.xyz VF16, VF05 NOP
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ NOP FCAND VI01, 0x330
+ NOP IBNE VI01, VI00, CheckMaxXSide
+ NOP NOP
+ MULx.w VF10, VF00, VF10 IADDIU VI08, VI00, 0x1
+ ADD.yz VF02, VF00, VF00 MOVE.xyzw VF01, VF16
+ SUBw.x VF02, VF00, VF00 NOP
+CheckMaxXSide:
+ MULAx.xyz ACC, VF17, VF06 IADDIU VI07, VI00, 0x40
+ MADDw.xyz VF16, VF12, VF00 IAND VI06, VI02, VI07
+ MUL.x VF10, VF06, VF09 IAND VI07, VI04, VI07
+ NOP NOP
+ NOP IBEQ VI06, VI07, CheckMinYSide
+ NOP NOP
+ CLIPw.xyz VF16, VF04 NOP
+ CLIPw.xyz VF16, VF05 NOP
+ CLIPw.xyz VF10, VF10 NOP
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ NOP FCAND VI01, 0xCC03
+ NOP IBNE VI01, VI00, CheckMinYSide
+ NOP NOP
+ MULx.w VF10, VF00, VF10 IADDIU VI08, VI00, 0x1
+ ADD.yz VF02, VF00, VF00 MOVE.xyzw VF01, VF16
+ ADDw.x VF02, VF00, VF00 NOP
+CheckMinYSide:
+ MULAy.xyz ACC, VF18, VF07 IADDIU VI07, VI00, 0x8
+ MADDw.xyz VF16, VF12, VF00 IAND VI06, VI02, VI07
+ MUL.y VF10, VF07, VF09 IAND VI07, VI04, VI07
+ NOP NOP
+ NOP IBEQ VI06, VI07, CheckMaxYSide
+ NOP NOP
+ CLIPw.xyz VF16, VF03 NOP
+ CLIPw.xyz VF16, VF05 NOP
+ CLIPw.xyz VF10, VF10 NOP
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ NOP FCAND VI01, 0x3C0C
+ NOP IBNE VI01, VI00, CheckMaxYSide
+ NOP NOP
+ MULy.w VF10, VF00, VF10 IADDIU VI08, VI00, 0x1
+ ADD.xz VF02, VF00, VF00 MOVE.xyzw VF01, VF16
+ SUBw.y VF02, VF00, VF00 NOP
+CheckMaxYSide:
+ MULAy.xyz ACC, VF18, VF06 IADDIU VI07, VI00, 0x4
+ MADDw.xyz VF16, VF12, VF00 IAND VI06, VI02, VI07
+ MUL.y VF10, VF06, VF09 IAND VI07, VI04, VI07
+ NOP NOP
+ NOP IBEQ VI06, VI07, CheckMinZSide
+ NOP NOP
+ CLIPw.xyz VF16, VF03 NOP
+ CLIPw.xyz VF16, VF05 NOP
+ CLIPw.xyz VF10, VF10 NOP
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ NOP FCAND VI01, 0x3C0C
+ NOP IBNE VI01, VI00, CheckMinZSide
+ NOP NOP
+ MULy.w VF10, VF00, VF10 IADDIU VI08, VI00, 0x1
+ ADD.xz VF02, VF00, VF00 MOVE.xyzw VF01, VF16
+ ADDw.y VF02, VF00, VF00 NOP
+CheckMinZSide:
+ MULAz.xyz ACC, VF19, VF07 IADDIU VI07, VI00, 0x20
+ MADDw.xyz VF16, VF12, VF00 IAND VI06, VI03, VI07
+ MUL.z VF10, VF07, VF09 IAND VI07, VI05, VI07
+ NOP NOP
+ NOP IBEQ VI06, VI07, CheckMaxZSide
+ NOP NOP
+ CLIPw.xyz VF16, VF03 NOP
+ CLIPw.xyz VF16, VF04 NOP
+ CLIPw.xyz VF10, VF10 NOP
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ NOP FCAND VI01, 0x3330
+ NOP IBNE VI01, VI00, CheckMaxZSide
+ NOP NOP
+ MULz.w VF10, VF00, VF10 IADDIU VI08, VI00, 0x1
+ ADD.xy VF02, VF00, VF00 MOVE.xyzw VF01, VF16
+ SUBw.z VF02, VF00, VF00 NOP
+CheckMaxZSide:
+ MULAz.xyz ACC, VF19, VF06 IADDIU VI07, VI00, 0x10
+ MADDw.xyz VF16, VF12, VF00 IAND VI06, VI03, VI07
+ MUL.z VF10, VF06, VF09 IAND VI07, VI05, VI07
+ NOP NOP
+ NOP IBEQ VI06, VI07, DoneAllChecks
+ NOP NOP
+ CLIPw.xyz VF16, VF03 NOP
+ CLIPw.xyz VF16, VF04 NOP
+ CLIPw.xyz VF10, VF10 NOP
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ NOP FCAND VI01, 0x3330
+ NOP IBNE VI01, VI00, DoneAllChecks
+ NOP NOP
+ MULz.w VF10, VF00, VF10 IADDIU VI08, VI00, 0x1
+ ADD.xy VF02, VF00, VF00 MOVE.xyzw VF01, VF16
+ ADDw.z VF02, VF00, VF00 NOP
+DoneAllChecks:
+ ADD.xyz VF01, VF01, VF14 IADD VI01, VI00, VI08
+ NOP[E] NOP
+ NOP NOP
+StartPointInsideAABB:
+ ADD.xyz VF01, VF12, VF14 WAITQ
+ NOP IADDIU VI01, VI00, 0x1
+ NOP[E] NOP
+ NOP NOP
+
+
+; 860
+.globl Vu0LineToTriangleCollisionCompressedStart
+Vu0LineToTriangleCollisionCompressedStart:
+ ITOF0.xyzw VF17, VF17 LOI 0.000244140625 ; 1.0/4096.0
+ ITOF0.xyzw VF14, VF14 NOP
+ ITOF0.xyzw VF15, VF15 NOP
+ ITOF0.xyzw VF16, VF16 NOP
+ MULi.xyz VF17, VF17, I LOI 0.0078125 ; 1.0/128.0
+ MULi.w VF17, VF17, I NOP
+ MULi.xyzw VF14, VF14, I NOP
+ MULi.xyzw VF15, VF15, I NOP
+ MULi.xyzw VF16, VF16, I NOP
+; fall through
+
+; 8A8
+; VF12: point0
+; VF13: point1
+; VF14-16: verts
+; VF17: plane
+; out:
+; VF01: intersection point
+; VF02: triangle normal
+; VF03 x: intersection parameter
+.globl Vu0LineToTriangleCollisionStart
+Vu0LineToTriangleCollisionStart:
+ MUL.xyz VF10, VF17, VF12 LOI 0.5
+ MUL.xyz VF11, VF17, VF13 NOP
+ SUB.xyz VF02, VF13, VF12 NOP ; line dist
+ ADD.xyz VF17, VF17, VF00 NOP
+ MULi.w VF03, VF00, I NOP
+ MULAx.w ACC, VF00, VF10 NOP
+ MADDAy.w ACC, VF00, VF10 IADDIU VI06, VI00, 0xE0
+ MADDz.w VF10, VF00, VF10 FMAND VI05, VI06 ; -- normal sign flags, unused
+ MULAx.w ACC, VF00, VF11 NOP
+ MADDAy.w ACC, VF00, VF11 NOP
+ MADDz.w VF11, VF00, VF11 NOP
+ SUB.w VF09, VF17, VF10 NOP ; plane-pos 0
+ CLIPw.xyz VF17, VF03 NOP ; compare normal against 0.5 to figure out which in which dimension to compare
+ NOP IADDIU VI02, VI00, 0x10 ; Sw flag
+ SUBA.w ACC, VF17, VF11 NOP ; plane-pos 1
+ SUB.w VF08, VF11, VF10 FMAND VI01, VI02
+ NOP NOP
+ NOP NOP
+ NOP FMAND VI02, VI02
+ NOP IBEQ VI01, VI02, QuitAndFail ; if on same side, no collision
+ NOP NOP
+ NOP DIV Q, VF09w, VF08w ; parameter of intersection
+ NOP FCAND VI01, 0x3 ; check x direction
+ NOP IADDIU VI02, VI01, 0x7F
+ NOP IADDIU VI06, VI00, 0x80
+ NOP IAND VI02, VI02, VI06 ; Sx flag
+ NOP FCAND VI01, 0xC ; check y direction
+ NOP IADDIU VI03, VI01, 0x3F
+ MULAw.xyz ACC, VF12, VF00 IADDIU VI06, VI00, 0x40
+ MADDq.xyz VF01, VF02, Q IAND VI03, VI03, VI06 ; point of intersection -- Sy flag
+ MULx.w VF01, VF00, VF00 FCAND VI01, 0x30 ; -- check z direction
+ ADDq.x VF03, VF00, Q IADDIU VI04, VI01, 0x1F ; output parameter
+ SUB.xyz VF05, VF15, VF14 IADDIU VI06, VI00, 0x20 ; edge vectors
+ SUB.xyz VF08, VF01, VF14 IAND VI04, VI04, VI06 ; edge vectors -- Sz flag
+ SUB.xyz VF06, VF16, VF15 IADD VI06, VI02, VI03 ; edge vectors
+ SUB.xyz VF09, VF01, VF15 IADD VI06, VI06, VI04 ; edge vectors -- combine flags
+ SUB.xyz VF07, VF14, VF16 NOP ; edge vectors
+ SUB.xyz VF10, VF01, VF16 NOP ; edge vectors
+ OPMULA.xyz ACC, VF08, VF05 NOP
+ OPMSUB.xyz VF18, VF05, VF08 NOP ; cross1
+ OPMULA.xyz ACC, VF09, VF06 NOP
+ OPMSUB.xyz VF19, VF06, VF09 NOP ; cross2
+ OPMULA.xyz ACC, VF10, VF07 NOP
+ OPMSUB.xyz VF20, VF07, VF10 FMAND VI02, VI06 ; cross3
+ NOP NOP
+ NOP FMAND VI03, VI06
+ NOP NOP
+ NOP FMAND VI04, VI06
+ NOP NOP
+ NOP IBNE VI03, VI02, QuitAndFail ; point has to lie on the same side of all edges (i.e. inside)
+ NOP NOP
+ NOP IBNE VI04, VI02, QuitAndFail
+ NOP NOP
+ MULw.xyz VF02, VF17, VF00 IADDIU VI01, VI00, 0x1 ; success
+ NOP[E] NOP
+ NOP NOP
+
+
+; A68
+; VF12: center
+; VF14: line origin
+; VF15: line vector to other point
+; out: VF16 xyz: nearest point on line; w: distance to that point
+DistanceBetweenSphereAndLine:
+ SUB.xyz VF20, VF12, VF14 NOP
+ MUL.xyz VF21, VF15, VF15 NOP
+ ADDA.xyz ACC, VF14, VF15 NOP
+ MSUBw.xyz VF25, VF12, VF00 NOP ; VF25 = VF12 - (VF14+VF15)
+ MUL.xyz VF22, VF20, VF20 NOP
+ MUL.xyz VF23, VF20, VF15 NOP
+ MULAx.w ACC, VF00, VF21 NOP
+ MADDAy.w ACC, VF00, VF21 NOP
+ MADDz.w VF21, VF00, VF21 NOP ; MagSq VF15 (line length)
+ MULAx.w ACC, VF00, VF23 NOP
+ MADDAy.w ACC, VF00, VF23 NOP
+ MADDz.w VF23, VF00, VF23 NOP ; dot(VF12-VF14, VF15)
+ MULAx.w ACC, VF00, VF22 NOP
+ MADDAy.w ACC, VF00, VF22 NOP
+ MADDz.w VF22, VF00, VF22 IADDIU VI08, VI00, 0x10 ; MagSq VF12-VF14 -- Sw bit
+ MUL.xyz VF25, VF25, VF25 FMAND VI08, VI08
+ NOP DIV Q, VF23w, VF21w
+ NOP IBNE VI00, VI08, NegativeRatio
+ NOP NOP
+ ADDA.xyz ACC, VF00, VF14 NOP
+ MADDq.xyz VF16, VF15, Q WAITQ ; nearest point on infinte line
+ ADDq.x VF24, VF00, Q NOP ; ratio
+ NOP NOP
+ NOP NOP
+ SUB.xyz VF26, VF16, VF12 NOP
+ CLIPw.xyz VF24, VF00 NOP ; compare ratio to 1.0
+ NOP NOP
+ NOP NOP
+ MUL.xyz VF26, VF26, VF26 NOP
+ NOP FCAND VI01, 0x1
+ NOP IBNE VI00, VI01, RatioGreaterThanOne
+ NOP NOP
+ MULAx.w ACC, VF00, VF26 NOP
+ MADDAy.w ACC, VF00, VF26 NOP
+ MADDz.w VF16, VF00, VF26 NOP ; distance
+ NOP JR VI15
+ NOP NOP
+NegativeRatio:
+ ADD.xyz VF16, VF00, VF14 NOP ; return line origin
+ MUL.w VF16, VF00, VF22 NOP ; and DistSq to it
+ NOP JR VI15
+ NOP NOP
+RatioGreaterThanOne:
+ MULAx.w ACC, VF00, VF25 NOP
+ MADDAy.w ACC, VF00, VF25 NOP
+ MADDz.w VF16, VF00, VF25 NOP
+ ADD.xyz VF16, VF14, VF15 NOP ; return toerh line point
+ NOP JR VI15
+ NOP NOP
+
+
+; BE0
+.globl Vu0SphereToTriangleCollisionCompressedStart
+Vu0SphereToTriangleCollisionCompressedStart:
+ ITOF0.xyzw VF17, VF17 LOI 0.000244140625 ; 1.0/4096.0
+ ITOF0.xyzw VF14, VF14 NOP
+ ITOF0.xyzw VF15, VF15 NOP
+ ITOF0.xyzw VF16, VF16 NOP
+ MULi.xyz VF17, VF17, I LOI 0.0078125 ; 1.0/128.0
+ MULi.w VF17, VF17, I NOP
+ MULi.xyzw VF14, VF14, I NOP
+ MULi.xyzw VF15, VF15, I NOP
+ MULi.xyzw VF16, VF16, I NOP
+; fall through
+
+; C28
+; VF12: sphere
+; VF14-16: verts
+; VF17: plane
+; out:
+; VF01: intersection point
+; VF02: triangle normal
+; VF03 x: intersection parameter
+.globl Vu0SphereToTriangleCollisionStart
+Vu0SphereToTriangleCollisionStart:
+ MUL.xyz VF02, VF12, VF17 LOI 0.1
+ ADD.xyz VF17, VF17, VF00 NOP
+ ADDw.x VF13, VF00, VF12 NOP
+ NOP NOP
+ MULAx.w ACC, VF00, VF02 IADDIU VI06, VI00, 0xE0
+ MADDAy.w ACC, VF00, VF02 FMAND VI05, VI06 ; normal sign flags
+ MADDAz.w ACC, VF00, VF02 NOP
+ MSUB.w VF02, VF00, VF17 NOP ; center plane pos
+ MULi.w VF03, VF00, I MOVE.xyzw VF04, VF03
+ NOP NOP
+ NOP NOP
+ CLIPw.xyz VF13, VF02 NOP ; compare dist and radius
+ CLIPw.xyz VF17, VF03 NOP
+ MULAw.xyz ACC, VF12, VF00 IADDIU VI07, VI00, 0x0 ; -- clear test case
+ MSUBw.xyz VF01, VF17, VF02 NOP
+ MULx.w VF01, VF00, VF00 FCAND VI01, 0x3 ; projected center on plane
+ ABS.w VF02, VF02 IBEQ VI00, VI01, QuitAndFail ; no intersection
+ NOP NOP
+ NOP FCAND VI01, 0x3 ; -- check x direction
+ SUB.xyz VF02, VF12, VF01 IADDIU VI02, VI01, 0x7F
+ NOP IADDIU VI06, VI00, 0x80
+ SUB.xyz VF05, VF15, VF14 IAND VI02, VI02, VI06
+ SUB.xyz VF08, VF01, VF14 FCAND VI01, 0xC ; -- check y direction
+ SUB.xyz VF06, VF16, VF15 IADDIU VI03, VI01, 0x3F
+ SUB.xyz VF09, VF01, VF15 IADDIU VI06, VI00, 0x40
+ SUB.xyz VF07, VF14, VF16 IAND VI03, VI03, VI06
+ SUB.xyz VF10, VF01, VF16 FCAND VI01, 0x30 ; -- check z direction
+ MUL.xyz VF03, VF02, VF02 IADDIU VI04, VI01, 0x1F
+ OPMULA.xyz ACC, VF08, VF05 IADDIU VI06, VI00, 0x20
+ OPMSUB.xyz VF18, VF05, VF08 IAND VI04, VI04, VI06
+ OPMULA.xyz ACC, VF09, VF06 NOP
+ OPMSUB.xyz VF19, VF06, VF09 IADD VI06, VI02, VI03
+ OPMULA.xyz ACC, VF10, VF07 IADD VI06, VI06, VI04 ; -- combine flags
+ OPMSUB.xyz VF20, VF07, VF10 FMAND VI02, VI06 ; -- cross 1 flags
+ MULAx.w ACC, VF00, VF03 IAND VI05, VI05, VI06
+ MADDAy.w ACC, VF00, VF03 FMAND VI03, VI06 ; -- cross 2 flags
+ MADDz.w VF03, VF00, VF03 IADDIU VI08, VI00, 0x3
+ NOP FMAND VI04, VI06 ; -- cross 3 flags
+ NOP NOP
+ NOP IBNE VI02, VI05, CheckSide2
+ NOP RSQRT Q, VF00w, VF03w
+ ADD.xyz VF04, VF00, VF16 IADDIU VI07, VI07, 0x1 ; inside side 1
+CheckSide2:
+ NOP IBNE VI03, VI05, CheckSide3
+ NOP NOP
+ ADD.xyz VF04, VF00, VF14 IADDIU VI07, VI07, 0x1 ; inside side 2
+CheckSide3:
+ NOP IBNE VI04, VI05, FinishCheckingSides
+ NOP NOP
+ ADD.xyz VF04, VF00, VF15 IADDIU VI07, VI07, 0x1 ; inside side 3
+ NOP NOP
+ NOP IBEQ VI07, VI08, TotallyInsideTriangle
+ NOP NOP
+FinishCheckingSides:
+ MUL.x VF13, VF13, VF13 IADDIU VI08, VI00, 0x2
+ MULq.xyz VF02, VF02, Q WAITQ
+ NOP IBNE VI07, VI08, IntersectionOutsideTwoSides
+ NOP NOP
+ NOP IBEQ VI02, VI05, CheckDistanceSide2
+ NOP NOP
+ NOP MOVE.xyzw VF15, VF05
+ NOP BAL VI15, DistanceBetweenSphereAndLine
+ NOP NOP
+ NOP B ProcessLineResult
+ NOP NOP
+CheckDistanceSide2:
+ NOP IBEQ VI03, VI05, CheckDistanceSide3
+ NOP NOP
+ NOP MOVE.xyzw VF14, VF15
+ NOP MOVE.xyzw VF15, VF06
+ NOP BAL VI15, DistanceBetweenSphereAndLine
+ NOP NOP
+ NOP B ProcessLineResult
+ NOP NOP
+CheckDistanceSide3:
+ NOP MOVE.xyzw VF14, VF16
+ NOP MOVE.xyzw VF15, VF07
+ NOP BAL VI15, DistanceBetweenSphereAndLine
+ NOP NOP
+ NOP B ProcessLineResult
+ NOP NOP
+IntersectionOutsideTwoSides:
+ SUB.xyz VF05, VF04, VF12 NOP
+ ADD.xyz VF01, VF00, VF04 NOP ; col point
+ SUB.xyz VF02, VF12, VF04 NOP
+ NOP NOP
+ MUL.xyz VF05, VF05, VF05 NOP
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ MULAx.w ACC, VF00, VF05 NOP
+ MADDAy.w ACC, VF00, VF05 NOP
+ MADDz.w VF05, VF00, VF05 NOP ; distSq to vertex
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ CLIPw.xyz VF13, VF05 SQRT Q, VF05w ; compare radiusSq and distSq
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ NOP FCAND VI01, 0x1
+ ADDq.x VF03, VF00, Q WAITQ ; dist to vertex
+ NOP IBEQ VI00, VI01, QuitAndFail ; too far
+ NOP NOP
+ NOP NOP
+ NOP DIV Q, VF00w, VF03x
+ MULq.xyz VF02, VF02, Q WAITQ ; col normal
+ NOP[E] NOP
+ NOP NOP
+TotallyInsideTriangle:
+ ADDw.x VF03, VF00, VF02 WAITQ
+ MULq.xyz VF02, VF02, Q NOP
+ NOP[E] IADDIU VI01, VI00, 0x1
+ NOP NOP
+ProcessLineResult:
+ CLIPw.xyz VF13, VF16 SQRT Q, VF16w
+ ADD.xyz VF01, VF00, VF16 NOP
+ SUB.xyz VF02, VF12, VF16 NOP
+ NOP NOP
+ NOP FCAND VI01, 0x1
+ ADDq.x VF03, VF00, Q WAITQ
+ NOP IBEQ VI00, VI01, QuitAndFail
+ NOP NOP
+ NOP NOP
+ NOP DIV Q, VF00w, VF03x
+ MULq.xyz VF02, VF02, Q WAITQ
+ NOP[E] NOP
+ NOP NOP
+
+EndOfMicrocode:
diff --git a/src/collision/vu0Collision_2.s b/src/collision/vu0Collision_2.s
new file mode 100644
index 00000000..716c29ac
--- /dev/null
+++ b/src/collision/vu0Collision_2.s
@@ -0,0 +1,191 @@
+QuitAndFail2:
+ NOP[E] IADDIU VI01, VI00, 0x0
+ NOP NOP
+
+
+QuitAndSucceed2:
+ NOP[E] IADDIU VI01, VI00, 0x1
+ NOP NOP
+
+
+; 20
+GetBBVertices:
+ MULw.xy VF02, VF01, VF00 NOP
+ MUL.z VF02, VF01, VF11 NOP
+ MULw.xz VF03, VF01, VF00 NOP
+ MUL.y VF03, VF01, VF11 NOP
+ MULw.x VF04, VF01, VF00 NOP
+ MUL.yz VF04, VF01, VF11 NOP
+ NOP JR VI15
+ NOP NOP
+
+
+; 60
+Vu0OBBToOBBCollision:
+ SUBw.xyz VF11, VF00, VF00 LOI 0.5
+ MULi.xyz VF12, VF12, I NOP
+ MULi.xyz VF13, VF13, I NOP
+ NOP NOP
+ NOP NOP
+ NOP MOVE.xyz VF01, VF12
+ NOP BAL VI15, GetBBVertices
+ NOP NOP
+ MULAx.xyz ACC, VF14, VF01 NOP
+ MADDAy.xyz ACC, VF15, VF01 NOP
+ MADDz.xyz VF01, VF16, VF01 NOP
+ MULAx.xyz ACC, VF14, VF02 NOP
+ MADDAy.xyz ACC, VF15, VF02 NOP
+ MADDz.xyz VF02, VF16, VF02 NOP
+ MULAx.xyz ACC, VF14, VF03 NOP
+ MADDAy.xyz ACC, VF15, VF03 NOP
+ MADDz.xyz VF03, VF16, VF03 NOP
+ MULAx.xyz ACC, VF14, VF04 NOP
+ MADDAy.xyz ACC, VF15, VF04 NOP
+ MADDz.xyz VF04, VF16, VF04 NOP
+ ABS.xyz VF05, VF01 NOP
+ ABS.xyz VF06, VF02 NOP
+ ABS.xyz VF07, VF03 NOP
+ ABS.xyz VF08, VF04 NOP
+ NOP NOP
+ MAX.xyz VF05, VF05, VF06 NOP
+ NOP NOP
+ MAX.xyz VF07, VF07, VF08 NOP
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ MAX.xyz VF05, VF05, VF07 NOP
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ ADD.xyz VF09, VF05, VF13 NOP
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ MULx.w VF05, VF00, VF09 NOP
+ MULy.w VF06, VF00, VF09 NOP
+ MULz.w VF07, VF00, VF09 NOP
+ CLIPw.xyz VF17, VF05 NOP
+ CLIPw.xyz VF17, VF06 NOP
+ CLIPw.xyz VF17, VF07 MOVE.xyz VF01, VF13
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ NOP FCAND VI01, 0x3330
+ NOP IBNE VI01, VI00, QuitAndFail2
+ NOP NOP
+ NOP BAL VI15, GetBBVertices
+ NOP NOP
+ MULAx.xyz ACC, VF18, VF01 NOP
+ MADDAy.xyz ACC, VF19, VF01 NOP
+ MADDz.xyz VF01, VF20, VF01 NOP
+ MULAx.xyz ACC, VF18, VF02 NOP
+ MADDAy.xyz ACC, VF19, VF02 NOP
+ MADDz.xyz VF02, VF20, VF02 NOP
+ MULAx.xyz ACC, VF18, VF03 NOP
+ MADDAy.xyz ACC, VF19, VF03 NOP
+ MADDz.xyz VF03, VF20, VF03 NOP
+ MULAx.xyz ACC, VF18, VF04 NOP
+ MADDAy.xyz ACC, VF19, VF04 NOP
+ MADDz.xyz VF04, VF20, VF04 NOP
+ ABS.xyz VF05, VF01 NOP
+ ABS.xyz VF06, VF02 NOP
+ ABS.xyz VF07, VF03 NOP
+ ABS.xyz VF08, VF04 NOP
+ NOP NOP
+ MAX.xyz VF05, VF05, VF06 NOP
+ NOP NOP
+ MAX.xyz VF07, VF07, VF08 NOP
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ MAX.xyz VF05, VF05, VF07 NOP
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ ADD.xyz VF09, VF05, VF12 NOP
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ MULx.w VF05, VF00, VF09 NOP
+ MULy.w VF06, VF00, VF09 NOP
+ MULz.w VF07, VF00, VF09 NOP
+ CLIPw.xyz VF21, VF05 NOP
+ CLIPw.xyz VF21, VF06 NOP
+ CLIPw.xyz VF21, VF07 NOP
+ NOP NOP
+ NOP NOP
+ NOP NOP
+ NOP FCAND VI01, 0x3330
+ NOP IBNE VI01, VI00, QuitAndFail2
+ NOP NOP
+ SUB.xyz VF06, VF02, VF01 NOP
+ SUB.xyz VF07, VF03, VF01 NOP
+ ADD.xyz VF08, VF04, VF01 NOP
+ ADD.x VF09, VF00, VF12 NOP
+ ADD.yz VF09, VF00, VF00 NOP
+ ADD.y VF10, VF00, VF12 NOP
+ ADD.xz VF10, VF00, VF00 NOP
+ ADD.z VF11, VF00, VF12 IADDI VI04, VI00, 0x0
+ ADD.xy VF11, VF00, VF00 IADD VI02, VI00, VI00
+ OPMULA.xyz ACC, VF06, VF09 NOP
+ OPMSUB.xyz VF01, VF09, VF06 NOP
+ OPMULA.xyz ACC, VF06, VF10 NOP
+ OPMSUB.xyz VF02, VF10, VF06 NOP
+ OPMULA.xyz ACC, VF06, VF11 NOP
+ OPMSUB.xyz VF03, VF11, VF06 SQI.xyzw VF01, (VI02++)
+ OPMULA.xyz ACC, VF07, VF09 NOP
+ OPMSUB.xyz VF01, VF09, VF07 SQI.xyzw VF02, (VI02++)
+ OPMULA.xyz ACC, VF07, VF10 NOP
+ OPMSUB.xyz VF02, VF10, VF07 SQI.xyzw VF03, (VI02++)
+ OPMULA.xyz ACC, VF07, VF11 NOP
+ OPMSUB.xyz VF03, VF11, VF07 SQI.xyzw VF01, (VI02++)
+ OPMULA.xyz ACC, VF08, VF09 NOP
+ OPMSUB.xyz VF01, VF09, VF08 SQI.xyzw VF02, (VI02++)
+ OPMULA.xyz ACC, VF08, VF10 NOP
+ OPMSUB.xyz VF02, VF10, VF08 SQI.xyzw VF03, (VI02++)
+ OPMULA.xyz ACC, VF08, VF11 LOI 0.5
+ OPMSUB.xyz VF01, VF11, VF08 SQI.xyzw VF01, (VI02++)
+ MULi.xyz VF06, VF06, I NOP
+ MULi.xyz VF07, VF07, I SQI.xyzw VF02, (VI02++)
+ MULi.xyz VF08, VF08, I NOP
+ MUL.xyz VF02, VF21, VF01 NOP
+ MUL.xyz VF03, VF12, VF01 NOP
+ MUL.xyz VF09, VF06, VF01 NOP
+ MUL.xyz VF10, VF07, VF01 NOP
+ MUL.xyz VF11, VF08, VF01 NOP
+ ABS.xyz VF03, VF03 NOP
+ ADDy.x VF05, VF09, VF09 NOP
+ ADDx.y VF05, VF10, VF10 NOP
+ ADDx.z VF05, VF11, VF11 NOP
+ NOP NOP
+EdgePairLoop:
+ ADDz.x VF05, VF05, VF09 NOP
+ ADDz.y VF05, VF05, VF10 NOP
+ ADDy.z VF05, VF05, VF11 NOP
+ MULAx.w ACC, VF00, VF02 IADD VI03, VI02, VI00
+ MADDAy.w ACC, VF00, VF02 LQD.xyzw VF01, (--VI02)
+ MADDz.w VF02, VF00, VF02 NOP
+ ABS.xyz VF05, VF05 NOP
+ MULAx.w ACC, VF00, VF03 NOP
+ MADDAy.w ACC, VF00, VF03 NOP
+ MADDAz.w ACC, VF00, VF03 NOP
+ MADDAx.w ACC, VF00, VF05 NOP
+ MADDAy.w ACC, VF00, VF05 NOP
+ MADDz.w VF03, VF00, VF05 NOP
+ ADDw.x VF04, VF00, VF02 NOP
+ MUL.xyz VF02, VF21, VF01 NOP
+ MUL.xyz VF03, VF12, VF01 NOP
+ MUL.xyz VF09, VF06, VF01 NOP
+ CLIPw.xyz VF04, VF03 NOP
+ MUL.xyz VF10, VF07, VF01 NOP
+ MUL.xyz VF11, VF08, VF01 NOP
+ ABS.xyz VF03, VF03 NOP
+ ADDy.x VF05, VF09, VF09 FCAND VI01, 0x3
+ ADDx.y VF05, VF10, VF10 IBNE VI01, VI00, QuitAndFail2
+ ADDx.z VF05, VF11, VF11 NOP
+ NOP IBNE VI03, VI00, EdgePairLoop
+ NOP NOP
+ NOP[E] IADDIU VI01, VI00, 0x1
+ NOP NOP
+
+EndOfMicrocode2: