mirror of
https://github.com/simtactics/mysimulation.git
synced 2025-03-20 00:41:20 +00:00
- NioTSO client isn't needed because we're using RayLib - Added FreeSO's API server to handle most backend operations
253 lines
8.1 KiB
C#
Executable file
253 lines
8.1 KiB
C#
Executable file
using FSO.SimAntics.Model.Routing;
|
||
using Microsoft.Xna.Framework;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
|
||
namespace FSO.Common.WorldGeometry
|
||
{
|
||
/// <summary>
|
||
/// Projects one mesh onto another mesh on a given axis, potentially with an offset from the surface.
|
||
/// Example use case: Projecting a road onto a terrain mesh
|
||
/// </summary>
|
||
public class MeshProjector
|
||
{
|
||
public MeshProjector(IEnumerable<BaseMeshTriangle> baseMesh, IEnumerable<MeshTriangle> projMesh)
|
||
{
|
||
foreach (var tri in baseMesh) tri.GenBounds();
|
||
foreach (var tri in projMesh) tri.GenBounds();
|
||
|
||
BaseMesh = baseMesh;
|
||
ProjectMesh = projMesh;
|
||
|
||
BaseSet = BaseTriangleSet.RoughBalanced(baseMesh.ToList());
|
||
}
|
||
|
||
IEnumerable<BaseMeshTriangle> BaseMesh;
|
||
BaseTriangleSet BaseSet;
|
||
IEnumerable<MeshTriangle> ProjectMesh;
|
||
|
||
public List<int> Indices;
|
||
public List<MeshPoint> Vertices;
|
||
|
||
public void Project()
|
||
{
|
||
Indices = new List<int>();
|
||
Vertices = new List<MeshPoint>();
|
||
//find list of potential intersect tris for a projtri
|
||
//build clipping edges for projtri
|
||
|
||
foreach (var projTri in ProjectMesh)
|
||
{
|
||
//find candidate baseTris
|
||
var candidates = BaseSet.AllIntersect(projTri);
|
||
foreach (var baseTri in candidates)
|
||
{
|
||
//if (projTri.RoughIntersects(baseTri))
|
||
//{
|
||
ClipTriangles(baseTri, projTri, Vertices, Indices);
|
||
//}
|
||
}
|
||
}
|
||
}
|
||
|
||
private void ClipTriangles(BaseMeshTriangle baseTri, MeshTriangle projTri, List<MeshPoint> outverts, List<int> inds)
|
||
{
|
||
//Sutherland–Hodgman algorithm
|
||
//clip a triangle against another by iteratively clipping each edge of the second one
|
||
|
||
//we want to clip against base tri
|
||
var outputList = new MeshPolygon(projTri);
|
||
var basePlane = new Plane(baseTri.Vertices[0], baseTri.Vertices[1], baseTri.Vertices[2]);
|
||
|
||
for (int i=0; i<3; i++)
|
||
{
|
||
if (outputList.Points.Count == 0) return;
|
||
var inputList = outputList;
|
||
var edge = new ClipEdge(baseTri.Vertices[i], baseTri.Vertices[(i + 1) % 3]);
|
||
outputList = new MeshPolygon();
|
||
var lastPoint = inputList.Points.Last();
|
||
int j = inputList.Points.Count-1;
|
||
foreach (var point in inputList.Points)
|
||
{
|
||
if (!edge.ShouldClip(point.Position))
|
||
{
|
||
if (edge.ShouldClip(lastPoint.Position))
|
||
{
|
||
outputList.Points.Add(edge.IntersectLine(inputList, j));
|
||
}
|
||
//we still need to project the point onto the surface...
|
||
var ray = new Ray(point.Position, new Vector3(0, -1, 0));
|
||
var intersect2 = ray.Intersects(basePlane);
|
||
if (intersect2 == null) {
|
||
ray.Direction *= -1;
|
||
intersect2 = ray.Intersects(basePlane);
|
||
if (intersect2 == null) { }
|
||
intersect2 = -(intersect2 ?? 0f);
|
||
}
|
||
point.Position.Y -= intersect2.Value;
|
||
outputList.Points.Add(point);
|
||
} else
|
||
{
|
||
if (!edge.ShouldClip(lastPoint.Position))
|
||
{
|
||
outputList.Points.Add(edge.IntersectLine(inputList, j));
|
||
}
|
||
}
|
||
j = (j + 1) % inputList.Points.Count;
|
||
lastPoint = point;
|
||
}
|
||
}
|
||
|
||
if (outputList.Points.Count < 3) return; //?
|
||
|
||
outputList.Triangulate(outverts, inds);
|
||
}
|
||
}
|
||
|
||
public class ClipEdge
|
||
{
|
||
Vector3 EdgeVec;
|
||
Vector2 DotVec;
|
||
Vector3 EdgePos;
|
||
Vector2 EdgePos2;
|
||
|
||
public ClipEdge(Vector3 from, Vector3 to)
|
||
{
|
||
//xz
|
||
//we assume the triangle is winding clockwise, so points on the left should be clipped
|
||
|
||
EdgeVec = to - from;
|
||
EdgePos = from;
|
||
EdgePos2 = new Vector2(from.X, from.Z);
|
||
DotVec = new Vector2(-EdgeVec.Z, EdgeVec.X);
|
||
}
|
||
|
||
public bool ShouldClip(Vector3 pos)
|
||
{
|
||
return (Vector2.Dot(DotVec, new Vector2(pos.X, pos.Z) - EdgePos2) < 0);
|
||
}
|
||
|
||
public MeshPoint IntersectLine(MeshPolygon tri, int lineInd)
|
||
{
|
||
var points = tri.Points;
|
||
var lineInd2 = (lineInd + 1) % points.Count;
|
||
var pt1 = tri.Points[lineInd];
|
||
var pt2 = tri.Points[lineInd2];
|
||
|
||
Vector3 a = EdgeVec; //line 1
|
||
Vector3 b = pt2.Position - pt1.Position; //line 2
|
||
Vector3 c = EdgePos - pt1.Position; //vec between starts
|
||
|
||
//percent of line 1 where we intersect with line 2
|
||
float ip = 1 / (-b.X * a.Z + a.X * b.Z); //projection
|
||
float t = (b.X * c.Z - b.Z * c.X) * ip;
|
||
|
||
//percent of line 2 where we intersect line 1
|
||
float ip2 = 1 / (-a.X * b.Z + b.X * a.Z);
|
||
float s = (a.X * (-c.Z) - a.Z * (-c.X)) * ip2;
|
||
|
||
//pos + vec * t = pos2 + vec2 * s
|
||
//vec * t - vec2 * s = pos2 - pos1
|
||
float[] newTC = new float[pt1.TexCoords.Length];
|
||
float ms = 1 - s;
|
||
for (int i=0; i<newTC.Length; i++)
|
||
{
|
||
newTC[i] = pt1.TexCoords[i] * ms + pt2.TexCoords[i] * s;
|
||
}
|
||
|
||
return new MeshPoint(
|
||
//position from the clip triangle (use t)
|
||
EdgePos + EdgeVec * t,
|
||
//texcoords from the two points in the poly (use s)
|
||
newTC
|
||
);
|
||
|
||
}
|
||
}
|
||
|
||
public class BaseMeshTriangle
|
||
{
|
||
public float x1;
|
||
public float y1;
|
||
public float x2;
|
||
public float y2;
|
||
|
||
public Vector3[] Vertices;
|
||
|
||
public void GenBounds()
|
||
{
|
||
x1 = Vertices[0].X;
|
||
y1 = Vertices[0].Z;
|
||
x2 = x1;
|
||
y2 = y1;
|
||
for (int i=1; i<Vertices.Length; i++)
|
||
{
|
||
var v = Vertices[i];
|
||
if (v.X < x1) x1 = v.X;
|
||
if (v.Z < y1) y1 = v.Z;
|
||
if (v.X > x2) x2 = v.X;
|
||
if (v.Z > y2) y2 = v.Z;
|
||
}
|
||
}
|
||
|
||
public bool RoughIntersects(BaseMeshTriangle other)
|
||
{
|
||
return !(x1 > other.x2 || x2 < other.x1 || y1 > other.y2 || y2 < other.y1);
|
||
}
|
||
}
|
||
|
||
public class MeshTriangle : BaseMeshTriangle
|
||
{
|
||
public float[][] TexCoords;
|
||
}
|
||
|
||
public class MeshPoint
|
||
{
|
||
public Vector3 Position;
|
||
public float[] TexCoords;
|
||
|
||
public MeshPoint(Vector3 pos, float[] texCoords)
|
||
{
|
||
Position = pos;
|
||
TexCoords = texCoords;
|
||
}
|
||
|
||
public MeshPoint(Vector3 pos, Vector2 texCoords)
|
||
{
|
||
Position = pos;
|
||
TexCoords = new float[] { texCoords.X, texCoords.Y };
|
||
}
|
||
}
|
||
|
||
public class MeshPolygon {
|
||
public List<MeshPoint> Points;
|
||
|
||
public MeshPolygon()
|
||
{
|
||
Points = new List<MeshPoint>();
|
||
}
|
||
|
||
public MeshPolygon(MeshTriangle tri)
|
||
{
|
||
Points = new List<MeshPoint>();
|
||
for (int i=0; i<3; i++)
|
||
{
|
||
Points.Add(new MeshPoint(tri.Vertices[i], tri.TexCoords[i]));
|
||
}
|
||
}
|
||
|
||
public void Triangulate(List<MeshPoint> outverts, List<int> inds)
|
||
{
|
||
//simple fan triangle fill
|
||
var baseInd = outverts.Count;
|
||
outverts.AddRange(Points);
|
||
|
||
for (int i=2; i<Points.Count; i++)
|
||
{
|
||
inds.Add(baseInd);
|
||
inds.Add(baseInd+i-1);
|
||
inds.Add(baseInd+i);
|
||
}
|
||
}
|
||
}
|
||
}
|