thief-mission-viewer/project/code/TMV/ModelLoader.cs

142 lines
4.9 KiB
C#

using System.Collections.Generic;
using System.IO;
using Godot;
using KeepersCompound.LGS;
namespace KeepersCompound.TMV;
public class ModelLoader
{
private readonly Dictionary<(string, string), MeshInstance3D> _cache = new();
public MeshInstance3D Load(string campaignName, string modelName, bool forceLoad = false)
{
campaignName ??= "";
if (!forceLoad)
{
if (_cache.TryGetValue((campaignName, modelName), out var fmModel))
{
return fmModel?.Duplicate() as MeshInstance3D;
}
else if (_cache.TryGetValue(("", modelName), out var omModel))
{
return omModel?.Duplicate() as MeshInstance3D;
}
}
// We don't care if this is null actually, we'll still cache that it's null lol
var model = Timing.TimeStage("Load Models", () => { return LoadModel(ref campaignName, modelName); });
_cache[(campaignName, modelName)] = model;
return model?.Duplicate() as MeshInstance3D;
}
public static MeshInstance3D LoadModel(ref string campaignName, string modelName)
{
var pathManager = Context.Instance.PathManager;
var (newCampaignName, modelPath) = pathManager.GetResourcePath(ResourceType.Object, campaignName, modelName);
campaignName = newCampaignName;
if (modelPath == null)
{
return null;
}
var modelFile = new ModelFile(modelPath);
if (modelFile == null)
{
GD.Print($"Failed to load model file: {modelPath}");
return null;
}
var materials = new List<StandardMaterial3D>();
foreach (var material in modelFile.Materials)
{
if (material.Type == 0)
{
var resName = Path.GetFileNameWithoutExtension(material.Name);
var (_, path) = pathManager.GetResourcePath(ResourceType.ObjectTexture, campaignName, resName);
if (path == null)
{
path = "user://textures/jorge.png";
GD.Print($"Failed to load model texture: {material.Name}");
}
var mat = new StandardMaterial3D
{
AlbedoTexture = TextureLoader.LoadTexture(path),
Transparency = BaseMaterial3D.TransparencyEnum.AlphaDepthPrePass,
};
var name = material.Name.ToLower();
for (var i = 0; i < 4; i++)
{
if (name.Contains($"replace{i}"))
{
mat.SetMeta($"TxtRepl{i}", true);
}
}
materials.Add(mat);
}
else
{
var b = (material.Handle) & 0xff;
var g = (material.Handle >> 8) & 0xff;
var r = (material.Handle >> 16) & 0xff;
var colour = new Color(r / 255.0f, g / 255.0f, b / 255.0f, 1.0f);
materials.Add(new StandardMaterial3D
{
AlbedoColor = colour
});
}
}
var surfaceDataMap = new Dictionary<int, MeshSurfaceData>();
foreach (var poly in modelFile.Polygons)
{
var vertices = new List<Vector3>();
var normal = modelFile.Normals[poly.Normal].ToGodotVec3();
var uvs = new List<Vector2>();
for (var i = 0; i < poly.VertexCount; i++)
{
var vertex = modelFile.Vertices[poly.VertexIndices[i]];
vertices.Add(vertex.ToGodotVec3());
if (i < poly.UvIndices.Length)
{
var uv = modelFile.Uvs[poly.UvIndices[i]];
uvs.Add(new Vector2(uv.X, uv.Y));
}
else
{
uvs.Add(Vector2.Zero);
}
}
if (!surfaceDataMap.ContainsKey(poly.Data))
{
surfaceDataMap.Add(poly.Data, new MeshSurfaceData());
}
surfaceDataMap[poly.Data].AddPolygon(vertices, normal, uvs, uvs);
}
var mesh = new ArrayMesh();
foreach (var (materialId, surfaceData) in surfaceDataMap)
{
var array = surfaceData.BuildSurfaceArray();
var surfaceIdx = mesh.GetSurfaceCount();
mesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, array);
for (var i = 0; i < materials.Count; i++)
{
var m = modelFile.Materials[i];
if (m.Slot == materialId)
{
mesh.SurfaceSetMaterial(surfaceIdx, materials[i]);
break;
}
}
}
var pos = -modelFile.Header.Center.ToGodotVec3();
var meshInstance = new MeshInstance3D { Mesh = mesh, Position = pos };
return meshInstance;
}
}