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
58d2293358
commit
d5c0bc2477
@ -144,6 +144,9 @@ namespace CodeWalker.GameFiles
|
||||
return n;
|
||||
}
|
||||
|
||||
public Matrix Transform { get; set; } = Matrix.Identity; //when it's the child of a bound composite
|
||||
public Matrix TransformInv { get; set; } = Matrix.Identity;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads the data-block from a stream.
|
||||
@ -237,7 +240,7 @@ namespace CodeWalker.GameFiles
|
||||
{
|
||||
return new SpaceSphereIntersectResult();
|
||||
}
|
||||
public virtual SpaceRayIntersectResult RayIntersect(ref Ray ray, float maxdist = float.MaxValue, float itemhitdist = float.MaxValue)
|
||||
public virtual SpaceRayIntersectResult RayIntersect(ref Ray ray, float maxdist = float.MaxValue)
|
||||
{
|
||||
return new SpaceRayIntersectResult();
|
||||
}
|
||||
@ -247,11 +250,33 @@ namespace CodeWalker.GameFiles
|
||||
{
|
||||
public override SpaceSphereIntersectResult SphereIntersect(ref BoundingSphere sph)
|
||||
{
|
||||
return base.SphereIntersect(ref sph);
|
||||
var res = new SpaceSphereIntersectResult();
|
||||
var bsph = new BoundingSphere();
|
||||
bsph.Center = Center;
|
||||
bsph.Radius = BoundingSphereRadius;
|
||||
if (sph.Intersects(ref bsph))
|
||||
{
|
||||
res.Hit = true;
|
||||
res.Normal = Vector3.Normalize(sph.Center - Center);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
public override SpaceRayIntersectResult RayIntersect(ref Ray ray, float maxdist = float.MaxValue, float itemhitdist = float.MaxValue)
|
||||
public override SpaceRayIntersectResult RayIntersect(ref Ray ray, float maxdist = float.MaxValue)
|
||||
{
|
||||
return base.RayIntersect(ref ray, maxdist, itemhitdist);
|
||||
var res = new SpaceRayIntersectResult();
|
||||
var bsph = new BoundingSphere();
|
||||
bsph.Center = Center;
|
||||
bsph.Radius = BoundingSphereRadius;
|
||||
float testdist;
|
||||
if (ray.Intersects(ref bsph, out testdist) && (testdist < maxdist))
|
||||
{
|
||||
res.Hit = true;
|
||||
res.HitDist = testdist;
|
||||
res.Position = ray.Position + ray.Direction * testdist;
|
||||
res.Normal = Vector3.Normalize(res.Position - Center);
|
||||
res.Material.Type = MaterialIndex;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
[TC(typeof(EXP))] public class BoundCapsule : Bounds
|
||||
@ -267,9 +292,6 @@ namespace CodeWalker.GameFiles
|
||||
public uint Unknown_78h { get; set; } // 0x00000000
|
||||
public uint Unknown_7Ch { get; set; } // 0x00000000
|
||||
|
||||
/// <summary>
|
||||
/// Reads the data-block from a stream.
|
||||
/// </summary>
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
base.Read(reader, parameters);
|
||||
@ -280,10 +302,6 @@ namespace CodeWalker.GameFiles
|
||||
this.Unknown_78h = reader.ReadUInt32();
|
||||
this.Unknown_7Ch = reader.ReadUInt32();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the data-block to a stream.
|
||||
/// </summary>
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
base.Write(writer, parameters);
|
||||
@ -297,22 +315,88 @@ namespace CodeWalker.GameFiles
|
||||
|
||||
public override SpaceSphereIntersectResult SphereIntersect(ref BoundingSphere sph)
|
||||
{
|
||||
return base.SphereIntersect(ref sph);
|
||||
var res = new SpaceSphereIntersectResult();
|
||||
var bcap = new BoundingCapsule();
|
||||
var extent = new Vector3(0, BoundingSphereRadius - Margin, 0);
|
||||
bcap.PointA = Center - extent;
|
||||
bcap.PointB = Center + extent;
|
||||
bcap.Radius = Margin;
|
||||
if (sph.Intersects(ref bcap, out res.Normal))
|
||||
{
|
||||
res.Hit = true;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
public override SpaceRayIntersectResult RayIntersect(ref Ray ray, float maxdist = float.MaxValue, float itemhitdist = float.MaxValue)
|
||||
public override SpaceRayIntersectResult RayIntersect(ref Ray ray, float maxdist = float.MaxValue)
|
||||
{
|
||||
return base.RayIntersect(ref ray, maxdist, itemhitdist);
|
||||
var res = new SpaceRayIntersectResult();
|
||||
var bcap = new BoundingCapsule();
|
||||
var extent = new Vector3(0, BoundingSphereRadius - Margin, 0);
|
||||
bcap.PointA = Center - extent;
|
||||
bcap.PointB = Center + extent;
|
||||
bcap.Radius = Margin;
|
||||
float testdist;
|
||||
if (ray.Intersects(ref bcap, out testdist) && (testdist < maxdist))
|
||||
{
|
||||
res.Hit = true;
|
||||
res.HitDist = testdist;
|
||||
res.Position = ray.Position + ray.Direction * testdist;
|
||||
res.Normal = bcap.Normal(ref res.Position);
|
||||
res.Material.Type = MaterialIndex;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
[TC(typeof(EXP))] public class BoundBox : Bounds
|
||||
{
|
||||
public override SpaceSphereIntersectResult SphereIntersect(ref BoundingSphere sph)
|
||||
{
|
||||
return base.SphereIntersect(ref sph);
|
||||
var res = new SpaceSphereIntersectResult();
|
||||
var bbox = new BoundingBox(BoundingBoxMin, BoundingBoxMax);
|
||||
if (sph.Intersects(ref bbox))
|
||||
{
|
||||
var sphmin = sph.Center - sph.Radius;
|
||||
var sphmax = sph.Center + sph.Radius;
|
||||
var n = Vector3.Zero;
|
||||
float eps = sph.Radius * 0.8f;
|
||||
if (Math.Abs(sphmax.X - BoundingBoxMin.X) < eps) n -= Vector3.UnitX;
|
||||
else if (Math.Abs(sphmin.X - BoundingBoxMax.X) < eps) n += Vector3.UnitX;
|
||||
else if (Math.Abs(sphmax.Y - BoundingBoxMin.Y) < eps) n -= Vector3.UnitY;
|
||||
else if (Math.Abs(sphmin.Y - BoundingBoxMax.Y) < eps) n += Vector3.UnitY;
|
||||
else if (Math.Abs(sphmax.Z - BoundingBoxMin.Z) < eps) n -= Vector3.UnitZ;
|
||||
else if (Math.Abs(sphmin.Z - BoundingBoxMax.Z) < eps) n += Vector3.UnitZ;
|
||||
else
|
||||
{ n = Vector3.UnitZ; } //ray starts inside the box...
|
||||
res.Normal = Vector3.Normalize(n);
|
||||
res.Hit = true;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
public override SpaceRayIntersectResult RayIntersect(ref Ray ray, float maxdist = float.MaxValue, float itemhitdist = float.MaxValue)
|
||||
public override SpaceRayIntersectResult RayIntersect(ref Ray ray, float maxdist = float.MaxValue)
|
||||
{
|
||||
return base.RayIntersect(ref ray, maxdist, itemhitdist);
|
||||
var res = new SpaceRayIntersectResult();
|
||||
var bbox = new BoundingBox(BoundingBoxMin, BoundingBoxMax);
|
||||
float testdist;
|
||||
if (ray.Intersects(ref bbox, out testdist) && (testdist < maxdist))
|
||||
{
|
||||
const float eps = 0.002f;
|
||||
var n = Vector3.Zero;
|
||||
var hpt = ray.Position + ray.Direction * testdist;
|
||||
if (Math.Abs(hpt.X - BoundingBoxMin.X) < eps) n = -Vector3.UnitX;
|
||||
else if (Math.Abs(hpt.X - BoundingBoxMax.X) < eps) n = Vector3.UnitX;
|
||||
else if (Math.Abs(hpt.Y - BoundingBoxMin.Y) < eps) n = -Vector3.UnitY;
|
||||
else if (Math.Abs(hpt.Y - BoundingBoxMax.Y) < eps) n = Vector3.UnitY;
|
||||
else if (Math.Abs(hpt.Z - BoundingBoxMin.Z) < eps) n = -Vector3.UnitZ;
|
||||
else if (Math.Abs(hpt.Z - BoundingBoxMax.Z) < eps) n = Vector3.UnitZ;
|
||||
else
|
||||
{ n = Vector3.UnitZ; } //ray starts inside the box...
|
||||
res.Hit = true;
|
||||
res.HitDist = testdist;
|
||||
res.Position = hpt;
|
||||
res.Normal = n;
|
||||
res.Material.Type = MaterialIndex;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
[TC(typeof(EXP))] public class BoundDisc : Bounds
|
||||
@ -328,9 +412,6 @@ namespace CodeWalker.GameFiles
|
||||
public uint Unknown_78h { get; set; } // 0x00000000
|
||||
public uint Unknown_7Ch { get; set; } // 0x00000000
|
||||
|
||||
/// <summary>
|
||||
/// Reads the data-block from a stream.
|
||||
/// </summary>
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
base.Read(reader, parameters);
|
||||
@ -341,10 +422,6 @@ namespace CodeWalker.GameFiles
|
||||
this.Unknown_78h = reader.ReadUInt32();
|
||||
this.Unknown_7Ch = reader.ReadUInt32();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the data-block to a stream.
|
||||
/// </summary>
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
base.Write(writer, parameters);
|
||||
@ -358,11 +435,37 @@ namespace CodeWalker.GameFiles
|
||||
|
||||
public override SpaceSphereIntersectResult SphereIntersect(ref BoundingSphere sph)
|
||||
{
|
||||
return base.SphereIntersect(ref sph);
|
||||
//as a temporary hack, just use the sphere-sphere intersection //TODO: sphere-disc intersection
|
||||
var res = new SpaceSphereIntersectResult();
|
||||
var bsph = new BoundingSphere();
|
||||
bsph.Center = Center;
|
||||
bsph.Radius = BoundingSphereRadius;
|
||||
if (sph.Intersects(ref bsph))
|
||||
{
|
||||
res.Hit = true;
|
||||
res.Normal = Vector3.Normalize(sph.Center - Center);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
public override SpaceRayIntersectResult RayIntersect(ref Ray ray, float maxdist = float.MaxValue, float itemhitdist = float.MaxValue)
|
||||
public override SpaceRayIntersectResult RayIntersect(ref Ray ray, float maxdist = float.MaxValue)
|
||||
{
|
||||
return base.RayIntersect(ref ray, maxdist, itemhitdist);
|
||||
var res = new SpaceRayIntersectResult();
|
||||
var bcyl = new BoundingCylinder();
|
||||
var size = new Vector3(Margin, 0, 0);
|
||||
bcyl.PointA = Center - size;
|
||||
bcyl.PointB = Center + size;
|
||||
bcyl.Radius = BoundingSphereRadius;
|
||||
Vector3 n;
|
||||
float testdist;
|
||||
if (ray.Intersects(ref bcyl, out testdist, out n) && (testdist < maxdist))
|
||||
{
|
||||
res.Hit = true;
|
||||
res.HitDist = testdist;
|
||||
res.Position = ray.Position + ray.Direction * testdist;
|
||||
res.Normal = n;
|
||||
res.Material.Type = MaterialIndex;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
[TC(typeof(EXP))] public class BoundCylinder : Bounds
|
||||
@ -378,9 +481,6 @@ namespace CodeWalker.GameFiles
|
||||
public uint Unknown_78h { get; set; } // 0x00000000
|
||||
public uint Unknown_7Ch { get; set; } // 0x00000000
|
||||
|
||||
/// <summary>
|
||||
/// Reads the data-block from a stream.
|
||||
/// </summary>
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
base.Read(reader, parameters);
|
||||
@ -391,10 +491,6 @@ namespace CodeWalker.GameFiles
|
||||
this.Unknown_78h = reader.ReadUInt32();
|
||||
this.Unknown_7Ch = reader.ReadUInt32();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the data-block to a stream.
|
||||
/// </summary>
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
base.Write(writer, parameters);
|
||||
@ -408,11 +504,40 @@ namespace CodeWalker.GameFiles
|
||||
|
||||
public override SpaceSphereIntersectResult SphereIntersect(ref BoundingSphere sph)
|
||||
{
|
||||
return base.SphereIntersect(ref sph);
|
||||
//as a temporary hack, just use the sphere-capsule intersection //TODO: sphere-cylinder intersection
|
||||
var res = new SpaceSphereIntersectResult();
|
||||
var bcap = new BoundingCapsule();
|
||||
var extent = (BoundingBoxMax - BoundingBoxMin).Abs();
|
||||
var size = new Vector3(0, extent.Y * 0.5f, 0);
|
||||
bcap.PointA = Center - size;
|
||||
bcap.PointB = Center + size;
|
||||
bcap.Radius = extent.X * 0.5f;
|
||||
if (sph.Intersects(ref bcap, out res.Normal))
|
||||
{
|
||||
res.Hit = true;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
public override SpaceRayIntersectResult RayIntersect(ref Ray ray, float maxdist = float.MaxValue, float itemhitdist = float.MaxValue)
|
||||
public override SpaceRayIntersectResult RayIntersect(ref Ray ray, float maxdist = float.MaxValue)
|
||||
{
|
||||
return base.RayIntersect(ref ray, maxdist, itemhitdist);
|
||||
var res = new SpaceRayIntersectResult();
|
||||
var bcyl = new BoundingCylinder();
|
||||
var extent = (BoundingBoxMax - BoundingBoxMin).Abs();
|
||||
var size = new Vector3(0, extent.Y * 0.5f, 0);
|
||||
bcyl.PointA = Center - size;
|
||||
bcyl.PointB = Center + size;
|
||||
bcyl.Radius = extent.X * 0.5f;
|
||||
Vector3 n;
|
||||
float testdist;
|
||||
if (ray.Intersects(ref bcyl, out testdist, out n) && (testdist < maxdist))
|
||||
{
|
||||
res.Hit = true;
|
||||
res.HitDist = testdist;
|
||||
res.Position = ray.Position + ray.Direction * testdist;
|
||||
res.Normal = n;
|
||||
res.Material.Type = MaterialIndex;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
[TC(typeof(EXP))] public class BoundGeometry : Bounds
|
||||
@ -758,11 +883,255 @@ namespace CodeWalker.GameFiles
|
||||
|
||||
public override SpaceSphereIntersectResult SphereIntersect(ref BoundingSphere sph)
|
||||
{
|
||||
return base.SphereIntersect(ref sph);
|
||||
var res = new SpaceSphereIntersectResult();
|
||||
var box = new BoundingBox();
|
||||
|
||||
if (Polygons == null)
|
||||
{ return res; }
|
||||
|
||||
box.Minimum = BoundingBoxMin;
|
||||
box.Maximum = BoundingBoxMax;
|
||||
if (!sph.Intersects(ref box))
|
||||
{ return res; }
|
||||
|
||||
SphereIntersectPolygons(ref sph, ref res, 0, Polygons.Length);
|
||||
|
||||
return res;
|
||||
}
|
||||
public override SpaceRayIntersectResult RayIntersect(ref Ray ray, float maxdist = float.MaxValue, float itemhitdist = float.MaxValue)
|
||||
public override SpaceRayIntersectResult RayIntersect(ref Ray ray, float maxdist = float.MaxValue)
|
||||
{
|
||||
return base.RayIntersect(ref ray, maxdist, itemhitdist);
|
||||
var res = new SpaceRayIntersectResult();
|
||||
var box = new BoundingBox();
|
||||
|
||||
if (Polygons == null)
|
||||
{ return res; }
|
||||
|
||||
box.Minimum = BoundingBoxMin;
|
||||
box.Maximum = BoundingBoxMax;
|
||||
float bvhboxhittest;
|
||||
if (!ray.Intersects(ref box, out bvhboxhittest))
|
||||
{ return res; }
|
||||
if (bvhboxhittest > maxdist)
|
||||
{ return res; } //already a closer hit.
|
||||
|
||||
res.HitDist = maxdist;
|
||||
|
||||
RayIntersectPolygons(ref ray, ref res, 0, Polygons.Length);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
protected void SphereIntersectPolygons(ref BoundingSphere sph, ref SpaceSphereIntersectResult res, int startIndex, int endIndex)
|
||||
{
|
||||
var box = new BoundingBox();
|
||||
var tsph = new BoundingSphere();
|
||||
var spht = new BoundingSphere();
|
||||
var sp = sph.Center;
|
||||
var sr = sph.Radius;
|
||||
var cg = CenterGeom;
|
||||
Vector3 p1, p2, p3, p4, a1, a2, a3;
|
||||
Vector3 n1 = Vector3.Zero;
|
||||
|
||||
for (int p = startIndex; p < endIndex; 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++;
|
||||
}
|
||||
}
|
||||
protected void RayIntersectPolygons(ref Ray ray, ref SpaceRayIntersectResult res, int startIndex, int endIndex)
|
||||
{
|
||||
var box = new BoundingBox();
|
||||
var tsph = new BoundingSphere();
|
||||
var rayt = new Ray();
|
||||
var rp = ray.Position;
|
||||
var rd = ray.Direction;
|
||||
var cg = CenterGeom;
|
||||
Vector3 p1, p2, p3, p4, a1, a2, a3;
|
||||
Vector3 n1 = Vector3.Zero;
|
||||
|
||||
for (int p = startIndex; p < endIndex; 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);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (polyhit && (polyhittestdist < res.HitDist))
|
||||
{
|
||||
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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1258,13 +1627,6 @@ namespace CodeWalker.GameFiles
|
||||
{
|
||||
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; }
|
||||
@ -1278,7 +1640,6 @@ namespace CodeWalker.GameFiles
|
||||
|
||||
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];
|
||||
@ -1311,103 +1672,10 @@ namespace CodeWalker.GameFiles
|
||||
{
|
||||
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++;
|
||||
}
|
||||
var lastp = Math.Min(node.PolyId + node.PolyCount, (int)PolygonsCount);
|
||||
|
||||
SphereIntersectPolygons(ref sph, ref res, node.PolyId, lastp);
|
||||
|
||||
}
|
||||
nodeind++;
|
||||
}
|
||||
@ -1418,18 +1686,10 @@ namespace CodeWalker.GameFiles
|
||||
return res;
|
||||
}
|
||||
|
||||
public override SpaceRayIntersectResult RayIntersect(ref Ray ray, float maxdist = float.MaxValue, float itemhitdist = float.MaxValue)
|
||||
public override SpaceRayIntersectResult RayIntersect(ref Ray ray, float maxdist = 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; }
|
||||
@ -1441,14 +1701,13 @@ namespace CodeWalker.GameFiles
|
||||
float bvhboxhittest;
|
||||
if (!ray.Intersects(ref box, out bvhboxhittest))
|
||||
{ return res; }
|
||||
if (bvhboxhittest > itemhitdist)
|
||||
if (bvhboxhittest > maxdist)
|
||||
{ return res; } //already a closer hit.
|
||||
|
||||
res.HitDist = itemhitdist;
|
||||
res.HitDist = maxdist;
|
||||
|
||||
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];
|
||||
@ -1458,8 +1717,6 @@ namespace CodeWalker.GameFiles
|
||||
{ continue; }
|
||||
if (bvhboxhittest > res.HitDist)
|
||||
{ continue; } //already a closer hit.
|
||||
if (bvhboxhittest > maxdist)
|
||||
{ continue; }
|
||||
|
||||
int nodeind = tree.NodeIndex1;
|
||||
int lastind = tree.NodeIndex2;
|
||||
@ -1485,101 +1742,10 @@ namespace CodeWalker.GameFiles
|
||||
{
|
||||
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;
|
||||
var lastp = Math.Min(node.PolyId + node.PolyCount, (int)PolygonsCount);
|
||||
|
||||
RayIntersectPolygons(ref ray, ref res, node.PolyId, lastp);
|
||||
|
||||
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++;
|
||||
}
|
||||
@ -1653,14 +1819,6 @@ namespace CodeWalker.GameFiles
|
||||
this.ChildrenCount1
|
||||
);
|
||||
|
||||
if ((Children != null) && (Children.data_items != null))
|
||||
{
|
||||
foreach (var child in Children.data_items)
|
||||
{
|
||||
if (child != null) child.Parent = this;
|
||||
}
|
||||
}
|
||||
|
||||
this.ChildrenTransformation1 = reader.ReadStructsAt<Matrix>(this.ChildrenTransformation1Pointer, this.ChildrenCount1);
|
||||
this.ChildrenTransformation2 = reader.ReadStructsAt<Matrix>(this.ChildrenTransformation2Pointer, this.ChildrenCount1);
|
||||
this.ChildrenBoundingBoxes = reader.ReadStructsAt<AABB_s>(this.ChildrenBoundingBoxesPointer, this.ChildrenCount1);
|
||||
@ -1670,6 +1828,29 @@ namespace CodeWalker.GameFiles
|
||||
this.BVH = reader.ReadBlockAt<BVH>(
|
||||
this.BVHPointer // offset
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
var childTransforms = ChildrenTransformation1 ?? ChildrenTransformation2;
|
||||
if ((Children != null) && (Children.data_items != null))
|
||||
{
|
||||
for (int i = 0; i < Children.data_items.Length; i++)
|
||||
{
|
||||
var child = Children.data_items[i];
|
||||
if (child != null)
|
||||
{
|
||||
child.Parent = this;
|
||||
|
||||
var xform = ((childTransforms != null) && (i < childTransforms.Length)) ? childTransforms[i] : Matrix.Identity;
|
||||
xform.Column4 = new Vector4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
child.Transform = xform;
|
||||
child.TransformInv = Matrix.Invert(xform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -1745,6 +1926,7 @@ namespace CodeWalker.GameFiles
|
||||
public override SpaceSphereIntersectResult SphereIntersect(ref BoundingSphere sph)
|
||||
{
|
||||
var res = new SpaceSphereIntersectResult();
|
||||
var tsph = sph;
|
||||
|
||||
var compchilds = Children?.data_items;
|
||||
if (compchilds == null)
|
||||
@ -1753,49 +1935,45 @@ namespace CodeWalker.GameFiles
|
||||
for (int i = 0; i < compchilds.Length; i++)
|
||||
{
|
||||
var c = compchilds[i];
|
||||
if (c == null) continue;
|
||||
|
||||
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;
|
||||
tsph.Center = c.TransformInv.Multiply(sph.Center);
|
||||
|
||||
var chit = c.SphereIntersect(ref tsph);
|
||||
|
||||
chit.Normal = c.Transform.MultiplyRot(chit.Normal);
|
||||
|
||||
res.TryUpdate(ref chit);
|
||||
}
|
||||
|
||||
res.TestComplete = true;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public override SpaceRayIntersectResult RayIntersect(ref Ray ray, float maxdist = float.MaxValue, float itemhitdist = float.MaxValue)
|
||||
public override SpaceRayIntersectResult RayIntersect(ref Ray ray, float maxdist = float.MaxValue)
|
||||
{
|
||||
var res = new SpaceRayIntersectResult();
|
||||
res.HitDist = itemhitdist;
|
||||
res.HitDist = maxdist;
|
||||
|
||||
var tray = ray;
|
||||
|
||||
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 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;
|
||||
tray.Position = c.TransformInv.Multiply(ray.Position);
|
||||
tray.Direction = c.TransformInv.MultiplyRot(ray.Direction);
|
||||
|
||||
var chit = c.RayIntersect(ref tray, res.HitDist);
|
||||
|
||||
chit.Position = c.Transform.Multiply(chit.Position);
|
||||
chit.Normal = c.Transform.MultiplyRot(chit.Normal);
|
||||
|
||||
res.TryUpdate(ref chit);
|
||||
}
|
||||
|
||||
return res;
|
||||
|
@ -157,10 +157,10 @@ namespace CodeWalker
|
||||
}
|
||||
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;
|
||||
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 Vector3.Normalize((pa - h * ba) / c.Radius);
|
||||
}
|
||||
|
||||
|
||||
@ -222,7 +222,7 @@ namespace CodeWalker
|
||||
if (y > 0.0f && y < baba)
|
||||
{
|
||||
dist = t;
|
||||
norm = (oc + t * r.Direction - ba * y / baba) / cylinder.Radius;
|
||||
norm = Vector3.Normalize((oc + t * r.Direction - ba * y / baba) / cylinder.Radius);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -231,7 +231,7 @@ namespace CodeWalker
|
||||
if (Math.Abs(k1 + k2 * t) < h)
|
||||
{
|
||||
dist = t;
|
||||
norm = ba * Math.Sign(y) / baba;
|
||||
norm = Vector3.Normalize(ba * Math.Sign(y) / baba);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,8 @@ namespace CodeWalker.World
|
||||
public bool EnableCollisions;
|
||||
public bool Enabled;
|
||||
|
||||
public float Lifetime;
|
||||
public float Age;
|
||||
|
||||
//public CollisionShape ..
|
||||
|
||||
|
@ -44,7 +44,11 @@ namespace CodeWalker.World
|
||||
|
||||
public SpaceNavGrid NavGrid;
|
||||
|
||||
public List<SpaceEntityCollision> Collisions = new List<SpaceEntityCollision>();
|
||||
public List<SpaceEntityCollision> Collisions = new List<SpaceEntityCollision>();
|
||||
private bool[] CollisionLayers = new[] { true, false, false };
|
||||
|
||||
private int CurrentHour;
|
||||
private MetaHash CurrentWeather;
|
||||
|
||||
|
||||
public void Init(GameFileCache gameFileCache, Action<string> updateStatus)
|
||||
@ -749,6 +753,7 @@ namespace CodeWalker.World
|
||||
|
||||
e.Velocity += dvgrav; //apply gravity
|
||||
e.Momentum = e.Velocity * e.Mass;
|
||||
e.Age += elapsed;
|
||||
|
||||
e.PreUpdate(elapsed);
|
||||
|
||||
@ -831,6 +836,11 @@ namespace CodeWalker.World
|
||||
}
|
||||
|
||||
|
||||
if ((e.Lifetime > 0.0f) && (e.Age > e.Lifetime))
|
||||
{
|
||||
TemporaryEntities.Remove(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -856,7 +866,7 @@ namespace CodeWalker.World
|
||||
|
||||
BoundingSphere sph = new BoundingSphere(r.HitPos + e.Center, e.Radius);
|
||||
|
||||
r.SphereHit = SphereIntersect(sph);
|
||||
r.SphereHit = SphereIntersect(sph, CollisionLayers);
|
||||
|
||||
if (!r.SphereHit.Hit)
|
||||
{
|
||||
@ -876,7 +886,7 @@ namespace CodeWalker.World
|
||||
sph.Center = r.HitPos + e.Center; //this really shouldn't happen... but just in case of glancing hit..
|
||||
}
|
||||
|
||||
r.SphereHit = SphereIntersect(sph); //this really should be a hit!
|
||||
r.SphereHit = SphereIntersect(sph, CollisionLayers); //this really should be a hit!
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -891,7 +901,7 @@ namespace CodeWalker.World
|
||||
while (curiter < maxiter) //iterate to find a closer hit time... improve this!
|
||||
{
|
||||
sph.Center = sphpos + disp * curt;
|
||||
var tcollres = SphereIntersect(sph);
|
||||
var tcollres = SphereIntersect(sph, CollisionLayers);
|
||||
if (tcollres.Hit)
|
||||
{
|
||||
r.HitT = curt;
|
||||
@ -977,6 +987,8 @@ namespace CodeWalker.World
|
||||
{
|
||||
if (!Inited) return;
|
||||
if (MapDataStore == null) return;
|
||||
CurrentHour = hour;
|
||||
CurrentWeather = weather;
|
||||
var items = MapDataStore.GetItems(ref cam.Position);
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
@ -1067,29 +1079,24 @@ namespace CodeWalker.World
|
||||
var res = new SpaceRayIntersectResult();
|
||||
if (GameFileCache == null) return res;
|
||||
bool testcomplete = true;
|
||||
res.HitDist = float.MaxValue;
|
||||
res.HitDist = maxdist;
|
||||
var box = new BoundingBox();
|
||||
float boxhitdisttest;
|
||||
|
||||
if (BoundsStore == null) return res;
|
||||
var boundslist = BoundsStore.GetItems(ref ray);
|
||||
if ((BoundsStore == null) || (MapDataStore == null)) return res;
|
||||
|
||||
foreach (var bound in boundslist)
|
||||
var boundslist = BoundsStore.GetItems(ref ray, layers);
|
||||
var mapdatalist = MapDataStore.GetItems(ref ray);
|
||||
|
||||
for (int i = 0; i < boundslist.Count; i++)
|
||||
{
|
||||
uint l = bound.Layer;
|
||||
if ((layers != null) && (l < 3))
|
||||
{
|
||||
if (!layers[l]) continue;
|
||||
}
|
||||
|
||||
var bound = boundslist[i];
|
||||
box.Minimum = bound.Min;
|
||||
box.Maximum = bound.Max;
|
||||
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)
|
||||
@ -1098,33 +1105,68 @@ namespace CodeWalker.World
|
||||
{ testcomplete = false; continue; } //ybn not loaded yet...
|
||||
|
||||
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 > res.HitDist)
|
||||
//{ continue; } //already a closer hit.
|
||||
//if (itemboxhitdisttest > maxdist)
|
||||
//{ continue; }
|
||||
|
||||
var bhit = b.RayIntersect(ref ray, maxdist, res.HitDist);
|
||||
if (bhit.Hit)
|
||||
{
|
||||
res.Hit = true;
|
||||
res.HitDist = bhit.HitDist;
|
||||
res.HitPolygon = bhit.HitPolygon;
|
||||
res.Material = bhit.Material;
|
||||
res.Normal = bhit.Normal;
|
||||
}
|
||||
|
||||
res.TestedNodeCount += bhit.TestedNodeCount;
|
||||
res.TestedPolyCount += bhit.TestedPolyCount;
|
||||
|
||||
var bhit = b.RayIntersect(ref ray, res.HitDist);
|
||||
res.TryUpdate(ref bhit);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < mapdatalist.Count; i++)
|
||||
{
|
||||
var mapdata = mapdatalist[i];
|
||||
if ((mapdata.ContentFlags & 1) == 0)
|
||||
{ continue; } //only test HD ymaps
|
||||
|
||||
box.Minimum = mapdata.entitiesExtentsMin;
|
||||
box.Maximum = mapdata.entitiesExtentsMax;
|
||||
if (ray.Intersects(ref box, out boxhitdisttest))
|
||||
{
|
||||
if (boxhitdisttest > res.HitDist)
|
||||
{ continue; } //already a closer hit
|
||||
|
||||
var hash = mapdata.Name;
|
||||
var ymap = (hash > 0) ? GameFileCache.GetYmap(hash) : null;
|
||||
if ((ymap != null) && (ymap.Loaded) && (ymap.AllEntities != null))
|
||||
{
|
||||
if (!IsYmapAvailable(hash, CurrentHour, CurrentWeather))
|
||||
{ continue; }
|
||||
|
||||
for (int e = 0; e < ymap.AllEntities.Length; e++)
|
||||
{
|
||||
var ent = ymap.AllEntities[e];
|
||||
|
||||
if (!EntityCollisionsEnabled(ent))
|
||||
{ continue; }
|
||||
|
||||
box.Minimum = ent.BBMin;
|
||||
box.Maximum = ent.BBMax;
|
||||
if (ray.Intersects(ref box, out boxhitdisttest))
|
||||
{
|
||||
if (boxhitdisttest > res.HitDist)
|
||||
{ continue; } //already a closer hit
|
||||
|
||||
if (ent.IsMlo)
|
||||
{
|
||||
var ihit = RayIntersectInterior(ref ray, ent, res.HitDist);
|
||||
res.TryUpdate(ref ihit);
|
||||
}
|
||||
else
|
||||
{
|
||||
var ehit = RayIntersectEntity(ref ray, ent, res.HitDist);
|
||||
res.TryUpdate(ref ehit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((ymap != null) && (!ymap.Loaded))
|
||||
{
|
||||
testcomplete = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if (res.Hit)
|
||||
{
|
||||
res.Position = ray.Position + ray.Direction * res.HitDist;
|
||||
@ -1134,24 +1176,163 @@ namespace CodeWalker.World
|
||||
|
||||
return res;
|
||||
}
|
||||
public SpaceRayIntersectResult RayIntersectEntity(ref Ray ray, YmapEntityDef ent, float maxdist = float.MaxValue)
|
||||
{
|
||||
var res = new SpaceRayIntersectResult();
|
||||
res.HitDist = maxdist;
|
||||
|
||||
public SpaceSphereIntersectResult SphereIntersect(BoundingSphere sph)
|
||||
var drawable = GameFileCache.TryGetDrawable(ent.Archetype);
|
||||
if (drawable != null)
|
||||
{
|
||||
var eori = ent.Orientation;
|
||||
var eorinv = Quaternion.Invert(ent.Orientation);
|
||||
var eray = new Ray();
|
||||
eray.Position = eorinv.Multiply(ray.Position - ent.Position);
|
||||
eray.Direction = eorinv.Multiply(ray.Direction);
|
||||
|
||||
if ((drawable is Drawable sdrawable) && (sdrawable.Bound != null))
|
||||
{
|
||||
var dhit = sdrawable.Bound.RayIntersect(ref eray, res.HitDist);
|
||||
if (dhit.Hit)
|
||||
{
|
||||
dhit.Position = eori.Multiply(dhit.Position) + ent.Position;
|
||||
dhit.Normal = eori.Multiply(dhit.Normal);
|
||||
}
|
||||
res.TryUpdate(ref dhit);
|
||||
}
|
||||
else if (drawable is FragDrawable fdrawable)
|
||||
{
|
||||
if (fdrawable.Bound != null)
|
||||
{
|
||||
var fhit = fdrawable.Bound.RayIntersect(ref eray, res.HitDist);
|
||||
if (fhit.Hit)
|
||||
{
|
||||
fhit.Position = eori.Multiply(fhit.Position) + ent.Position;
|
||||
fhit.Normal = eori.Multiply(fhit.Normal);
|
||||
}
|
||||
res.TryUpdate(ref fhit);
|
||||
}
|
||||
var fbound = fdrawable.OwnerFragment?.PhysicsLODGroup?.PhysicsLOD1?.Bound;
|
||||
if (fbound != null)
|
||||
{
|
||||
var fhit = fbound.RayIntersect(ref eray, res.HitDist);//TODO: these probably have extra transforms..!
|
||||
if (fhit.Hit)
|
||||
{
|
||||
fhit.Position = eori.Multiply(fhit.Position) + ent.Position;
|
||||
fhit.Normal = eori.Multiply(fhit.Normal);
|
||||
}
|
||||
res.TryUpdate(ref fhit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
public SpaceRayIntersectResult RayIntersectInterior(ref Ray ray, YmapEntityDef mlo, float maxdist = float.MaxValue)
|
||||
{
|
||||
var res = new SpaceRayIntersectResult();
|
||||
res.HitDist = maxdist;
|
||||
|
||||
if (mlo.Archetype == null)
|
||||
{ return res; }
|
||||
|
||||
var iori = mlo.Orientation;
|
||||
var iorinv = Quaternion.Invert(mlo.Orientation);
|
||||
var iray = new Ray();
|
||||
iray.Position = iorinv.Multiply(ray.Position - mlo.Position);
|
||||
iray.Direction = iorinv.Multiply(ray.Direction);
|
||||
|
||||
var hash = mlo.Archetype.Hash;
|
||||
var ybn = GameFileCache.GetYbn(hash);
|
||||
if ((ybn != null) && (ybn.Loaded))
|
||||
{
|
||||
var ihit = ybn.Bounds.RayIntersect(ref iray, res.HitDist);
|
||||
if (ihit.Hit)
|
||||
{
|
||||
ihit.Position = iori.Multiply(ihit.Position) + mlo.Position;
|
||||
ihit.Normal = iori.Multiply(ihit.Normal);
|
||||
}
|
||||
res.TryUpdate(ref ihit);
|
||||
}
|
||||
|
||||
var mlodat = mlo.MloInstance;
|
||||
if (mlodat == null)
|
||||
{ return res; }
|
||||
|
||||
var box = new BoundingBox();
|
||||
float boxhitdisttest;
|
||||
|
||||
if (mlodat.Entities != null)
|
||||
{
|
||||
for (int j = 0; j < mlodat.Entities.Length; j++) //should really improve this by using rooms!
|
||||
{
|
||||
var intent = mlodat.Entities[j];
|
||||
if (intent.Archetype == null) continue; //missing archetype...
|
||||
|
||||
if (!EntityCollisionsEnabled(intent))
|
||||
{ continue; }
|
||||
|
||||
box.Minimum = intent.BBMin;
|
||||
box.Maximum = intent.BBMax;
|
||||
if (ray.Intersects(ref box, out boxhitdisttest))
|
||||
{
|
||||
if (boxhitdisttest > res.HitDist)
|
||||
{ continue; } //already a closer hit
|
||||
|
||||
var ehit = RayIntersectEntity(ref ray, intent, res.HitDist);
|
||||
res.TryUpdate(ref ehit);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mlodat.EntitySets != null)
|
||||
{
|
||||
foreach (var entitysets in mlodat.EntitySets)
|
||||
{
|
||||
var entityset = entitysets.Value;
|
||||
if (!entityset.Visible) continue;
|
||||
var entities = entityset.Entities;
|
||||
for (int i = 0; i < entities.Count; i++) //should really improve this by using rooms!
|
||||
{
|
||||
var intent = entities[i];
|
||||
if (intent.Archetype == null) continue; //missing archetype...
|
||||
|
||||
if (!EntityCollisionsEnabled(intent))
|
||||
{ continue; }
|
||||
|
||||
box.Minimum = intent.BBMin;
|
||||
box.Maximum = intent.BBMax;
|
||||
if (ray.Intersects(ref box, out boxhitdisttest))
|
||||
{
|
||||
if (boxhitdisttest > res.HitDist)
|
||||
{ continue; } //already a closer hit
|
||||
|
||||
var ehit = RayIntersectEntity(ref ray, intent, res.HitDist);
|
||||
res.TryUpdate(ref ehit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public SpaceSphereIntersectResult SphereIntersect(BoundingSphere sph, bool[] layers = null)
|
||||
{
|
||||
var res = new SpaceSphereIntersectResult();
|
||||
if (GameFileCache == null) return res;
|
||||
int polytestcount = 0;
|
||||
int nodetestcount = 0;
|
||||
bool testcomplete = true;
|
||||
Vector3 sphmin = sph.Center - sph.Radius;
|
||||
Vector3 sphmax = sph.Center + sph.Radius;
|
||||
var box = new BoundingBox();
|
||||
|
||||
if (BoundsStore == null) return res;
|
||||
var boundslist = BoundsStore.GetItems(ref sphmin, ref sphmax);
|
||||
if ((BoundsStore == null) || (MapDataStore == null)) return res;
|
||||
|
||||
var boundslist = BoundsStore.GetItems(ref sphmin, ref sphmax, layers);
|
||||
var mapdatalist = MapDataStore.GetItems(ref sphmin, ref sphmax);
|
||||
|
||||
foreach (var bound in boundslist)
|
||||
for (int i = 0; i < boundslist.Count; i++)
|
||||
{
|
||||
var bound = boundslist[i];
|
||||
box.Minimum = bound.Min;
|
||||
box.Maximum = bound.Max;
|
||||
if (sph.Intersects(ref box))
|
||||
@ -1163,35 +1344,208 @@ namespace CodeWalker.World
|
||||
{ testcomplete = false; continue; } //ybn not loaded yet...
|
||||
|
||||
var b = ybn.Bounds;
|
||||
|
||||
//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)
|
||||
{
|
||||
res.Hit = true;
|
||||
res.HitPolygon = bhit.HitPolygon;
|
||||
res.Normal = bhit.Normal;
|
||||
}
|
||||
polytestcount += bhit.TestedPolyCount;
|
||||
nodetestcount += bhit.TestedNodeCount;
|
||||
res.TryUpdate(ref bhit);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < mapdatalist.Count; i++)
|
||||
{
|
||||
var mapdata = mapdatalist[i];
|
||||
if ((mapdata.ContentFlags & 1) == 0)
|
||||
{ continue; } //only test HD ymaps
|
||||
|
||||
box.Minimum = mapdata.entitiesExtentsMin;
|
||||
box.Maximum = mapdata.entitiesExtentsMax;
|
||||
if (sph.Intersects(ref box))
|
||||
{
|
||||
var hash = mapdata.Name;
|
||||
var ymap = (hash > 0) ? GameFileCache.GetYmap(hash) : null;
|
||||
if ((ymap != null) && (ymap.Loaded) && (ymap.AllEntities != null))
|
||||
{
|
||||
if (!IsYmapAvailable(hash, CurrentHour, CurrentWeather))
|
||||
{ continue; }
|
||||
|
||||
for (int e = 0; e < ymap.AllEntities.Length; e++)
|
||||
{
|
||||
var ent = ymap.AllEntities[e];
|
||||
|
||||
if (!EntityCollisionsEnabled(ent))
|
||||
{ continue; }
|
||||
|
||||
box.Minimum = ent.BBMin;
|
||||
box.Maximum = ent.BBMax;
|
||||
if (sph.Intersects(ref box))
|
||||
{
|
||||
if (ent.IsMlo)
|
||||
{
|
||||
var ihit = SphereIntersectInterior(ref sph, ent);
|
||||
res.TryUpdate(ref ihit);
|
||||
}
|
||||
else
|
||||
{
|
||||
var ehit = SphereIntersectEntity(ref sph, ent);
|
||||
res.TryUpdate(ref ehit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((ymap != null) && (!ymap.Loaded))
|
||||
{
|
||||
testcomplete = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//if (hit)
|
||||
//{
|
||||
// hitpos = ray.Position + ray.Direction * itemhitdist;
|
||||
//}
|
||||
|
||||
res.TestedNodeCount = nodetestcount;
|
||||
res.TestedPolyCount = polytestcount;
|
||||
res.TestComplete = testcomplete;
|
||||
|
||||
return res;
|
||||
}
|
||||
public SpaceSphereIntersectResult SphereIntersectEntity(ref BoundingSphere sph, YmapEntityDef ent)
|
||||
{
|
||||
var res = new SpaceSphereIntersectResult();
|
||||
|
||||
var drawable = GameFileCache.TryGetDrawable(ent.Archetype);
|
||||
if (drawable != null)
|
||||
{
|
||||
var eori = ent.Orientation;
|
||||
var eorinv = Quaternion.Invert(ent.Orientation);
|
||||
var esph = sph;
|
||||
esph.Center = eorinv.Multiply(sph.Center - ent.Position);
|
||||
|
||||
if ((drawable is Drawable sdrawable) && (sdrawable.Bound != null))
|
||||
{
|
||||
var dhit = sdrawable.Bound.SphereIntersect(ref esph);
|
||||
if (dhit.Hit)
|
||||
{
|
||||
dhit.Position = eori.Multiply(dhit.Position) + ent.Position;
|
||||
dhit.Normal = eori.Multiply(dhit.Normal);
|
||||
}
|
||||
res.TryUpdate(ref dhit);
|
||||
}
|
||||
else if (drawable is FragDrawable fdrawable)
|
||||
{
|
||||
if (fdrawable.Bound != null)
|
||||
{
|
||||
var fhit = fdrawable.Bound.SphereIntersect(ref esph);
|
||||
if (fhit.Hit)
|
||||
{
|
||||
fhit.Position = eori.Multiply(fhit.Position) + ent.Position;
|
||||
fhit.Normal = eori.Multiply(fhit.Normal);
|
||||
}
|
||||
res.TryUpdate(ref fhit);
|
||||
}
|
||||
var fbound = fdrawable.OwnerFragment?.PhysicsLODGroup?.PhysicsLOD1?.Bound;
|
||||
if (fbound != null)
|
||||
{
|
||||
var fhit = fbound.SphereIntersect(ref esph);//TODO: these probably have extra transforms..!
|
||||
if (fhit.Hit)
|
||||
{
|
||||
fhit.Position = eori.Multiply(fhit.Position) + ent.Position;
|
||||
fhit.Normal = eori.Multiply(fhit.Normal);
|
||||
}
|
||||
res.TryUpdate(ref fhit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
public SpaceSphereIntersectResult SphereIntersectInterior(ref BoundingSphere sph, YmapEntityDef mlo)
|
||||
{
|
||||
var res = new SpaceSphereIntersectResult();
|
||||
|
||||
if (mlo.Archetype == null)
|
||||
{ return res; }
|
||||
|
||||
var iori = mlo.Orientation;
|
||||
var iorinv = Quaternion.Invert(mlo.Orientation);
|
||||
var isph = sph;
|
||||
isph.Center = iorinv.Multiply(sph.Center - mlo.Position);
|
||||
|
||||
var hash = mlo.Archetype.Hash;
|
||||
var ybn = GameFileCache.GetYbn(hash);
|
||||
if ((ybn != null) && (ybn.Loaded))
|
||||
{
|
||||
var ihit = ybn.Bounds.SphereIntersect(ref isph);
|
||||
if (ihit.Hit)
|
||||
{
|
||||
ihit.Position = iori.Multiply(ihit.Position) + mlo.Position;
|
||||
ihit.Normal = iori.Multiply(ihit.Normal);
|
||||
}
|
||||
res.TryUpdate(ref ihit);
|
||||
}
|
||||
|
||||
var mlodat = mlo.MloInstance;
|
||||
if (mlodat == null)
|
||||
{ return res; }
|
||||
|
||||
var box = new BoundingBox();
|
||||
|
||||
if (mlodat.Entities != null)
|
||||
{
|
||||
for (int j = 0; j < mlodat.Entities.Length; j++) //should really improve this by using rooms!
|
||||
{
|
||||
var intent = mlodat.Entities[j];
|
||||
if (intent.Archetype == null) continue; //missing archetype...
|
||||
|
||||
if (!EntityCollisionsEnabled(intent))
|
||||
{ continue; }
|
||||
|
||||
box.Minimum = intent.BBMin;
|
||||
box.Maximum = intent.BBMax;
|
||||
if (sph.Intersects(ref box))
|
||||
{
|
||||
var ehit = SphereIntersectEntity(ref sph, intent);
|
||||
res.TryUpdate(ref ehit);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mlodat.EntitySets != null)
|
||||
{
|
||||
foreach (var entitysets in mlodat.EntitySets)
|
||||
{
|
||||
var entityset = entitysets.Value;
|
||||
if (!entityset.Visible) continue;
|
||||
var entities = entityset.Entities;
|
||||
for (int i = 0; i < entities.Count; i++) //should really improve this by using rooms!
|
||||
{
|
||||
var intent = entities[i];
|
||||
if (intent.Archetype == null) continue; //missing archetype...
|
||||
|
||||
if (!EntityCollisionsEnabled(intent))
|
||||
{ continue; }
|
||||
|
||||
box.Minimum = intent.BBMin;
|
||||
box.Maximum = intent.BBMax;
|
||||
if (sph.Intersects(ref box))
|
||||
{
|
||||
var ehit = SphereIntersectEntity(ref sph, intent);
|
||||
res.TryUpdate(ref ehit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private bool EntityCollisionsEnabled(YmapEntityDef ent)
|
||||
{
|
||||
if ((ent._CEntityDef.lodLevel != rage__eLodType.LODTYPES_DEPTH_ORPHANHD) && (ent._CEntityDef.lodLevel != rage__eLodType.LODTYPES_DEPTH_HD))
|
||||
{ return false; } //only test HD entities
|
||||
|
||||
if ((ent._CEntityDef.flags & 4) > 0)
|
||||
{ return false; } //embedded collisions disabled
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1227,7 +1581,7 @@ namespace CodeWalker.World
|
||||
RootNode.TrySplit(SplitThreshold);
|
||||
}
|
||||
|
||||
public List<MapDataStoreNode> GetItems(ref Vector3 p)
|
||||
public List<MapDataStoreNode> GetItems(ref Vector3 p) //get items at a point, using the streaming extents
|
||||
{
|
||||
VisibleItems.Clear();
|
||||
|
||||
@ -1236,6 +1590,28 @@ namespace CodeWalker.World
|
||||
RootNode.GetItems(ref p, VisibleItems);
|
||||
}
|
||||
|
||||
return VisibleItems;
|
||||
}
|
||||
public List<MapDataStoreNode> GetItems(ref Vector3 min, ref Vector3 max) //get items intersecting a box, using the entities extents
|
||||
{
|
||||
VisibleItems.Clear();
|
||||
|
||||
if (RootNode != null)
|
||||
{
|
||||
RootNode.GetItems(ref min, ref max, VisibleItems);
|
||||
}
|
||||
|
||||
return VisibleItems;
|
||||
}
|
||||
public List<MapDataStoreNode> GetItems(ref Ray ray) //get items intersecting a ray, using the entities extents
|
||||
{
|
||||
VisibleItems.Clear();
|
||||
|
||||
if (RootNode != null)
|
||||
{
|
||||
RootNode.GetItems(ref ray, VisibleItems);
|
||||
}
|
||||
|
||||
return VisibleItems;
|
||||
}
|
||||
}
|
||||
@ -1312,7 +1688,7 @@ namespace CodeWalker.World
|
||||
Items = newItems;
|
||||
}
|
||||
|
||||
public void GetItems(ref Vector3 p, List<MapDataStoreNode> items)
|
||||
public void GetItems(ref Vector3 p, List<MapDataStoreNode> items) //get items at a point, using the streaming extents
|
||||
{
|
||||
if ((p.X >= BBMin.X) && (p.X <= BBMax.X) && (p.Y >= BBMin.Y) && (p.Y <= BBMax.Y))
|
||||
{
|
||||
@ -1342,6 +1718,67 @@ namespace CodeWalker.World
|
||||
}
|
||||
}
|
||||
}
|
||||
public void GetItems(ref Vector3 min, ref Vector3 max, List<MapDataStoreNode> items) //get items intersecting a box, using the entities extents
|
||||
{
|
||||
if ((max.X >= BBMin.X) && (min.X <= BBMax.X) && (max.Y >= BBMin.Y) && (min.Y <= BBMax.Y))
|
||||
{
|
||||
if (Items != null)
|
||||
{
|
||||
for (int i = 0; i < Items.Count; i++)
|
||||
{
|
||||
var item = Items[i];
|
||||
var imin = item.entitiesExtentsMin;
|
||||
var imax = item.entitiesExtentsMax;
|
||||
if ((max.X >= imin.X) && (min.X <= imax.X) && (max.Y >= imin.Y) && (min.Y <= imax.Y))
|
||||
{
|
||||
items.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Children != null)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
var c = Children[i];
|
||||
if (c != null)
|
||||
{
|
||||
c.GetItems(ref min, ref max, items);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public void GetItems(ref Ray ray, List<MapDataStoreNode> items) //get items intersecting a ray, using the entities extents
|
||||
{
|
||||
var bb = new BoundingBox(BBMin, BBMax);
|
||||
if (ray.Intersects(ref bb))
|
||||
{
|
||||
if (Items != null)
|
||||
{
|
||||
for (int i = 0; i < Items.Count; i++)
|
||||
{
|
||||
var item = Items[i];
|
||||
bb.Minimum = item.entitiesExtentsMin;
|
||||
bb.Maximum = item.entitiesExtentsMax;
|
||||
if (ray.Intersects(ref bb))
|
||||
{
|
||||
items.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Children != null)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
var c = Children[i];
|
||||
if (c != null)
|
||||
{
|
||||
c.GetItems(ref ray, items);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1708,6 +2145,21 @@ namespace CodeWalker.World
|
||||
public int TestedPolyCount;
|
||||
public bool TestComplete;
|
||||
public BoundMaterial_s Material;
|
||||
|
||||
public void TryUpdate(ref SpaceRayIntersectResult r)
|
||||
{
|
||||
if (r.Hit)
|
||||
{
|
||||
Hit = true;
|
||||
HitDist = r.HitDist;
|
||||
HitPolygon = r.HitPolygon;
|
||||
Material = r.Material;
|
||||
Position = r.Position;
|
||||
Normal = r.Normal;
|
||||
}
|
||||
TestedNodeCount += r.TestedNodeCount;
|
||||
TestedPolyCount += r.TestedPolyCount;
|
||||
}
|
||||
}
|
||||
public struct SpaceSphereIntersectResult
|
||||
{
|
||||
@ -1719,6 +2171,18 @@ namespace CodeWalker.World
|
||||
public int TestedNodeCount;
|
||||
public int TestedPolyCount;
|
||||
public bool TestComplete;
|
||||
|
||||
public void TryUpdate(ref SpaceSphereIntersectResult r)
|
||||
{
|
||||
if (r.Hit)
|
||||
{
|
||||
Hit = true;
|
||||
HitPolygon = r.HitPolygon;
|
||||
Normal = r.Normal;
|
||||
}
|
||||
TestedPolyCount += r.TestedPolyCount;
|
||||
TestedNodeCount += r.TestedNodeCount;
|
||||
}
|
||||
}
|
||||
|
||||
public struct SpaceEntityCollision
|
||||
|
@ -1899,13 +1899,12 @@ namespace CodeWalker.Rendering
|
||||
}
|
||||
|
||||
RenderableBoundGeometry[] geoms = new RenderableBoundGeometry[bound.Children.data_items.Length];
|
||||
var childTransforms = bound.ChildrenTransformation1 ?? bound.ChildrenTransformation2;
|
||||
long dsize = 0;
|
||||
for (int i = 0; i < bound.Children.data_items.Length; i++)
|
||||
{
|
||||
var rgeom = new RenderableBoundGeometry(this);
|
||||
var child = bound.Children.data_items[i];
|
||||
var xform = ((childTransforms != null) && (i < childTransforms.Length)) ? childTransforms[i] : Matrix.Identity; xform.Column4 = new Vector4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
var xform = (child != null) ? child.Transform : Matrix.Identity;
|
||||
if (child is BoundGeometry bgeom)
|
||||
{
|
||||
rgeom.Init(bgeom);
|
||||
|
@ -2804,22 +2804,25 @@ namespace CodeWalker.Rendering
|
||||
|
||||
if ((rendercollisionmeshes || (SelectionMode == MapSelectionMode.Collision)) && rendercollisionmeshlayerdrawable)
|
||||
{
|
||||
Drawable sdrawable = rndbl.Key as Drawable;
|
||||
if ((sdrawable != null) && (sdrawable.Bound != null))
|
||||
if ((entity == null) || ((entity._CEntityDef.flags & 4) == 0)) //skip if entity embedded collisions disabled
|
||||
{
|
||||
RenderCollisionMesh(sdrawable.Bound, entity);
|
||||
}
|
||||
FragDrawable fdrawable = rndbl.Key as FragDrawable;
|
||||
if (fdrawable != null)
|
||||
{
|
||||
if (fdrawable.Bound != null)
|
||||
Drawable sdrawable = rndbl.Key as Drawable;
|
||||
if ((sdrawable != null) && (sdrawable.Bound != null))
|
||||
{
|
||||
RenderCollisionMesh(fdrawable.Bound, entity);
|
||||
RenderCollisionMesh(sdrawable.Bound, entity);
|
||||
}
|
||||
var fbound = fdrawable.OwnerFragment?.PhysicsLODGroup?.PhysicsLOD1?.Bound;
|
||||
if (fbound != null)
|
||||
FragDrawable fdrawable = rndbl.Key as FragDrawable;
|
||||
if (fdrawable != null)
|
||||
{
|
||||
RenderCollisionMesh(fbound, entity);//TODO: these probably have extra transforms..!
|
||||
if (fdrawable.Bound != null)
|
||||
{
|
||||
RenderCollisionMesh(fdrawable.Bound, entity);
|
||||
}
|
||||
var fbound = fdrawable.OwnerFragment?.PhysicsLODGroup?.PhysicsLOD1?.Bound;
|
||||
if (fbound != null)
|
||||
{
|
||||
RenderCollisionMesh(fbound, entity);//TODO: these probably have extra transforms..!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2119,6 +2119,7 @@ namespace CodeWalker
|
||||
e.Radius = arch.BSRadius * 0.7f;
|
||||
e.EnableCollisions = true;
|
||||
e.Enabled = true;
|
||||
e.Lifetime = 20.0f;
|
||||
|
||||
lock (Renderer.RenderSyncRoot)
|
||||
{
|
||||
@ -2238,14 +2239,14 @@ namespace CodeWalker
|
||||
Ray mray = new Ray();
|
||||
mray.Position = camera.MouseRay.Position + camera.Position;
|
||||
mray.Direction = camera.MouseRay.Direction;
|
||||
return space.RayIntersect(mray);
|
||||
return space.RayIntersect(mray, float.MaxValue, collisionmeshlayers);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public SpaceRayIntersectResult Raycast(Ray ray)
|
||||
{
|
||||
return space.RayIntersect(ray);
|
||||
return space.RayIntersect(ray, float.MaxValue, collisionmeshlayers);
|
||||
}
|
||||
|
||||
private void UpdateMouseHitsFromRenderer()
|
||||
|
Loading…
Reference in New Issue
Block a user