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

145 lines
4.9 KiB
C#
Raw Normal View History

using System.Collections.Generic;
using Godot;
using KeepersCompound.LGS;
namespace KeepersCompound.TMV;
2024-08-26 11:38:13 +00:00
public class ModelLoader
{
2024-08-26 11:38:13 +00:00
private readonly Dictionary<(string, string), MeshInstance3D> _cache;
private readonly ResourcePathManager _pathManager;
public ModelLoader(ResourcePathManager pathManager)
{
_pathManager = pathManager;
_cache = new Dictionary<(string, string), MeshInstance3D>();
}
public MeshInstance3D Load(string campaignName, string modelName, bool forceLoad = false)
{
campaignName ??= "";
if (!forceLoad)
{
if (_cache.TryGetValue((campaignName, modelName), out var fmModel))
{
2024-08-30 16:56:28 +00:00
return fmModel?.Duplicate() as MeshInstance3D;
2024-08-26 11:38:13 +00:00
}
else if (_cache.TryGetValue(("", modelName), out var omModel))
{
2024-08-30 16:56:28 +00:00
return omModel?.Duplicate() as MeshInstance3D;
2024-08-26 11:38:13 +00:00
}
}
// 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(_pathManager, ref campaignName, modelName); });
2024-08-29 17:09:10 +00:00
_cache[(campaignName, modelName)] = model;
return model?.Duplicate() as MeshInstance3D;
2024-08-26 11:38:13 +00:00
}
public static MeshInstance3D LoadModel(ResourcePathManager pathManager, ref string campaignName, string modelName)
{
2024-08-26 11:38:13 +00:00
var modelPath = pathManager.GetObjectPath(ref campaignName, modelName);
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 path = pathManager.GetObjectTexturePath(campaignName, material.Name);
2024-08-25 15:49:43 +00:00
if (path == null)
{
path = "user://textures/jorge.png";
2024-08-25 15:49:43 +00:00
GD.Print($"Failed to load model texture: {material.Name}");
}
2024-08-31 10:59:54 +00:00
var mat = new StandardMaterial3D
{
2024-09-01 12:51:05 +00:00
AlbedoTexture = TextureLoader.LoadTexture(path),
Transparency = BaseMaterial3D.TransparencyEnum.AlphaDepthPrePass,
2024-08-31 10:59:54 +00:00
};
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;
}
}