diff --git a/project/code/TMV/Mission.cs b/project/code/TMV/Mission.cs index e3db0ec..c1ed970 100644 --- a/project/code/TMV/Mission.cs +++ b/project/code/TMV/Mission.cs @@ -164,23 +164,18 @@ public partial class Mission : Node3D // Let's try and place an object :) var modelName = modelNameProp.modelName + ".bin"; - var objPath = _installPaths.GetObjectPath(_campaignName, modelName); - var pos = brush.position.ToGodotVec3(); var rawRot = brush.angle; var rot = new Vector3(rawRot.Y, rawRot.Z, rawRot.X) * 360 / ushort.MaxValue; var scale = scaleProp == null ? Vector3.One : scaleProp.scale.ToGodotVec3(false); - var model = new Model + var model = ModelLoader.LoadModel(_installPaths, _campaignName, modelName); + if (model != null) { - Position = pos, - RotationDegrees = rot, - Scale = scale, - }; - if (objPath != null) - { - model.BuildModel("", objPath); + model.Position = pos; + model.RotationDegrees = rot; + model.Scale = scale; + AddChild(model); } - AddChild(model); } } diff --git a/project/code/TMV/ModelLoader.cs b/project/code/TMV/ModelLoader.cs new file mode 100644 index 0000000..c01e1ff --- /dev/null +++ b/project/code/TMV/ModelLoader.cs @@ -0,0 +1,107 @@ +using System.Collections.Generic; +using System.IO; +using Godot; +using KeepersCompound.LGS; + +namespace KeepersCompound.TMV; + +public static class ModelLoader +{ + public static MeshInstance3D LoadModel(ResourcePathManager pathManager, string campaignName, string modelName) + { + var modelPath = pathManager.GetObjectPath(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; + } + + // TODO: Remove this disgusting hack. Not only is it a hack, it doesn't support custom models + var baseDir = Path.GetDirectoryName(modelPath); + var options = new EnumerationOptions + { + MatchCasing = MatchCasing.CaseInsensitive, + RecurseSubdirectories = true, + }; + var materials = new List(); + foreach (var material in modelFile.Materials) + { + if (material.Type == 0) + { + var paths = Directory.GetFiles(baseDir, material.Name, options); + if (paths.IsEmpty()) continue; + + materials.Add(new StandardMaterial3D + { + AlbedoTexture = TextureLoader.LoadTexture(paths[0]) + }); + } + 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(); + foreach (var poly in modelFile.Polygons) + { + var vertices = new List(); + var normal = modelFile.Normals[poly.Normal].ToGodotVec3(); + var uvs = new List(); + 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; + } +} \ No newline at end of file