2024-08-25 15:47:05 +00:00
|
|
|
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-25 15:47:05 +00:00
|
|
|
{
|
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))
|
|
|
|
{
|
|
|
|
return fmModel == null ? fmModel : fmModel.Duplicate() as MeshInstance3D;
|
|
|
|
}
|
|
|
|
else if (_cache.TryGetValue(("", modelName), out var omModel))
|
|
|
|
{
|
|
|
|
return omModel == null ? omModel : omModel.Duplicate() as MeshInstance3D;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We don't care if this is null actually, we'll still cache that it's null lol
|
|
|
|
var model = LoadModel(_pathManager, ref campaignName, modelName);
|
|
|
|
var key = (campaignName, modelName);
|
|
|
|
if (_cache.ContainsKey(key))
|
|
|
|
{
|
|
|
|
_cache[key] = model;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_cache.TryAdd(key, model);
|
|
|
|
}
|
|
|
|
return model == null ? model : model.Duplicate() as MeshInstance3D;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static MeshInstance3D LoadModel(ResourcePathManager pathManager, ref string campaignName, string modelName)
|
2024-08-25 15:47:05 +00:00
|
|
|
{
|
2024-08-26 11:38:13 +00:00
|
|
|
var modelPath = pathManager.GetObjectPath(ref campaignName, modelName);
|
2024-08-25 15:47:05 +00:00
|
|
|
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)
|
|
|
|
{
|
2024-08-25 16:09:59 +00:00
|
|
|
var path = pathManager.GetObjectTexturePath(campaignName, modelName, material.Name);
|
2024-08-25 15:49:43 +00:00
|
|
|
if (path == null)
|
|
|
|
{
|
2024-08-25 16:09:59 +00:00
|
|
|
// TODO: JORGE
|
2024-08-25 15:49:43 +00:00
|
|
|
GD.Print($"Failed to load model texture: {material.Name}");
|
|
|
|
continue;
|
|
|
|
}
|
2024-08-25 15:47:05 +00:00
|
|
|
|
|
|
|
materials.Add(new StandardMaterial3D
|
|
|
|
{
|
2024-08-25 15:49:43 +00:00
|
|
|
AlbedoTexture = TextureLoader.LoadTexture(path)
|
2024-08-25 15:47:05 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|