using UnityEngine; using UnityEngine.Rendering; public partial class RenderModule { private readonly UniQuake uq; public RenderModule(UniQuake uniQuake) { uq = uniQuake; BuildCallbacks(); } private float xPos = -8f; private int UploadAliasModel(string name, QVec3 boundsMin, QVec3 boundsMax, QAliasHeader header, QTriVertex[][] poseVertices, QTriangle[] triangles, QSTVert[] stVertices) { Debug.Log($"Alias model '{name}' with {header.numVerts} vertices, {header.numTriangles} triangles, {header.numFrames} frame(s)"); ConvertVertices(header, poseVertices[0], out var vertices, out var normals); ConvertTriangles(triangles, out var indices); ConvertUVs(stVertices, header.skinWidth, header.skinHeight, out var uvs); var mesh = new Mesh { name = name }; mesh.SetVertices(vertices); mesh.SetNormals(normals); mesh.SetIndices(indices, MeshTopology.Triangles, 0, false); mesh.SetUVs(0, uvs); CreateBlendShapes(mesh, vertices, normals, header, poseVertices); mesh.Optimize(); // This ensures that triangles will be properly fused and organized in the best possible way mesh.RecalculateBounds(); mesh.UploadMeshData(true); var go = new GameObject(System.IO.Path.GetFileNameWithoutExtension(name)); go.transform.SetPositionAndRotation(new Vector3(xPos, 0, 0), Quaternion.Euler(-90, 90, 0)); if (header.numFrames > 1) { var mr = go.AddComponent(); mr.material = new Material(Shader.Find("Universal Render Pipeline/Simple Lit")); mr.sharedMesh = mesh; mr.shadowCastingMode = ShadowCastingMode.Off; mr.receiveShadows = false; mr.lightProbeUsage = LightProbeUsage.Off; mr.reflectionProbeUsage = ReflectionProbeUsage.Off; go.AddComponent(); } else { var mf = go.AddComponent(); mf.sharedMesh = mesh; var mr = go.AddComponent(); mr.material = new Material(Shader.Find("Universal Render Pipeline/Simple Lit")); mr.shadowCastingMode = ShadowCastingMode.Off; mr.receiveShadows = false; mr.lightProbeUsage = LightProbeUsage.Off; mr.reflectionProbeUsage = ReflectionProbeUsage.Off; } xPos += 0.5f; return 1; } private static void ConvertVertices(QAliasHeader header, QTriVertex[] triVerts, out Vector3[] vertices, out Vector3[] normals) { int numVerts = triVerts.Length; vertices = new Vector3[numVerts]; normals = new Vector3[numVerts]; Vector3 scale = header.scale.ToVector3(); Vector3 origin = header.scaleOrigin.ToVector3(); for (int i = 0; i < numVerts; ++i) { vertices[i] = Vector3.Scale(triVerts[i].ToVector3(), scale); normals[i] = QLightNormals.Get(triVerts[i].lightNormalIndex); } } private static void ConvertTriangles(QTriangle[] triangles, out ushort[] indices) { int numTris = triangles.Length; indices = new ushort[numTris * 3]; for (int i = 0; i < numTris; ++i) { indices[i * 3 + 0] = (ushort)triangles[i].vertIndex[2]; indices[i * 3 + 1] = (ushort)triangles[i].vertIndex[1]; indices[i * 3 + 2] = (ushort)triangles[i].vertIndex[0]; } } private static void ConvertUVs(QSTVert[] stVerts, int skinWidth, int skinHeight, out Vector2[] uvs) { int numVerts = stVerts.Length; uvs = new Vector2[numVerts]; // TODO FIXME: this only works correctly for the front side of a model. // To also correctly UV the back side, we need to duplicate vertices on the back/front seam, // and add half the skin width to UVs that are on back side vertices. Vector2 scale = new Vector2(1.0f / skinWidth, 1.0f / skinHeight); for (int i = 0; i < numVerts; ++i) { uvs[i] = Vector2.Scale(new Vector2(stVerts[i].s, skinHeight - stVerts[i].t), scale); } } private static void CreateBlendShapes( Mesh mesh, Vector3[] baseVertices, Vector3[] baseNormals, QAliasHeader header, QTriVertex[][] poseVertices) { var deltaVertices = new Vector3[header.numVerts]; var deltaNormals = new Vector3[header.numVerts]; Vector3 scale = header.scale.ToVector3(); Vector3 origin = header.scaleOrigin.ToVector3(); // Frame 0 is the base pose, so we start with frame 1 for (int frameIdx = 1; frameIdx < header.numFrames; ++frameIdx) { var poseVerts = poseVertices[frameIdx]; for (int vertIdx = 0; vertIdx < header.numVerts; ++vertIdx) { Vector3 vert = Vector3.Scale(poseVerts[vertIdx].ToVector3(), scale); deltaVertices[vertIdx] = vert - baseVertices[vertIdx]; deltaNormals[vertIdx] = QLightNormals.Get(poseVerts[vertIdx].lightNormalIndex) - baseNormals[vertIdx]; } mesh.AddBlendShapeFrame(mesh.name, (float)frameIdx / (header.numFrames-1), deltaVertices, deltaNormals, null); } } }