using System.Numerics; using AcDream.Core.Terrain; using DatReaderWriter.DBObjs; namespace AcDream.Core.Meshing; public static class GfxObjMesh { /// /// Walk a GfxObj's polygons and produce one /// per referenced Surface. Polygons are triangulated as fans. /// public static IReadOnlyList Build(GfxObj gfxObj) { // Group output vertices and indices per surface index. var perSurface = new Dictionary Vertices, List Indices, Dictionary<(int pos, int uv), uint> Dedupe)>(); foreach (var kvp in gfxObj.Polygons) { var poly = kvp.Value; if (poly.VertexIds.Count < 3) continue; // degenerate int surfaceIdx = poly.PosSurface; if (surfaceIdx < 0 || surfaceIdx >= gfxObj.Surfaces.Count) continue; // out of range surface if (!perSurface.TryGetValue(surfaceIdx, out var bucket)) { bucket = (new List(), new List(), new Dictionary<(int, int), uint>()); perSurface[surfaceIdx] = bucket; } // Collect output vertex indices for this polygon. var polyOut = new List(poly.VertexIds.Count); bool skipPoly = false; for (int i = 0; i < poly.VertexIds.Count; i++) { int posIdx = poly.VertexIds[i]; int uvIdx = i < poly.PosUVIndices.Count ? poly.PosUVIndices[i] : 0; if (!gfxObj.VertexArray.Vertices.TryGetValue((ushort)posIdx, out var sw)) { skipPoly = true; break; } var texcoord = uvIdx >= 0 && uvIdx < sw.UVs.Count ? new Vector2(sw.UVs[uvIdx].U, sw.UVs[uvIdx].V) : Vector2.Zero; var key = (posIdx, uvIdx); if (!bucket.Dedupe.TryGetValue(key, out var outIdx)) { outIdx = (uint)bucket.Vertices.Count; bucket.Vertices.Add(new Vertex(sw.Origin, sw.Normal, texcoord)); bucket.Dedupe[key] = outIdx; } polyOut.Add(outIdx); } if (skipPoly || polyOut.Count < 3) continue; // Fan triangulation: (v0, v1, v2), (v0, v2, v3), ... for (int i = 1; i < polyOut.Count - 1; i++) { bucket.Indices.Add(polyOut[0]); bucket.Indices.Add(polyOut[i]); bucket.Indices.Add(polyOut[i + 1]); } } // Emit one sub-mesh per surface. var result = new List(perSurface.Count); foreach (var kvp in perSurface) { var surfaceId = (uint)gfxObj.Surfaces[kvp.Key]; result.Add(new GfxObjSubMesh( SurfaceId: surfaceId, Vertices: kvp.Value.Vertices.ToArray(), Indices: kvp.Value.Indices.ToArray())); } return result; } }