143 lines
4.3 KiB
C#
143 lines
4.3 KiB
C#
using Nerd_STF.Mathematics;
|
|
using Nerd_STF.Mathematics.Algebra;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
|
|
namespace BasicProjectionRenderer;
|
|
|
|
public record class Line(int IndA, int IndB) : IEquatable<Line>
|
|
{
|
|
public virtual bool Equals(Line? other)
|
|
{
|
|
if (other is null) return false;
|
|
return (IndA == other.IndA && IndB == other.IndB) ||
|
|
(IndA == other.IndB && IndB == other.IndA);
|
|
}
|
|
public override int GetHashCode() => base.GetHashCode();
|
|
}
|
|
public record class Face(int IndA, int IndB, int IndC);
|
|
|
|
public class Mesh
|
|
{
|
|
public required Float3[] points;
|
|
public required Line[] lines;
|
|
public required Face[] faces;
|
|
|
|
public Float3 Location { get; set; } = (0, 0, 0);
|
|
public (Angle, Angle, Angle) Rotation
|
|
{
|
|
get => _rotation;
|
|
set
|
|
{
|
|
if (_rotation != value)
|
|
{
|
|
_rotation = value;
|
|
CalculateRotationMatrix();
|
|
}
|
|
}
|
|
}
|
|
public Float3 Scale { get; set; } = (1, 1, 1);
|
|
|
|
private readonly Dictionary<int, Float3> _pointCache = [];
|
|
private (Angle, Angle, Angle) _rotation = (Angle.Zero, Angle.Zero, Angle.Zero);
|
|
private Matrix3x3 _rotMatrix = Matrix3x3.Identity;
|
|
|
|
private void CalculateRotationMatrix()
|
|
{
|
|
double radX = _rotation.Item1.Radians,
|
|
radY = _rotation.Item2.Radians,
|
|
radZ = _rotation.Item3.Radians;
|
|
|
|
(double cosX, double sinX) = (MathE.Cos(radX), MathE.Sin(radX));
|
|
(double cosY, double sinY) = (MathE.Cos(radY), MathE.Sin(radY));
|
|
(double cosZ, double sinZ) = (MathE.Cos(radZ), MathE.Sin(radZ));
|
|
Matrix3x3 rotX = new([
|
|
[ 1, 0, 0 ],
|
|
[ 0, cosX, -sinX ],
|
|
[ 0, sinX, cosX ]
|
|
]);
|
|
Matrix3x3 rotY = new([
|
|
[ cosY, 0, sinY ],
|
|
[ 0, 1, 0 ],
|
|
[ -sinY, 0, cosY ]
|
|
]);
|
|
Matrix3x3 rotZ = new([
|
|
[ cosZ, -sinZ, 0 ],
|
|
[ sinZ, cosZ, 0 ],
|
|
[ 0, 0, 1 ]
|
|
]);
|
|
_rotMatrix = rotX * rotY * rotZ;
|
|
_pointCache.Clear();
|
|
Array.Sort(faces, SortFace);
|
|
}
|
|
|
|
public Float3 GetPoint(int index)
|
|
{
|
|
if (_pointCache.TryGetValue(index, out Float3 cached)) return cached;
|
|
|
|
Float3 p = points[index];
|
|
p = _rotMatrix * p;
|
|
p *= Scale;
|
|
p += Location;
|
|
_pointCache.Add(index, p);
|
|
|
|
return p;
|
|
}
|
|
|
|
private int SortFace(Face f1, Face f2)
|
|
{
|
|
Float3 f1a = GetPoint(f1.IndA), f2a = GetPoint(f2.IndA),
|
|
f1b = GetPoint(f1.IndB), f2b = GetPoint(f2.IndB),
|
|
f1c = GetPoint(f1.IndC), f2c = GetPoint(f2.IndC);
|
|
Float3 avg1 = (f1a + f1b + f1c) / 3,
|
|
avg2 = (f2a + f2b + f2c) / 3;
|
|
return avg1.z.CompareTo(avg2.z);
|
|
}
|
|
|
|
public static Mesh FromObj(Stream data)
|
|
{
|
|
StreamReader reader = new(data, leaveOpen: true);
|
|
string? line;
|
|
List<Float3> points = [];
|
|
List<Line> lines = [];
|
|
List<Face> faces = [];
|
|
while ((line = reader.ReadLine()) is not null)
|
|
{
|
|
line = line.Trim();
|
|
if (line.StartsWith('#')) continue;
|
|
|
|
string[] parts = line.Split(' ');
|
|
switch (parts[0])
|
|
{
|
|
case "v":
|
|
points.Add((double.Parse(parts[1]), double.Parse(parts[2]), double.Parse(parts[3])));
|
|
break;
|
|
|
|
case "f":
|
|
int indA = int.Parse(parts[1]) - 1,
|
|
indB = int.Parse(parts[2]) - 1,
|
|
indC = int.Parse(parts[3]) - 1;
|
|
|
|
/*Line l1 = new(indA, indB), l2 = new(indB, indC), l3 = new(indC, indA);
|
|
if (!lines.Contains(l1)) lines.Add(l1);
|
|
if (!lines.Contains(l2)) lines.Add(l2);
|
|
if (!lines.Contains(l3)) lines.Add(l3);*/
|
|
|
|
faces.Add(new(indA, indB, indC));
|
|
break;
|
|
}
|
|
}
|
|
reader.Close();
|
|
Console.WriteLine($"{points.Count} verts, {lines.Count} edges, {faces.Count} faces");
|
|
return new()
|
|
{
|
|
points = [.. points],
|
|
lines = [.. lines],
|
|
faces = [.. faces]
|
|
};
|
|
}
|
|
|
|
public override int GetHashCode() => points.GetHashCode();
|
|
}
|