diff --git a/Assets/Scripts/Modules/BrushModel.cs b/Assets/Scripts/Modules/BrushModel.cs index 4129ca1..18dbb0b 100644 --- a/Assets/Scripts/Modules/BrushModel.cs +++ b/Assets/Scripts/Modules/BrushModel.cs @@ -1,12 +1,14 @@ using System; using System.Collections.Generic; +using System.Runtime.InteropServices; using UnityEngine; using UnityEngine.Rendering; public class BrushModel { - private readonly string name; - private readonly List meshes = new List(); + public string Name { get; } + + private readonly List subModels = new List(); // Reusable temporary data containers private readonly List tempVertices = new List(); @@ -14,53 +16,52 @@ public class BrushModel private readonly List tempLightmapUVs = new List(); private readonly List tempIndices = new List(); - private readonly GameObject rootGameObject; - private readonly Material debugMaterial; - public BrushModel(string name) { - this.name = name; + Name = name; + } + + public int SubModelCount => subModels.Count; - rootGameObject = new GameObject(name); - rootGameObject.transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity); - - debugMaterial = new Material(Shader.Find("Universal Render Pipeline/Simple Lit")); + public SubModel GetSubModel(int index) + { + return subModels[index]; } public void ImportMeshData(QModel model) { - var subModels = model.SubModels; - var surfaces = model.Surfaces; + var inSubModels = model.SubModels; + var inSurfaces = model.Surfaces; - for (int modelIdx = 0; modelIdx < subModels.Length; ++modelIdx) + for (int modelIdx = 0; modelIdx < inSubModels.Length; ++modelIdx) { - var modelGO = new GameObject($"SubModel_{modelIdx}"); - modelGO.transform.SetParent(rootGameObject.transform); - - var headNode = subModels[modelIdx].GetHeadNode(model); + var subModel = new SubModel(); + subModels.Add(subModel); + // Traverse the BSP tree and group the surfaces based on their material properties + var headNode = inSubModels[modelIdx].GetHeadNode(model); var surfaceGroups = new Dictionary<(IntPtr, int), List>(); - GroupSurfaces(headNode, surfaces, surfaceGroups); + GroupSurfaces(headNode, inSurfaces, surfaceGroups); + // Create a single mesh for each group of surfaces foreach (var group in surfaceGroups) { var key = group.Key; - var groupGO = new GameObject($"T{key.Item1}_L{key.Item2}"); - groupGO.transform.SetParent(modelGO.transform); - - CreateMeshFromSurfaces(group.Value, groupGO.name, groupGO); + var mesh = CreateMeshFromSurfaces(group.Value, $"T{key.Item1}_L{key.Item2}"); + + subModel.AddSurfaceMesh(new SurfaceMesh(mesh, key.Item1, key.Item2)); } } } public void Dispose() { - foreach (var mesh in meshes) + foreach (var subModel in subModels) { - UnityEngine.Object.Destroy(mesh); + subModel.Dispose(); } - meshes.Clear(); + subModels.Clear(); } private void GroupSurfaces(QNode node, QSurface[] surfaces, Dictionary<(IntPtr, int), List> surfaceGroups) @@ -88,7 +89,7 @@ public class BrushModel } } - private void CreateMeshFromSurfaces(List surfaces, string key, GameObject parentGO) + private Mesh CreateMeshFromSurfaces(List surfaces, string key) { tempVertices.Clear(); tempTextureUVs.Clear(); @@ -128,21 +129,43 @@ public class BrushModel mesh.SetIndices(tempIndices, MeshTopology.Triangles, 0); mesh.RecalculateNormals(); mesh.UploadMeshData(true); - meshes.Add(mesh); - - CreateMeshObject(mesh, parentGO); + return mesh; } - private void CreateMeshObject(Mesh mesh, GameObject parentGO) + public class SubModel { - var mf = parentGO.AddComponent(); - mf.sharedMesh = mesh; - - var mr = parentGO.AddComponent(); - mr.sharedMaterial = debugMaterial; - mr.shadowCastingMode = ShadowCastingMode.Off; - mr.receiveShadows = false; - mr.lightProbeUsage = LightProbeUsage.Off; - mr.reflectionProbeUsage = ReflectionProbeUsage.Off; + public List SurfaceMeshes { get; } = new List(); + + public void AddSurfaceMesh(SurfaceMesh surfaceMesh) + { + SurfaceMeshes.Add(surfaceMesh); + } + + public void Dispose() + { + foreach (var surfaceMesh in SurfaceMeshes) + { + surfaceMesh.Dispose(); + } + } + } + + public class SurfaceMesh + { + public Mesh Mesh { get; } + public QTexture Texture { get; } + public int Lightmap { get; } + + public SurfaceMesh(Mesh mesh, IntPtr texturePtr, int lightmap) + { + Mesh = mesh; + Texture = Marshal.PtrToStructure(texturePtr); + Lightmap = lightmap; + } + + public void Dispose() + { + UnityEngine.Object.Destroy(Mesh); + } } } diff --git a/Assets/Scripts/Modules/RenderModule.cs b/Assets/Scripts/Modules/RenderModule.cs index 364534e..785a00d 100644 --- a/Assets/Scripts/Modules/RenderModule.cs +++ b/Assets/Scripts/Modules/RenderModule.cs @@ -136,7 +136,39 @@ public partial class RenderModule var brushModel = new BrushModel(model.name); brushModel.ImportMeshData(model); - + + // DEBUG + var worldGO = new GameObject(model.name); + for (int i = 0; i < brushModel.SubModelCount; ++i) + { + var subModel = brushModel.GetSubModel(i); + var subModelGO = new GameObject($"SubModel_{i}"); + subModelGO.transform.SetParent(worldGO.transform); + + foreach (var surfaceMesh in subModel.SurfaceMeshes) + { + var meshGO = new GameObject(surfaceMesh.Mesh.name); + meshGO.transform.SetParent(subModelGO.transform); + + var mf = meshGO.AddComponent(); + mf.sharedMesh = surfaceMesh.Mesh; + + var material = new Material(Shader.Find("Universal Render Pipeline/Simple Lit")); + uint texNum = surfaceMesh.Texture.TextureNum; + if (texNum > 0 && textures.ContainsKey(texNum)) + { + material.mainTexture = textures[texNum]; + } + + var mr = meshGO.AddComponent(); + mr.sharedMaterial = material; + mr.shadowCastingMode = ShadowCastingMode.Off; + mr.receiveShadows = false; + mr.lightProbeUsage = LightProbeUsage.Off; + mr.reflectionProbeUsage = ReflectionProbeUsage.Off; + } + } + brushModels.Add(brushModel); return brushModels.Count; }