282 lines
6.8 KiB
C++
282 lines
6.8 KiB
C++
#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 ¢er, 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 |