You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
171 lines
5.2 KiB
171 lines
5.2 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.InteropServices;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
|
|
public class BrushModel
|
|
{
|
|
public string Name { get; }
|
|
|
|
private readonly List<SubModel> subModels = new List<SubModel>();
|
|
|
|
// Reusable temporary data containers
|
|
private readonly List<Vector3> tempVertices = new List<Vector3>();
|
|
private readonly List<Vector2> tempTextureUVs = new List<Vector2>();
|
|
private readonly List<Vector2> tempLightmapUVs = new List<Vector2>();
|
|
private readonly List<ushort> tempIndices = new List<ushort>();
|
|
|
|
public BrushModel(string name)
|
|
{
|
|
Name = name;
|
|
}
|
|
|
|
public int SubModelCount => subModels.Count;
|
|
|
|
public SubModel GetSubModel(int index)
|
|
{
|
|
return subModels[index];
|
|
}
|
|
|
|
public void ImportMeshData(QModel model)
|
|
{
|
|
var inSubModels = model.SubModels;
|
|
var inSurfaces = model.Surfaces;
|
|
|
|
for (int modelIdx = 0; modelIdx < inSubModels.Length; ++modelIdx)
|
|
{
|
|
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<QSurface>>();
|
|
GroupSurfaces(headNode, inSurfaces, surfaceGroups);
|
|
|
|
// Create a single mesh for each group of surfaces
|
|
foreach (var group in surfaceGroups)
|
|
{
|
|
var key = group.Key;
|
|
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 subModel in subModels)
|
|
{
|
|
subModel.Dispose();
|
|
}
|
|
|
|
subModels.Clear();
|
|
}
|
|
|
|
private void GroupSurfaces(QNode node, QSurface[] surfaces, Dictionary<(IntPtr, int), List<QSurface>> surfaceGroups)
|
|
{
|
|
if (node.contents < 0) // Leaf node
|
|
return;
|
|
|
|
for (int surfIdx = 0; surfIdx < node.numSurfaces; ++surfIdx)
|
|
{
|
|
var surface = surfaces[node.firstSurface + surfIdx];
|
|
|
|
IntPtr texPtr = surface.TextureInfo.texture;
|
|
int lightNum = surface.lightmapTextureNum;
|
|
var key = (texPtr, lightNum);
|
|
|
|
if (!surfaceGroups.ContainsKey(key))
|
|
surfaceGroups[key] = new List<QSurface>();
|
|
|
|
surfaceGroups[key].Add(surface);
|
|
}
|
|
|
|
foreach (var childNode in node.Children)
|
|
{
|
|
GroupSurfaces(childNode, surfaces, surfaceGroups);
|
|
}
|
|
}
|
|
|
|
private Mesh CreateMeshFromSurfaces(List<QSurface> surfaces, string key)
|
|
{
|
|
tempVertices.Clear();
|
|
tempTextureUVs.Clear();
|
|
tempLightmapUVs.Clear();
|
|
tempIndices.Clear();
|
|
|
|
int vertOffset = 0;
|
|
|
|
for (int surfIdx = 0; surfIdx < surfaces.Count; ++surfIdx)
|
|
{
|
|
foreach (var polyVerts in surfaces[surfIdx].GetPolygons())
|
|
{
|
|
for (int vertIdx = 0; vertIdx < polyVerts.Length; ++vertIdx)
|
|
{
|
|
tempVertices.Add(polyVerts[vertIdx].position.ToVector3().ToUnity());
|
|
tempTextureUVs.Add(polyVerts[vertIdx].textureUV.ToVector2());
|
|
tempLightmapUVs.Add(polyVerts[vertIdx].lightmapUV.ToVector2());
|
|
}
|
|
|
|
// Reconstruct triangle fan
|
|
for (ushort index = 2; index < polyVerts.Length; ++index)
|
|
{
|
|
tempIndices.Add((ushort)vertOffset);
|
|
tempIndices.Add((ushort)(vertOffset + index - 1));
|
|
tempIndices.Add((ushort)(vertOffset + index));
|
|
}
|
|
|
|
vertOffset += polyVerts.Length;
|
|
}
|
|
}
|
|
|
|
Mesh mesh = new Mesh();
|
|
mesh.name = $"Surfaces_{key}";
|
|
mesh.SetVertices(tempVertices);
|
|
mesh.SetUVs(0, tempTextureUVs);
|
|
mesh.SetUVs(1, tempLightmapUVs);
|
|
mesh.SetIndices(tempIndices, MeshTopology.Triangles, 0);
|
|
mesh.RecalculateNormals();
|
|
mesh.UploadMeshData(true);
|
|
return mesh;
|
|
}
|
|
|
|
public class SubModel
|
|
{
|
|
public List<SurfaceMesh> SurfaceMeshes { get; } = new List<SurfaceMesh>();
|
|
|
|
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<QTexture>(texturePtr);
|
|
Lightmap = lightmap;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
UnityEngine.Object.Destroy(Mesh);
|
|
}
|
|
}
|
|
}
|