mirror of
https://mirror.ghproxy.com/https://github.com/dexyfex/CodeWalker
synced 2025-01-09 23:28:03 +08:00
Collision detection refactoring and improvements
This commit is contained in:
parent
dd03e24fb0
commit
58d2293358
@ -32,11 +32,11 @@ using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using CodeWalker.World;
|
||||
|
||||
using TC = System.ComponentModel.TypeConverterAttribute;
|
||||
using EXP = System.ComponentModel.ExpandableObjectConverter;
|
||||
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
|
||||
@ -231,9 +231,29 @@ namespace CodeWalker.GameFiles
|
||||
default: return null; // throw new Exception("Unknown bound type");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public virtual SpaceSphereIntersectResult SphereIntersect(ref BoundingSphere sph)
|
||||
{
|
||||
return new SpaceSphereIntersectResult();
|
||||
}
|
||||
public virtual SpaceRayIntersectResult RayIntersect(ref Ray ray, float maxdist = float.MaxValue, float itemhitdist = float.MaxValue)
|
||||
{
|
||||
return new SpaceRayIntersectResult();
|
||||
}
|
||||
|
||||
}
|
||||
[TC(typeof(EXP))] public class BoundSphere : Bounds
|
||||
{ }
|
||||
{
|
||||
public override SpaceSphereIntersectResult SphereIntersect(ref BoundingSphere sph)
|
||||
{
|
||||
return base.SphereIntersect(ref sph);
|
||||
}
|
||||
public override SpaceRayIntersectResult RayIntersect(ref Ray ray, float maxdist = float.MaxValue, float itemhitdist = float.MaxValue)
|
||||
{
|
||||
return base.RayIntersect(ref ray, maxdist, itemhitdist);
|
||||
}
|
||||
}
|
||||
[TC(typeof(EXP))] public class BoundCapsule : Bounds
|
||||
{
|
||||
public override long BlockLength
|
||||
@ -274,9 +294,27 @@ namespace CodeWalker.GameFiles
|
||||
writer.Write(this.Unknown_78h);
|
||||
writer.Write(this.Unknown_7Ch);
|
||||
}
|
||||
|
||||
public override SpaceSphereIntersectResult SphereIntersect(ref BoundingSphere sph)
|
||||
{
|
||||
return base.SphereIntersect(ref sph);
|
||||
}
|
||||
public override SpaceRayIntersectResult RayIntersect(ref Ray ray, float maxdist = float.MaxValue, float itemhitdist = float.MaxValue)
|
||||
{
|
||||
return base.RayIntersect(ref ray, maxdist, itemhitdist);
|
||||
}
|
||||
}
|
||||
[TC(typeof(EXP))] public class BoundBox : Bounds
|
||||
{ }
|
||||
{
|
||||
public override SpaceSphereIntersectResult SphereIntersect(ref BoundingSphere sph)
|
||||
{
|
||||
return base.SphereIntersect(ref sph);
|
||||
}
|
||||
public override SpaceRayIntersectResult RayIntersect(ref Ray ray, float maxdist = float.MaxValue, float itemhitdist = float.MaxValue)
|
||||
{
|
||||
return base.RayIntersect(ref ray, maxdist, itemhitdist);
|
||||
}
|
||||
}
|
||||
[TC(typeof(EXP))] public class BoundDisc : Bounds
|
||||
{
|
||||
public override long BlockLength
|
||||
@ -317,6 +355,15 @@ namespace CodeWalker.GameFiles
|
||||
writer.Write(this.Unknown_78h);
|
||||
writer.Write(this.Unknown_7Ch);
|
||||
}
|
||||
|
||||
public override SpaceSphereIntersectResult SphereIntersect(ref BoundingSphere sph)
|
||||
{
|
||||
return base.SphereIntersect(ref sph);
|
||||
}
|
||||
public override SpaceRayIntersectResult RayIntersect(ref Ray ray, float maxdist = float.MaxValue, float itemhitdist = float.MaxValue)
|
||||
{
|
||||
return base.RayIntersect(ref ray, maxdist, itemhitdist);
|
||||
}
|
||||
}
|
||||
[TC(typeof(EXP))] public class BoundCylinder : Bounds
|
||||
{
|
||||
@ -358,6 +405,15 @@ namespace CodeWalker.GameFiles
|
||||
writer.Write(this.Unknown_78h);
|
||||
writer.Write(this.Unknown_7Ch);
|
||||
}
|
||||
|
||||
public override SpaceSphereIntersectResult SphereIntersect(ref BoundingSphere sph)
|
||||
{
|
||||
return base.SphereIntersect(ref sph);
|
||||
}
|
||||
public override SpaceRayIntersectResult RayIntersect(ref Ray ray, float maxdist = float.MaxValue, float itemhitdist = float.MaxValue)
|
||||
{
|
||||
return base.RayIntersect(ref ray, maxdist, itemhitdist);
|
||||
}
|
||||
}
|
||||
[TC(typeof(EXP))] public class BoundGeometry : Bounds
|
||||
{
|
||||
@ -699,6 +755,15 @@ namespace CodeWalker.GameFiles
|
||||
}
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
public override SpaceSphereIntersectResult SphereIntersect(ref BoundingSphere sph)
|
||||
{
|
||||
return base.SphereIntersect(ref sph);
|
||||
}
|
||||
public override SpaceRayIntersectResult RayIntersect(ref Ray ray, float maxdist = float.MaxValue, float itemhitdist = float.MaxValue)
|
||||
{
|
||||
return base.RayIntersect(ref ray, maxdist, itemhitdist);
|
||||
}
|
||||
}
|
||||
|
||||
[TC(typeof(EXP))] public class BoundUnknown1 : ResourceSystemBlock
|
||||
@ -1186,6 +1251,345 @@ namespace CodeWalker.GameFiles
|
||||
if (BVH != null) list.Add(BVH);
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override SpaceSphereIntersectResult SphereIntersect(ref BoundingSphere sph)
|
||||
{
|
||||
var res = new SpaceSphereIntersectResult();
|
||||
var box = new BoundingBox();
|
||||
Vector3 p1, p2, p3, p4, a1, a2, a3;
|
||||
Vector3 n1 = Vector3.Zero;
|
||||
var tsph = new BoundingSphere();
|
||||
var spht = new BoundingSphere();
|
||||
var sp = sph.Center;
|
||||
var sr = sph.Radius;
|
||||
|
||||
|
||||
if (Polygons == null)
|
||||
{ return res; }
|
||||
if ((BVH?.Nodes?.data_items == null) || (BVH?.Trees?.data_items == null))
|
||||
{ return res; }
|
||||
|
||||
box.Minimum = BoundingBoxMin;
|
||||
box.Maximum = BoundingBoxMax;
|
||||
if (!sph.Intersects(ref box))
|
||||
{ return res; }
|
||||
|
||||
var q = BVH.Quantum.XYZ();
|
||||
var c = BVH.BoundingBoxCenter.XYZ();
|
||||
var cg = CenterGeom;
|
||||
for (int t = 0; t < BVH.Trees.data_items.Length; t++)
|
||||
{
|
||||
var tree = BVH.Trees.data_items[t];
|
||||
box.Minimum = new Vector3(tree.MinX, tree.MinY, tree.MinZ) * q + c;
|
||||
box.Maximum = new Vector3(tree.MaxX, tree.MaxY, tree.MaxZ) * q + c;
|
||||
if (!sph.Intersects(ref box))
|
||||
{ continue; }
|
||||
|
||||
int nodeind = tree.NodeIndex1;
|
||||
int lastind = tree.NodeIndex2;
|
||||
while (nodeind < lastind)
|
||||
{
|
||||
var node = BVH.Nodes.data_items[nodeind];
|
||||
box.Minimum = new Vector3(node.MinX, node.MinY, node.MinZ) * q + c;
|
||||
box.Maximum = new Vector3(node.MaxX, node.MaxY, node.MaxZ) * q + c;
|
||||
bool nodehit = sph.Intersects(ref box);
|
||||
bool nodeskip = !nodehit;
|
||||
if (node.PolyCount <= 0) //intermediate node with child nodes
|
||||
{
|
||||
if (nodeskip)
|
||||
{
|
||||
nodeind += node.PolyId; //(child node count)
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeind++;
|
||||
}
|
||||
}
|
||||
else //leaf node, with polygons
|
||||
{
|
||||
if (!nodeskip)
|
||||
{
|
||||
var lastp = node.PolyId + node.PolyCount;
|
||||
lastp = Math.Min(lastp, (int)PolygonsCount);
|
||||
for (int p = node.PolyId; p < lastp; p++)
|
||||
{
|
||||
var polygon = Polygons[p];
|
||||
bool polyhit = false;
|
||||
switch (polygon.Type)
|
||||
{
|
||||
case BoundPolygonType.Triangle:
|
||||
var ptri = polygon as BoundPolygonTriangle;
|
||||
p1 = GetVertex(ptri.vertIndex1) + cg;
|
||||
p2 = GetVertex(ptri.vertIndex2) + cg;
|
||||
p3 = GetVertex(ptri.vertIndex3) + cg;
|
||||
polyhit = sph.Intersects(ref p1, ref p2, ref p3);
|
||||
if (polyhit) n1 = Vector3.Normalize(Vector3.Cross(p2 - p1, p3 - p1));
|
||||
break;
|
||||
case BoundPolygonType.Sphere:
|
||||
var psph = polygon as BoundPolygonSphere;
|
||||
tsph.Center = GetVertex(psph.sphereIndex) + cg;
|
||||
tsph.Radius = psph.sphereRadius;
|
||||
polyhit = sph.Intersects(ref tsph);
|
||||
if (polyhit) n1 = Vector3.Normalize(sph.Center - tsph.Center);
|
||||
break;
|
||||
case BoundPolygonType.Capsule:
|
||||
var pcap = polygon as BoundPolygonCapsule;
|
||||
var tcap = new BoundingCapsule();
|
||||
tcap.PointA = GetVertex(pcap.capsuleIndex1) + cg;
|
||||
tcap.PointB = GetVertex(pcap.capsuleIndex2) + cg;
|
||||
tcap.Radius = pcap.capsuleRadius;
|
||||
polyhit = sph.Intersects(ref tcap, out n1);
|
||||
break;
|
||||
case BoundPolygonType.Box:
|
||||
var pbox = polygon as BoundPolygonBox;
|
||||
p1 = GetVertex(pbox.boxIndex1);// + cg; //corner
|
||||
p2 = GetVertex(pbox.boxIndex2);// + cg;
|
||||
p3 = GetVertex(pbox.boxIndex3);// + cg;
|
||||
p4 = GetVertex(pbox.boxIndex4);// + cg;
|
||||
a1 = ((p3 + p4) - (p1 + p2)) * 0.5f;
|
||||
a2 = p3 - (p1 + a1);
|
||||
a3 = p4 - (p1 + a1);
|
||||
Vector3 bs = new Vector3(a1.Length(), a2.Length(), a3.Length());
|
||||
Vector3 m1 = a1 / bs.X;
|
||||
Vector3 m2 = a2 / bs.Y;
|
||||
Vector3 m3 = a3 / bs.Z;
|
||||
if ((bs.X < bs.Y) && (bs.X < bs.Z)) m1 = Vector3.Cross(m2, m3);
|
||||
else if (bs.Y < bs.Z) m2 = Vector3.Cross(m3, m1);
|
||||
else m3 = Vector3.Cross(m1, m2);
|
||||
Vector3 tp = sp - (p1 + cg);
|
||||
spht.Center = new Vector3(Vector3.Dot(tp, m1), Vector3.Dot(tp, m2), Vector3.Dot(tp, m3));
|
||||
spht.Radius = sph.Radius;
|
||||
box.Minimum = Vector3.Zero;
|
||||
box.Maximum = bs;
|
||||
polyhit = spht.Intersects(ref box);
|
||||
if (polyhit)
|
||||
{
|
||||
Vector3 smin = spht.Center - spht.Radius;
|
||||
Vector3 smax = spht.Center + spht.Radius;
|
||||
float eps = spht.Radius * 0.8f;
|
||||
n1 = Vector3.Zero;
|
||||
if (Math.Abs(smax.X) < eps) n1 -= m1;
|
||||
else if (Math.Abs(smin.X - bs.X) < eps) n1 += m1;
|
||||
if (Math.Abs(smax.Y) < eps) n1 -= m2;
|
||||
else if (Math.Abs(smin.Y - bs.Y) < eps) n1 += m2;
|
||||
if (Math.Abs(smax.Z) < eps) n1 -= m3;
|
||||
else if (Math.Abs(smin.Z - bs.Z) < eps) n1 += m3;
|
||||
float n1l = n1.Length();
|
||||
if (n1l > 0.0f) n1 = n1 / n1l;
|
||||
else n1 = Vector3.UnitZ;
|
||||
}
|
||||
break;
|
||||
case BoundPolygonType.Cylinder:
|
||||
var pcyl = polygon as BoundPolygonCylinder;
|
||||
//var tcyl = new BoundingCylinder();
|
||||
//tcyl.PointA = GetVertex(pcyl.cylinderIndex1) + cg;
|
||||
//tcyl.PointB = GetVertex(pcyl.cylinderIndex2) + cg;
|
||||
//tcyl.Radius = pcyl.cylinderRadius;
|
||||
//////polyhit = ray.Intersects(ref tcyl, out polyhittestdist, out n1);
|
||||
////////TODO
|
||||
var ttcap = new BoundingCapsule();//just use the capsule intersection for now...
|
||||
ttcap.PointA = GetVertex(pcyl.cylinderIndex1) + cg;
|
||||
ttcap.PointB = GetVertex(pcyl.cylinderIndex2) + cg;
|
||||
ttcap.Radius = pcyl.cylinderRadius;
|
||||
polyhit = sph.Intersects(ref ttcap, out n1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (polyhit) // && (polyhittestdist < itemhitdist) && (polyhittestdist < maxdist))
|
||||
{
|
||||
res.HitPolygon = polygon;
|
||||
//itemhitdist = polyhittestdist;
|
||||
//ybnhit = true;
|
||||
res.Hit = true;
|
||||
res.Normal = n1;
|
||||
}
|
||||
res.TestedPolyCount++;
|
||||
}
|
||||
}
|
||||
nodeind++;
|
||||
}
|
||||
res.TestedNodeCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public override SpaceRayIntersectResult RayIntersect(ref Ray ray, float maxdist = float.MaxValue, float itemhitdist = float.MaxValue)
|
||||
{
|
||||
var res = new SpaceRayIntersectResult();
|
||||
var box = new BoundingBox();
|
||||
var tsph = new BoundingSphere();
|
||||
var rayt = new Ray();
|
||||
Vector3 p1, p2, p3, p4, a1, a2, a3;
|
||||
Vector3 n1 = Vector3.Zero;
|
||||
var rp = ray.Position;
|
||||
var rd = ray.Direction;
|
||||
|
||||
|
||||
|
||||
if (Polygons == null)
|
||||
{ return res; }
|
||||
if ((BVH?.Nodes?.data_items == null) || (BVH?.Trees?.data_items == null))
|
||||
{ return res; }
|
||||
|
||||
box.Minimum = BoundingBoxMin;
|
||||
box.Maximum = BoundingBoxMax;
|
||||
float bvhboxhittest;
|
||||
if (!ray.Intersects(ref box, out bvhboxhittest))
|
||||
{ return res; }
|
||||
if (bvhboxhittest > itemhitdist)
|
||||
{ return res; } //already a closer hit.
|
||||
|
||||
res.HitDist = itemhitdist;
|
||||
|
||||
var q = BVH.Quantum.XYZ();
|
||||
var c = BVH.BoundingBoxCenter.XYZ();
|
||||
var cg = CenterGeom;
|
||||
for (int t = 0; t < BVH.Trees.data_items.Length; t++)
|
||||
{
|
||||
var tree = BVH.Trees.data_items[t];
|
||||
box.Minimum = new Vector3(tree.MinX, tree.MinY, tree.MinZ) * q + c;
|
||||
box.Maximum = new Vector3(tree.MaxX, tree.MaxY, tree.MaxZ) * q + c;
|
||||
if (!ray.Intersects(ref box, out bvhboxhittest))
|
||||
{ continue; }
|
||||
if (bvhboxhittest > res.HitDist)
|
||||
{ continue; } //already a closer hit.
|
||||
if (bvhboxhittest > maxdist)
|
||||
{ continue; }
|
||||
|
||||
int nodeind = tree.NodeIndex1;
|
||||
int lastind = tree.NodeIndex2;
|
||||
while (nodeind < lastind)
|
||||
{
|
||||
var node = BVH.Nodes.data_items[nodeind];
|
||||
box.Minimum = new Vector3(node.MinX, node.MinY, node.MinZ) * q + c;
|
||||
box.Maximum = new Vector3(node.MaxX, node.MaxY, node.MaxZ) * q + c;
|
||||
bool nodehit = ray.Intersects(ref box, out bvhboxhittest);
|
||||
bool nodeskip = !nodehit || (bvhboxhittest > res.HitDist);
|
||||
if (node.PolyCount <= 0) //intermediate node with child nodes
|
||||
{
|
||||
if (nodeskip)
|
||||
{
|
||||
nodeind += node.PolyId; //(child node count)
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeind++;
|
||||
}
|
||||
}
|
||||
else //leaf node, with polygons
|
||||
{
|
||||
if (!nodeskip)
|
||||
{
|
||||
var lastp = node.PolyId + node.PolyCount;
|
||||
lastp = Math.Min(lastp, (int)PolygonsCount);
|
||||
for (int p = node.PolyId; p < lastp; p++)
|
||||
{
|
||||
var polygon = Polygons[p];
|
||||
float polyhittestdist = float.MaxValue;
|
||||
bool polyhit = false;
|
||||
switch (polygon.Type)
|
||||
{
|
||||
case BoundPolygonType.Triangle:
|
||||
var ptri = polygon as BoundPolygonTriangle;
|
||||
p1 = GetVertex(ptri.vertIndex1) + cg;
|
||||
p2 = GetVertex(ptri.vertIndex2) + cg;
|
||||
p3 = GetVertex(ptri.vertIndex3) + cg;
|
||||
polyhit = ray.Intersects(ref p1, ref p2, ref p3, out polyhittestdist);
|
||||
if (polyhit) n1 = Vector3.Normalize(Vector3.Cross(p2 - p1, p3 - p1));
|
||||
break;
|
||||
case BoundPolygonType.Sphere:
|
||||
var psph = polygon as BoundPolygonSphere;
|
||||
tsph.Center = GetVertex(psph.sphereIndex) + cg;
|
||||
tsph.Radius = psph.sphereRadius;
|
||||
polyhit = ray.Intersects(ref tsph, out polyhittestdist);
|
||||
if (polyhit) n1 = Vector3.Normalize((ray.Position + ray.Direction * polyhittestdist) - tsph.Center);
|
||||
break;
|
||||
case BoundPolygonType.Capsule:
|
||||
var pcap = polygon as BoundPolygonCapsule;
|
||||
var tcap = new BoundingCapsule();
|
||||
tcap.PointA = GetVertex(pcap.capsuleIndex1) + cg;
|
||||
tcap.PointB = GetVertex(pcap.capsuleIndex2) + cg;
|
||||
tcap.Radius = pcap.capsuleRadius;
|
||||
polyhit = ray.Intersects(ref tcap, out polyhittestdist);
|
||||
res.Position = (ray.Position + ray.Direction * polyhittestdist);
|
||||
if (polyhit) n1 = tcap.Normal(ref res.Position);
|
||||
break;
|
||||
case BoundPolygonType.Box:
|
||||
var pbox = polygon as BoundPolygonBox;
|
||||
p1 = GetVertex(pbox.boxIndex1);// + cg; //corner
|
||||
p2 = GetVertex(pbox.boxIndex2);// + cg;
|
||||
p3 = GetVertex(pbox.boxIndex3);// + cg;
|
||||
p4 = GetVertex(pbox.boxIndex4);// + cg;
|
||||
a1 = ((p3 + p4) - (p1 + p2)) * 0.5f;
|
||||
a2 = p3 - (p1 + a1);
|
||||
a3 = p4 - (p1 + a1);
|
||||
Vector3 bs = new Vector3(a1.Length(), a2.Length(), a3.Length());
|
||||
Vector3 m1 = a1 / bs.X;
|
||||
Vector3 m2 = a2 / bs.Y;
|
||||
Vector3 m3 = a3 / bs.Z;
|
||||
if ((bs.X < bs.Y) && (bs.X < bs.Z)) m1 = Vector3.Cross(m2, m3);
|
||||
else if (bs.Y < bs.Z) m2 = Vector3.Cross(m3, m1);
|
||||
else m3 = Vector3.Cross(m1, m2);
|
||||
Vector3 tp = rp - (p1 + cg);
|
||||
rayt.Position = new Vector3(Vector3.Dot(tp, m1), Vector3.Dot(tp, m2), Vector3.Dot(tp, m3));
|
||||
rayt.Direction = new Vector3(Vector3.Dot(rd, m1), Vector3.Dot(rd, m2), Vector3.Dot(rd, m3));
|
||||
box.Minimum = Vector3.Zero;
|
||||
box.Maximum = bs;
|
||||
polyhit = rayt.Intersects(ref box, out polyhittestdist);
|
||||
if (polyhit)
|
||||
{
|
||||
Vector3 hpt = rayt.Position + rayt.Direction * polyhittestdist;
|
||||
const float eps = 0.002f;
|
||||
if (Math.Abs(hpt.X) < eps) n1 = -m1;
|
||||
else if (Math.Abs(hpt.X - bs.X) < eps) n1 = m1;
|
||||
else if (Math.Abs(hpt.Y) < eps) n1 = -m2;
|
||||
else if (Math.Abs(hpt.Y - bs.Y) < eps) n1 = m2;
|
||||
else if (Math.Abs(hpt.Z) < eps) n1 = -m3;
|
||||
else if (Math.Abs(hpt.Z - bs.Z) < eps) n1 = m3;
|
||||
else
|
||||
{ n1 = Vector3.UnitZ; } //ray starts inside the box...
|
||||
}
|
||||
break;
|
||||
case BoundPolygonType.Cylinder:
|
||||
var pcyl = polygon as BoundPolygonCylinder;
|
||||
var tcyl = new BoundingCylinder();
|
||||
tcyl.PointA = GetVertex(pcyl.cylinderIndex1) + cg;
|
||||
tcyl.PointB = GetVertex(pcyl.cylinderIndex2) + cg;
|
||||
tcyl.Radius = pcyl.cylinderRadius;
|
||||
polyhit = ray.Intersects(ref tcyl, out polyhittestdist, out n1);
|
||||
if (polyhit) n1.Normalize();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (polyhit && (polyhittestdist < res.HitDist) && (polyhittestdist < maxdist))
|
||||
{
|
||||
res.HitDist = polyhittestdist;
|
||||
res.Hit = true;
|
||||
res.Normal = n1;
|
||||
res.HitPolygon = polygon;
|
||||
|
||||
byte matind = ((PolygonMaterialIndices != null) && (p < PolygonMaterialIndices.Length)) ? PolygonMaterialIndices[p] : (byte)0;
|
||||
BoundMaterial_s mat = ((Materials != null) && (matind < Materials.Length)) ? Materials[matind] : new BoundMaterial_s();
|
||||
res.Material = mat;
|
||||
}
|
||||
res.TestedPolyCount++;
|
||||
}
|
||||
}
|
||||
nodeind++;
|
||||
}
|
||||
res.TestedNodeCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
[TC(typeof(EXP))] public class BoundComposite : Bounds
|
||||
{
|
||||
@ -1334,6 +1738,69 @@ namespace CodeWalker.GameFiles
|
||||
if (BVH != null) list.Add(BVH);
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public override SpaceSphereIntersectResult SphereIntersect(ref BoundingSphere sph)
|
||||
{
|
||||
var res = new SpaceSphereIntersectResult();
|
||||
|
||||
var compchilds = Children?.data_items;
|
||||
if (compchilds == null)
|
||||
{ return res; }
|
||||
|
||||
for (int i = 0; i < compchilds.Length; i++)
|
||||
{
|
||||
var c = compchilds[i];
|
||||
|
||||
var chit = c.SphereIntersect(ref sph);
|
||||
if (chit.Hit)
|
||||
{
|
||||
res.Hit = true;
|
||||
res.HitPolygon = chit.HitPolygon;
|
||||
res.Normal = chit.Normal;
|
||||
}
|
||||
res.TestedPolyCount += chit.TestedPolyCount;
|
||||
res.TestedNodeCount += chit.TestedNodeCount;
|
||||
}
|
||||
|
||||
res.TestComplete = true;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public override SpaceRayIntersectResult RayIntersect(ref Ray ray, float maxdist = float.MaxValue, float itemhitdist = float.MaxValue)
|
||||
{
|
||||
var res = new SpaceRayIntersectResult();
|
||||
res.HitDist = itemhitdist;
|
||||
|
||||
var compchilds = Children?.data_items;
|
||||
if (compchilds == null)
|
||||
{ return res; }
|
||||
for (int i = 0; i < compchilds.Length; i++)
|
||||
{
|
||||
Bounds c = compchilds[i];
|
||||
if (c == null)
|
||||
{ continue; }
|
||||
|
||||
var bghit = c.RayIntersect(ref ray, maxdist, res.HitDist);
|
||||
if (bghit.Hit)
|
||||
{
|
||||
res.Hit = true;
|
||||
res.HitDist = bghit.HitDist;
|
||||
res.HitPolygon = bghit.HitPolygon;
|
||||
res.Material = bghit.Material;
|
||||
res.Normal = bghit.Normal;
|
||||
}
|
||||
res.TestedNodeCount += bghit.TestedNodeCount;
|
||||
res.TestedPolyCount += bghit.TestedPolyCount;
|
||||
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[Flags] public enum EBoundCompositeFlags
|
||||
|
@ -96,4 +96,200 @@ namespace CodeWalker
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public struct BoundingCapsule
|
||||
{
|
||||
public Vector3 PointA;
|
||||
public Vector3 PointB;
|
||||
public float Radius;
|
||||
}
|
||||
|
||||
public static class BoundingCapsuleMath
|
||||
{
|
||||
|
||||
public static bool Intersects(this Ray r, ref BoundingCapsule capsule, out float dist)
|
||||
{
|
||||
// intersect capsule : http://www.iquilezles.org/www/articles/intersectors/intersectors.htm
|
||||
Vector3 ba = capsule.PointB - capsule.PointA;
|
||||
Vector3 oa = r.Position - capsule.PointA;
|
||||
float baba = Vector3.Dot(ba,ba);
|
||||
float bard = Vector3.Dot(ba,r.Direction);
|
||||
float baoa = Vector3.Dot(ba,oa);
|
||||
float rdoa = Vector3.Dot(r.Direction,oa);
|
||||
float oaoa = Vector3.Dot(oa,oa);
|
||||
|
||||
float r2 = capsule.Radius * capsule.Radius;
|
||||
float a = baba - bard*bard;
|
||||
float b = baba*rdoa - baoa*bard;
|
||||
float c = baba*oaoa - baoa*baoa - r2*baba;
|
||||
float h = b*b - a*c;
|
||||
|
||||
if( h>=0.0f )
|
||||
{
|
||||
float t = (-b-(float)Math.Sqrt(h))/a;
|
||||
|
||||
float y = baoa + t*bard;
|
||||
|
||||
// body
|
||||
if (y > 0.0f && y < baba)
|
||||
{
|
||||
dist = t;
|
||||
return true;
|
||||
}
|
||||
|
||||
// caps
|
||||
Vector3 oc = (y<=0.0f) ? oa : r.Position - capsule.PointB;
|
||||
b = Vector3.Dot(r.Direction,oc);
|
||||
c = Vector3.Dot(oc,oc) - r2;
|
||||
h = b*b - c;
|
||||
if( h>0.0f )
|
||||
{
|
||||
dist = -b - (float)Math.Sqrt(h);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
dist = -1.0f;
|
||||
return false;
|
||||
}
|
||||
public static Vector3 Normal(this BoundingCapsule c, ref Vector3 position)
|
||||
{
|
||||
Vector3 ba = c.PointB - c.PointA;
|
||||
Vector3 pa = position - c.PointA;
|
||||
float h = Math.Min(Math.Max(Vector3.Dot(pa,ba)/Vector3.Dot(ba,ba),0.0f),1.0f);
|
||||
return (pa - h*ba)/c.Radius;
|
||||
}
|
||||
|
||||
|
||||
public static bool Intersects(this BoundingSphere sph, ref BoundingCapsule capsule, out Vector3 norm)
|
||||
{
|
||||
var dist = LineMath.PointSegmentDistance(ref sph.Center, ref capsule.PointA, ref capsule.PointB);
|
||||
var rads = sph.Radius + capsule.Radius;
|
||||
if (dist <= rads)
|
||||
{
|
||||
norm = LineMath.PointSegmentNormal(ref sph.Center, ref capsule.PointA, ref capsule.PointB);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
norm = Vector3.Up;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public struct BoundingCylinder
|
||||
{
|
||||
public Vector3 PointA;
|
||||
public Vector3 PointB;
|
||||
public float Radius;
|
||||
}
|
||||
public static class BoundingCylinderMath
|
||||
{
|
||||
|
||||
public static bool Intersects(this Ray r, ref BoundingCylinder cylinder, out float dist, out Vector3 norm)
|
||||
{
|
||||
// intersect cylinder : https://www.shadertoy.com/view/4lcSRn
|
||||
Vector3 ba = cylinder.PointB - cylinder.PointA;
|
||||
Vector3 oc = r.Position - cylinder.PointA;
|
||||
float baba = Vector3.Dot(ba, ba);
|
||||
float bard = Vector3.Dot(ba, r.Direction);
|
||||
float baoc = Vector3.Dot(ba, oc);
|
||||
|
||||
float r2 = cylinder.Radius * cylinder.Radius;
|
||||
float k2 = baba - bard * bard;
|
||||
float k1 = baba * Vector3.Dot(oc, r.Direction) - baoc * bard;
|
||||
float k0 = baba * Vector3.Dot(oc, oc) - baoc * baoc - r2 * baba;
|
||||
|
||||
float h = k1 * k1 - k2 * k0;
|
||||
if (h < 0.0f)
|
||||
{
|
||||
dist = -1.0f;
|
||||
norm = Vector3.Up;
|
||||
return false;
|
||||
}
|
||||
h = (float)Math.Sqrt(h);
|
||||
float t = (-k1 - h) / k2;
|
||||
|
||||
// body
|
||||
float y = baoc + t * bard;
|
||||
if (y > 0.0f && y < baba)
|
||||
{
|
||||
dist = t;
|
||||
norm = (oc + t * r.Direction - ba * y / baba) / cylinder.Radius;
|
||||
return true;
|
||||
}
|
||||
|
||||
// caps
|
||||
t = (((y < 0.0f) ? 0.0f : baba) - baoc) / bard;
|
||||
if (Math.Abs(k1 + k2 * t) < h)
|
||||
{
|
||||
dist = t;
|
||||
norm = ba * Math.Sign(y) / baba;
|
||||
return true;
|
||||
}
|
||||
|
||||
dist = -1.0f;
|
||||
norm = Vector3.Up;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static class LineMath
|
||||
{
|
||||
|
||||
|
||||
public static float PointSegmentDistance(ref Vector3 v, ref Vector3 a, ref Vector3 b)
|
||||
{
|
||||
//https://stackoverflow.com/questions/4858264/find-the-distance-from-a-3d-point-to-a-line-segment
|
||||
Vector3 ab = b - a;
|
||||
Vector3 av = v - a;
|
||||
|
||||
if (Vector3.Dot(av, ab) <= 0.0f)// Point is lagging behind start of the segment, so perpendicular distance is not viable.
|
||||
{
|
||||
return av.Length(); // Use distance to start of segment instead.
|
||||
}
|
||||
|
||||
Vector3 bv = v - b;
|
||||
if (Vector3.Dot(bv, ab) >= 0.0f)// Point is advanced past the end of the segment, so perpendicular distance is not viable.
|
||||
{
|
||||
return bv.Length(); // Use distance to end of the segment instead.
|
||||
}
|
||||
|
||||
return Vector3.Cross(ab, av).Length() / ab.Length();// Perpendicular distance of point to segment.
|
||||
}
|
||||
|
||||
public static Vector3 PointSegmentNormal(ref Vector3 v, ref Vector3 a, ref Vector3 b)
|
||||
{
|
||||
Vector3 ab = b - a;
|
||||
Vector3 av = v - a;
|
||||
|
||||
if (Vector3.Dot(av, ab) <= 0.0f)
|
||||
{
|
||||
return Vector3.Normalize(av);
|
||||
}
|
||||
|
||||
Vector3 bv = v - b;
|
||||
if (Vector3.Dot(bv, ab) >= 0.0f)
|
||||
{
|
||||
return Vector3.Normalize(bv);
|
||||
}
|
||||
|
||||
return Vector3.Normalize(Vector3.Cross(Vector3.Cross(ab, av), ab));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -860,14 +860,14 @@ namespace CodeWalker.World
|
||||
|
||||
if (!r.SphereHit.Hit)
|
||||
{
|
||||
if (absdisp > (e.Radius * 2.0f)) //fast-moving... do a ray test to make sure it's not tunnelling
|
||||
if (absdisp > e.Radius) //fast-moving... do a ray test to make sure it's not tunnelling
|
||||
{
|
||||
Ray rayt = new Ray(sphpos, r.HitVelDir);
|
||||
float rayl = absdisp + e.Radius * 4.0f; //include some extra incase of glancing hit
|
||||
var rayhit = RayIntersect(rayt, rayl);
|
||||
if (rayhit.Hit) //looks like it is tunnelling... need to find the sphere hit point
|
||||
{
|
||||
sph.Center = rayhit.Position;
|
||||
sph.Center = rayhit.Position - (r.HitVelDir*Math.Min(e.Radius*0.5f, rayhit.HitDist));
|
||||
float hitd = rayhit.HitDist;
|
||||
r.HitT = hitd / absdisp;
|
||||
if (r.HitT > 1.0f)
|
||||
@ -1066,24 +1066,9 @@ namespace CodeWalker.World
|
||||
{
|
||||
var res = new SpaceRayIntersectResult();
|
||||
if (GameFileCache == null) return res;
|
||||
int polytestcount = 0;
|
||||
int nodetestcount = 0;
|
||||
bool testcomplete = true;
|
||||
res.HitDist = float.MaxValue;
|
||||
var box = new BoundingBox();
|
||||
var tsph = new BoundingSphere();
|
||||
var rayt = new Ray();
|
||||
var rp = ray.Position;
|
||||
var rd = ray.Direction;
|
||||
var boxhitdist = float.MaxValue;
|
||||
var itemhitdist = float.MaxValue;
|
||||
Vector3 p1, p2, p3, p4, a1, a2, a3;
|
||||
Vector3 n1 = Vector3.Zero;
|
||||
float polyhittestdist = 0;
|
||||
bool hit = false;
|
||||
BoundPolygon hitpoly = null;
|
||||
BoundMaterial_s hitmat = new BoundMaterial_s();
|
||||
Vector3 hitnorm = Vector3.Zero;
|
||||
Vector3 hitpos = Vector3.Zero;
|
||||
|
||||
if (BoundsStore == null) return res;
|
||||
var boundslist = BoundsStore.GetItems(ref ray);
|
||||
@ -1101,216 +1086,51 @@ namespace CodeWalker.World
|
||||
float boxhitdisttest;
|
||||
if (ray.Intersects(ref box, out boxhitdisttest))
|
||||
{
|
||||
if (boxhitdisttest > res.HitDist)
|
||||
{ continue; } //already a closer hit
|
||||
if (boxhitdisttest > maxdist)
|
||||
{ continue; }
|
||||
|
||||
YbnFile ybn = GameFileCache.GetYbn(bound.Name);
|
||||
if (ybn == null)
|
||||
{ continue; } //ybn not found?
|
||||
if (!ybn.Loaded)
|
||||
{ testcomplete = false; continue; } //ybn not loaded yet...
|
||||
|
||||
bool ybnhit = false;
|
||||
var b = ybn.Bounds;
|
||||
box.Minimum = b.BoundingBoxMin;
|
||||
box.Maximum = b.BoundingBoxMax;
|
||||
float itemboxhitdisttest;
|
||||
if (!ray.Intersects(ref box, out itemboxhitdisttest))
|
||||
{ continue; } //ray doesn't hit this ybn
|
||||
if (itemboxhitdisttest > itemhitdist)
|
||||
{ continue; } //already a closer hit.
|
||||
if (itemboxhitdisttest > maxdist)
|
||||
{ continue; }
|
||||
|
||||
switch (b.Type)
|
||||
//box.Minimum = b.BoundingBoxMin;
|
||||
//box.Maximum = b.BoundingBoxMax;
|
||||
//float itemboxhitdisttest;
|
||||
//if (!ray.Intersects(ref box, out itemboxhitdisttest))
|
||||
//{ continue; } //ray doesn't hit this ybn
|
||||
//if (itemboxhitdisttest > res.HitDist)
|
||||
//{ continue; } //already a closer hit.
|
||||
//if (itemboxhitdisttest > maxdist)
|
||||
//{ continue; }
|
||||
|
||||
var bhit = b.RayIntersect(ref ray, maxdist, res.HitDist);
|
||||
if (bhit.Hit)
|
||||
{
|
||||
case 10: //BoundComposite
|
||||
BoundComposite boundcomp = b as BoundComposite;
|
||||
if (boundcomp == null)
|
||||
{ continue; }
|
||||
var compchilds = boundcomp.Children?.data_items;
|
||||
if (compchilds == null)
|
||||
{ continue; }
|
||||
for (int i = 0; i < compchilds.Length; i++)
|
||||
{
|
||||
BoundBVH bgeom = compchilds[i] as BoundBVH;
|
||||
if (bgeom == null)
|
||||
{ continue; }
|
||||
if (bgeom.Polygons == null)
|
||||
{ continue; }
|
||||
if ((bgeom.BVH?.Nodes?.data_items == null) || (bgeom.BVH?.Trees?.data_items == null))
|
||||
{ continue; }
|
||||
|
||||
box.Minimum = bgeom.BoundingBoxMin;
|
||||
box.Maximum = bgeom.BoundingBoxMax;
|
||||
float bvhboxhittest;
|
||||
if (!ray.Intersects(ref box, out bvhboxhittest))
|
||||
{ continue; }
|
||||
if (bvhboxhittest > itemhitdist)
|
||||
{ continue; } //already a closer hit.
|
||||
|
||||
var q = bgeom.BVH.Quantum.XYZ();
|
||||
var c = bgeom.BVH.BoundingBoxCenter.XYZ();
|
||||
var cg = bgeom.CenterGeom;
|
||||
for (int t = 0; t < bgeom.BVH.Trees.data_items.Length; t++)
|
||||
{
|
||||
var tree = bgeom.BVH.Trees.data_items[t];
|
||||
box.Minimum = new Vector3(tree.MinX, tree.MinY, tree.MinZ) * q + c;
|
||||
box.Maximum = new Vector3(tree.MaxX, tree.MaxY, tree.MaxZ) * q + c;
|
||||
if (!ray.Intersects(ref box, out bvhboxhittest))
|
||||
{ continue; }
|
||||
if (bvhboxhittest > itemhitdist)
|
||||
{ continue; } //already a closer hit.
|
||||
if (bvhboxhittest > maxdist)
|
||||
{ continue; }
|
||||
|
||||
int nodeind = tree.NodeIndex1;
|
||||
int lastind = tree.NodeIndex2;
|
||||
while (nodeind < lastind)
|
||||
{
|
||||
var node = bgeom.BVH.Nodes.data_items[nodeind];
|
||||
box.Minimum = new Vector3(node.MinX, node.MinY, node.MinZ) * q + c;
|
||||
box.Maximum = new Vector3(node.MaxX, node.MaxY, node.MaxZ) * q + c;
|
||||
bool nodehit = ray.Intersects(ref box, out bvhboxhittest);
|
||||
bool nodeskip = !nodehit || (bvhboxhittest > itemhitdist);
|
||||
if (node.PolyCount <= 0) //intermediate node with child nodes
|
||||
{
|
||||
if (nodeskip)
|
||||
{
|
||||
nodeind += node.PolyId; //(child node count)
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeind++;
|
||||
}
|
||||
}
|
||||
else //leaf node, with polygons
|
||||
{
|
||||
if (!nodeskip)
|
||||
{
|
||||
var lastp = node.PolyId + node.PolyCount;
|
||||
lastp = Math.Min(lastp, (int)bgeom.PolygonsCount);
|
||||
for (int p = node.PolyId; p < lastp; p++)
|
||||
{
|
||||
var polygon = bgeom.Polygons[p];
|
||||
bool polyhit = false;
|
||||
switch (polygon.Type)
|
||||
{
|
||||
case BoundPolygonType.Triangle:
|
||||
var ptri = polygon as BoundPolygonTriangle;
|
||||
p1 = bgeom.GetVertex(ptri.vertIndex1) + cg;
|
||||
p2 = bgeom.GetVertex(ptri.vertIndex2) + cg;
|
||||
p3 = bgeom.GetVertex(ptri.vertIndex3) + cg;
|
||||
polyhit = ray.Intersects(ref p1, ref p2, ref p3, out polyhittestdist);
|
||||
if (polyhit) n1 = Vector3.Normalize(Vector3.Cross(p2 - p1, p3 - p1));
|
||||
break;
|
||||
case BoundPolygonType.Sphere:
|
||||
var psph = polygon as BoundPolygonSphere;
|
||||
tsph.Center = bgeom.GetVertex(psph.sphereIndex) + cg;
|
||||
tsph.Radius = psph.sphereRadius;
|
||||
polyhit = ray.Intersects(ref tsph, out polyhittestdist);
|
||||
if (polyhit) n1 = Vector3.Normalize((ray.Position + ray.Direction * polyhittestdist) - tsph.Center);
|
||||
break;
|
||||
case BoundPolygonType.Capsule:
|
||||
//TODO
|
||||
break;
|
||||
case BoundPolygonType.Box:
|
||||
var pbox = polygon as BoundPolygonBox;
|
||||
p1 = bgeom.GetVertex(pbox.boxIndex1);// + cg; //corner
|
||||
p2 = bgeom.GetVertex(pbox.boxIndex2);// + cg;
|
||||
p3 = bgeom.GetVertex(pbox.boxIndex3);// + cg;
|
||||
p4 = bgeom.GetVertex(pbox.boxIndex4);// + cg;
|
||||
a1 = ((p3 + p4) - (p1 + p2)) * 0.5f;
|
||||
a2 = p3 - (p1 + a1);
|
||||
a3 = p4 - (p1 + a1);
|
||||
Vector3 bs = new Vector3(a1.Length(), a2.Length(), a3.Length());
|
||||
Vector3 m1 = a1 / bs.X;
|
||||
Vector3 m2 = a2 / bs.Y;
|
||||
Vector3 m3 = a3 / bs.Z;
|
||||
if ((bs.X < bs.Y) && (bs.X < bs.Z)) m1 = Vector3.Cross(m2, m3);
|
||||
else if (bs.Y < bs.Z) m2 = Vector3.Cross(m1, m3);
|
||||
else m3 = Vector3.Cross(m1, m2);
|
||||
Vector3 tp = rp - (p1 + cg);
|
||||
rayt.Position = new Vector3(Vector3.Dot(tp, m1), Vector3.Dot(tp, m2), Vector3.Dot(tp, m3));
|
||||
rayt.Direction = new Vector3(Vector3.Dot(rd, m1), Vector3.Dot(rd, m2), Vector3.Dot(rd, m3));
|
||||
box.Minimum = Vector3.Zero;
|
||||
box.Maximum = bs;
|
||||
polyhit = rayt.Intersects(ref box, out polyhittestdist);
|
||||
if (polyhit)
|
||||
{
|
||||
Vector3 hpt = rayt.Position + rayt.Direction * polyhittestdist;
|
||||
const float eps = 0.002f;
|
||||
if (Math.Abs(hpt.X) < eps) n1 = -m1;
|
||||
else if (Math.Abs(hpt.X - bs.X) < eps) n1 = m1;
|
||||
else if (Math.Abs(hpt.Y) < eps) n1 = -m2;
|
||||
else if (Math.Abs(hpt.Y - bs.Y) < eps) n1 = m2;
|
||||
else if (Math.Abs(hpt.Z) < eps) n1 = -m3;
|
||||
else if (Math.Abs(hpt.Z - bs.Z) < eps) n1 = m3;
|
||||
else
|
||||
{ n1 = Vector3.UnitZ; } //ray starts inside the box...
|
||||
}
|
||||
break;
|
||||
case BoundPolygonType.Cylinder:
|
||||
//TODO
|
||||
break;
|
||||
}
|
||||
if (polyhit && (polyhittestdist < itemhitdist) && (polyhittestdist < maxdist))
|
||||
{
|
||||
itemhitdist = polyhittestdist;
|
||||
ybnhit = true;
|
||||
hit = true;
|
||||
hitnorm = n1;
|
||||
hitpoly = polygon;
|
||||
|
||||
byte matind = ((bgeom.PolygonMaterialIndices != null) && (p < bgeom.PolygonMaterialIndices.Length)) ? bgeom.PolygonMaterialIndices[p] : (byte)0;
|
||||
BoundMaterial_s mat = ((bgeom.Materials != null) && (matind < bgeom.Materials.Length)) ? bgeom.Materials[matind] : new BoundMaterial_s();
|
||||
hitmat = mat;
|
||||
}
|
||||
polytestcount++;
|
||||
}
|
||||
}
|
||||
nodeind++;
|
||||
}
|
||||
nodetestcount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 3: //BoundBox - found in drawables - TODO
|
||||
BoundBox boundbox = b as BoundBox;
|
||||
if (boundbox == null)
|
||||
{ continue; }
|
||||
break;
|
||||
case 0: //BoundSphere - found in drawables - TODO
|
||||
BoundSphere boundsphere = b as BoundSphere;
|
||||
if (boundsphere == null)
|
||||
{ continue; }
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
res.Hit = true;
|
||||
res.HitDist = bhit.HitDist;
|
||||
res.HitPolygon = bhit.HitPolygon;
|
||||
res.Material = bhit.Material;
|
||||
res.Normal = bhit.Normal;
|
||||
}
|
||||
|
||||
|
||||
if (ybnhit)
|
||||
{
|
||||
boxhitdist = boxhitdisttest;
|
||||
//hit = true;
|
||||
}
|
||||
res.TestedNodeCount += bhit.TestedNodeCount;
|
||||
res.TestedPolyCount += bhit.TestedPolyCount;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (hit)
|
||||
if (res.Hit)
|
||||
{
|
||||
hitpos = ray.Position + ray.Direction * itemhitdist;
|
||||
res.Position = ray.Position + ray.Direction * res.HitDist;
|
||||
}
|
||||
|
||||
res.TestedNodeCount = nodetestcount;
|
||||
res.TestedPolyCount = polytestcount;
|
||||
res.TestComplete = testcomplete;
|
||||
res.Hit = hit;
|
||||
res.HitDist = itemhitdist;
|
||||
res.HitPolygon = hitpoly;
|
||||
res.Material = hitmat;
|
||||
res.Position = hitpos;
|
||||
res.Normal = hitnorm;
|
||||
|
||||
return res;
|
||||
}
|
||||
@ -1325,20 +1145,6 @@ namespace CodeWalker.World
|
||||
Vector3 sphmin = sph.Center - sph.Radius;
|
||||
Vector3 sphmax = sph.Center + sph.Radius;
|
||||
var box = new BoundingBox();
|
||||
var tsph = new BoundingSphere();
|
||||
var spht = new BoundingSphere();
|
||||
var sp = sph.Center;
|
||||
var sr = sph.Radius;
|
||||
//var boxhitdist = float.MaxValue;
|
||||
//var itemhitdist = float.MaxValue;
|
||||
Vector3 p1, p2, p3, p4, a1, a2, a3;
|
||||
Vector3 n1 = Vector3.Zero;
|
||||
//float polyhittestdist = 0;
|
||||
bool hit = false;
|
||||
BoundPolygon hitpoly = null;
|
||||
Vector3 hitnorm = Vector3.Zero;
|
||||
Vector3 hitpos = Vector3.Zero;
|
||||
|
||||
|
||||
if (BoundsStore == null) return res;
|
||||
var boundslist = BoundsStore.GetItems(ref sphmin, ref sphmax);
|
||||
@ -1356,184 +1162,25 @@ namespace CodeWalker.World
|
||||
if (!ybn.Loaded)
|
||||
{ testcomplete = false; continue; } //ybn not loaded yet...
|
||||
|
||||
//bool ybnhit = false;
|
||||
var b = ybn.Bounds;
|
||||
box.Minimum = b.BoundingBoxMin;
|
||||
box.Maximum = b.BoundingBoxMax;
|
||||
if (!sph.Intersects(ref box))
|
||||
{ continue; } //ray doesn't hit this ybn
|
||||
|
||||
switch (b.Type)
|
||||
//box.Minimum = b.BoundingBoxMin;
|
||||
//box.Maximum = b.BoundingBoxMax;
|
||||
//if (!sph.Intersects(ref box))
|
||||
//{ continue; } //ray doesn't hit this ybn
|
||||
|
||||
var bhit = b.SphereIntersect(ref sph);
|
||||
if (bhit.Hit)
|
||||
{
|
||||
case 10: //BoundComposite
|
||||
BoundComposite boundcomp = b as BoundComposite;
|
||||
if (boundcomp == null)
|
||||
{ continue; }
|
||||
var compchilds = boundcomp.Children?.data_items;
|
||||
if (compchilds == null)
|
||||
{ continue; }
|
||||
for (int i = 0; i < compchilds.Length; i++)
|
||||
{
|
||||
BoundBVH bgeom = compchilds[i] as BoundBVH;
|
||||
if (bgeom == null)
|
||||
{ continue; }
|
||||
if (bgeom.Polygons == null)
|
||||
{ continue; }
|
||||
if ((bgeom.BVH?.Nodes?.data_items == null) || (bgeom.BVH?.Trees?.data_items == null))
|
||||
{ continue; }
|
||||
|
||||
box.Minimum = bgeom.BoundingBoxMin;
|
||||
box.Maximum = bgeom.BoundingBoxMax;
|
||||
if (!sph.Intersects(ref box))
|
||||
{ continue; }
|
||||
|
||||
var q = bgeom.BVH.Quantum.XYZ();
|
||||
var c = bgeom.BVH.BoundingBoxCenter.XYZ();
|
||||
var cg = bgeom.CenterGeom;
|
||||
for (int t = 0; t < bgeom.BVH.Trees.data_items.Length; t++)
|
||||
{
|
||||
var tree = bgeom.BVH.Trees.data_items[t];
|
||||
box.Minimum = new Vector3(tree.MinX, tree.MinY, tree.MinZ) * q + c;
|
||||
box.Maximum = new Vector3(tree.MaxX, tree.MaxY, tree.MaxZ) * q + c;
|
||||
if (!sph.Intersects(ref box))
|
||||
{ continue; }
|
||||
|
||||
int nodeind = tree.NodeIndex1;
|
||||
int lastind = tree.NodeIndex2;
|
||||
while (nodeind < lastind)
|
||||
{
|
||||
var node = bgeom.BVH.Nodes.data_items[nodeind];
|
||||
box.Minimum = new Vector3(node.MinX, node.MinY, node.MinZ) * q + c;
|
||||
box.Maximum = new Vector3(node.MaxX, node.MaxY, node.MaxZ) * q + c;
|
||||
bool nodehit = sph.Intersects(ref box);
|
||||
bool nodeskip = !nodehit;
|
||||
if (node.PolyCount <= 0) //intermediate node with child nodes
|
||||
{
|
||||
if (nodeskip)
|
||||
{
|
||||
nodeind += node.PolyId; //(child node count)
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeind++;
|
||||
}
|
||||
}
|
||||
else //leaf node, with polygons
|
||||
{
|
||||
if (!nodeskip)
|
||||
{
|
||||
var lastp = node.PolyId + node.PolyCount;
|
||||
lastp = Math.Min(lastp, (int)bgeom.PolygonsCount);
|
||||
for (int p = node.PolyId; p < lastp; p++)
|
||||
{
|
||||
var polygon = bgeom.Polygons[p];
|
||||
bool polyhit = false;
|
||||
switch (polygon.Type)
|
||||
{
|
||||
case BoundPolygonType.Triangle:
|
||||
var ptri = polygon as BoundPolygonTriangle;
|
||||
p1 = bgeom.GetVertex(ptri.vertIndex1) + cg;
|
||||
p2 = bgeom.GetVertex(ptri.vertIndex2) + cg;
|
||||
p3 = bgeom.GetVertex(ptri.vertIndex3) + cg;
|
||||
polyhit = sph.Intersects(ref p1, ref p2, ref p3);
|
||||
if (polyhit) n1 = Vector3.Normalize(Vector3.Cross(p2 - p1, p3 - p1));
|
||||
break;
|
||||
case BoundPolygonType.Sphere:
|
||||
var psph = polygon as BoundPolygonSphere;
|
||||
tsph.Center = bgeom.GetVertex(psph.sphereIndex) + cg;
|
||||
tsph.Radius = psph.sphereRadius;
|
||||
polyhit = sph.Intersects(ref tsph);
|
||||
if (polyhit) n1 = Vector3.Normalize(sph.Center - tsph.Center);
|
||||
break;
|
||||
case BoundPolygonType.Capsule:
|
||||
//TODO
|
||||
break;
|
||||
case BoundPolygonType.Box:
|
||||
var pbox = polygon as BoundPolygonBox;
|
||||
p1 = bgeom.GetVertex(pbox.boxIndex1);// + cg; //corner
|
||||
p2 = bgeom.GetVertex(pbox.boxIndex2);// + cg;
|
||||
p3 = bgeom.GetVertex(pbox.boxIndex3);// + cg;
|
||||
p4 = bgeom.GetVertex(pbox.boxIndex4);// + cg;
|
||||
a1 = ((p3 + p4) - (p1 + p2)) * 0.5f;
|
||||
a2 = p3 - (p1 + a1);
|
||||
a3 = p4 - (p1 + a1);
|
||||
Vector3 bs = new Vector3(a1.Length(), a2.Length(), a3.Length());
|
||||
Vector3 m1 = a1 / bs.X;
|
||||
Vector3 m2 = a2 / bs.Y;
|
||||
Vector3 m3 = a3 / bs.Z;
|
||||
if ((bs.X < bs.Y) && (bs.X < bs.Z)) m1 = Vector3.Cross(m2, m3);
|
||||
else if (bs.Y < bs.Z) m2 = Vector3.Cross(m1, m3);
|
||||
else m3 = Vector3.Cross(m1, m2);
|
||||
Vector3 tp = sp - (p1 + cg);
|
||||
spht.Center = new Vector3(Vector3.Dot(tp, m1), Vector3.Dot(tp, m2), Vector3.Dot(tp, m3));
|
||||
spht.Radius = sph.Radius;
|
||||
box.Minimum = Vector3.Zero;
|
||||
box.Maximum = bs;
|
||||
polyhit = spht.Intersects(ref box);
|
||||
if (polyhit)
|
||||
{
|
||||
Vector3 smin = spht.Center - spht.Radius;
|
||||
Vector3 smax = spht.Center + spht.Radius;
|
||||
float eps = spht.Radius * 0.8f;
|
||||
n1 = Vector3.Zero;
|
||||
if (Math.Abs(smax.X) < eps) n1 -= m1;
|
||||
else if (Math.Abs(smin.X - bs.X) < eps) n1 += m1;
|
||||
if (Math.Abs(smax.Y) < eps) n1 -= m2;
|
||||
else if (Math.Abs(smin.Y - bs.Y) < eps) n1 += m2;
|
||||
if (Math.Abs(smax.Z) < eps) n1 -= m3;
|
||||
else if (Math.Abs(smin.Z - bs.Z) < eps) n1 += m3;
|
||||
float n1l = n1.Length();
|
||||
if (n1l > 0.0f) n1 = n1 / n1l;
|
||||
else n1 = Vector3.UnitZ;
|
||||
}
|
||||
break;
|
||||
case BoundPolygonType.Cylinder:
|
||||
//TODO
|
||||
break;
|
||||
}
|
||||
if (polyhit) // && (polyhittestdist < itemhitdist) && (polyhittestdist < maxdist))
|
||||
{
|
||||
hitpoly = polygon;
|
||||
//itemhitdist = polyhittestdist;
|
||||
//ybnhit = true;
|
||||
hit = true;
|
||||
hitnorm = n1;
|
||||
}
|
||||
polytestcount++;
|
||||
}
|
||||
}
|
||||
nodeind++;
|
||||
}
|
||||
nodetestcount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 3: //BoundBox - found in drawables - TODO
|
||||
BoundBox boundbox = b as BoundBox;
|
||||
if (boundbox == null)
|
||||
{ continue; }
|
||||
break;
|
||||
case 0: //BoundSphere - found in drawables - TODO
|
||||
BoundSphere boundsphere = b as BoundSphere;
|
||||
if (boundsphere == null)
|
||||
{ continue; }
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
res.Hit = true;
|
||||
res.HitPolygon = bhit.HitPolygon;
|
||||
res.Normal = bhit.Normal;
|
||||
}
|
||||
|
||||
|
||||
//if (ybnhit)
|
||||
//{
|
||||
// //boxhitdist = boxhitdisttest;
|
||||
// //hit = true;
|
||||
//}
|
||||
|
||||
polytestcount += bhit.TestedPolyCount;
|
||||
nodetestcount += bhit.TestedNodeCount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//if (hit)
|
||||
//{
|
||||
// hitpos = ray.Position + ray.Direction * itemhitdist;
|
||||
@ -1542,11 +1189,6 @@ namespace CodeWalker.World
|
||||
res.TestedNodeCount = nodetestcount;
|
||||
res.TestedPolyCount = polytestcount;
|
||||
res.TestComplete = testcomplete;
|
||||
res.Hit = hit;
|
||||
res.HitDist = 0;// itemhitdist;
|
||||
res.HitPolygon = hitpoly;
|
||||
res.Position = hitpos;
|
||||
res.Normal = hitnorm;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
32
WorldForm.cs
32
WorldForm.cs
@ -154,7 +154,7 @@ namespace CodeWalker
|
||||
MapSelection PrevMouseHit = new MapSelection();
|
||||
|
||||
bool MouseRayCollisionEnabled = true;
|
||||
bool MouseRayCollisionVisible = true;
|
||||
bool MouseRayCollisionVisible = false;
|
||||
SpaceRayIntersectResult MouseRayCollision = new SpaceRayIntersectResult();
|
||||
|
||||
string SelectionModeStr = "Entity";
|
||||
@ -628,7 +628,7 @@ namespace CodeWalker
|
||||
Vector3 move = lftxy * movecontrol.X + fwdxy * movecontrol.Y;
|
||||
Vector2 movexy = new Vector2(move.X, move.Y);
|
||||
|
||||
movexy *= (1.0f + (Input.xblt * 15.0f)); //boost with left trigger
|
||||
movexy *= (1.0f + (Math.Min(Math.Max(Input.xblt, 0.0f), 1.0f) * 15.0f)); //boost with left trigger
|
||||
|
||||
pedEntity.ControlMovement = movexy;
|
||||
pedEntity.ControlJump = Input.kbjump || Input.ControllerButtonPressed(GamepadButtonFlags.X);
|
||||
@ -1281,13 +1281,15 @@ namespace CodeWalker
|
||||
const uint caqu = 4294967040;// (uint)new Color4(0.0f, 1.0f, 1.0f, 1.0f).ToRgba();
|
||||
//const uint cyel = 4278255615;//
|
||||
|
||||
if (MouseRayCollisionEnabled && MouseRayCollisionVisible)
|
||||
if (ControlBrushEnabled && MouseRayCollision.Hit)
|
||||
{
|
||||
if (MouseRayCollision.Hit)
|
||||
{
|
||||
var arup = GetPerpVec(MouseRayCollision.Normal);
|
||||
Renderer.RenderBrushRadiusOutline(MouseRayCollision.Position, MouseRayCollision.Normal, arup, ProjectForm.GetInstanceBrushRadius(), cgrn);
|
||||
}
|
||||
var arup = GetPerpVec(MouseRayCollision.Normal);
|
||||
Renderer.RenderBrushRadiusOutline(MouseRayCollision.Position, MouseRayCollision.Normal, arup, ProjectForm.GetInstanceBrushRadius(), cgrn);
|
||||
}
|
||||
if (MouseRayCollisionVisible && MouseRayCollision.Hit)
|
||||
{
|
||||
var arup = GetPerpVec(MouseRayCollision.Normal);
|
||||
Renderer.RenderSelectionArrowOutline(MouseRayCollision.Position, MouseRayCollision.Normal, arup, Quaternion.Identity, 1.0f, 0.05f, cgrn);
|
||||
}
|
||||
|
||||
if (!ShowSelectionBounds)
|
||||
@ -2200,13 +2202,21 @@ namespace CodeWalker
|
||||
if (Input.CtrlPressed && ProjectForm != null && ProjectForm.CanPaintInstances())
|
||||
{
|
||||
ControlBrushEnabled = true;
|
||||
MouseRayCollisionEnabled = true;
|
||||
MouseRayCollisionVisible = false;
|
||||
MouseRayCollision = GetSpaceMouseRay();
|
||||
}
|
||||
else if (MouseRayCollisionEnabled)
|
||||
else
|
||||
{
|
||||
ControlBrushEnabled = false;
|
||||
MouseRayCollisionEnabled = false;
|
||||
if (Input.CtrlPressed && MouseRayCollisionEnabled)
|
||||
{
|
||||
MouseRayCollisionVisible = true;
|
||||
MouseRayCollision = GetSpaceMouseRay();
|
||||
}
|
||||
else
|
||||
{
|
||||
MouseRayCollisionVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user