diff --git a/Assets/Scripts/Data/QExtensions.cs b/Assets/Scripts/Data/QExtensions.cs index ec73005..f7d35a0 100644 --- a/Assets/Scripts/Data/QExtensions.cs +++ b/Assets/Scripts/Data/QExtensions.cs @@ -24,4 +24,10 @@ public static class QExtensions { return new Vector3(vec.x, vec.y, vec.z); } + + public static Vector3 ToVector3(this QTriVertex triVertex) + { + byte[] v = triVertex.v; + return new Vector3(v[0] / 255f, v[1] / 255f, v[2] / 255f); + } } diff --git a/Assets/Scripts/Modules/AliasModelAnimator.cs b/Assets/Scripts/Modules/AliasModelAnimator.cs new file mode 100644 index 0000000..e5c8cd9 --- /dev/null +++ b/Assets/Scripts/Modules/AliasModelAnimator.cs @@ -0,0 +1,33 @@ +using System.Collections; +using UnityEngine; + +public class AliasModelAnimator : MonoBehaviour +{ + //private int frameNumber = 0; + private float frameNumber = 0; + + IEnumerator Start() + { + var meshRenderer = GetComponent(); + if (!meshRenderer) + { + Destroy(this); + yield break; + } + + var mesh = meshRenderer.sharedMesh; + int numFrames = mesh.GetBlendShapeFrameCount(0); + + while (true) + { + float blendWeight = (float)frameNumber / numFrames; + meshRenderer.SetBlendShapeWeight(0, blendWeight); + + //yield return new WaitForSeconds(0.1f); // Animate at 10 fps + yield return null; + + //frameNumber = (frameNumber + 1) % numFrames; + frameNumber = (frameNumber + Time.deltaTime * 10) % numFrames; + } + } +} diff --git a/Assets/Scripts/Modules/AliasModelAnimator.cs.meta b/Assets/Scripts/Modules/AliasModelAnimator.cs.meta new file mode 100644 index 0000000..bfeaf4e --- /dev/null +++ b/Assets/Scripts/Modules/AliasModelAnimator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7ff59a2f3571489c92ba4dc81d3df990 +timeCreated: 1618590871 \ No newline at end of file diff --git a/Assets/Scripts/Modules/RenderModule.cs b/Assets/Scripts/Modules/RenderModule.cs index 970e23e..3e8ca52 100644 --- a/Assets/Scripts/Modules/RenderModule.cs +++ b/Assets/Scripts/Modules/RenderModule.cs @@ -1,4 +1,5 @@ using UnityEngine; +using UnityEngine.Rendering; public partial class RenderModule { @@ -16,44 +17,65 @@ public partial class RenderModule QTriVertex[][] poseVertices, QTriangle[] triangles, QSTVert[] stVertices) { Debug.Log($"Alias model '{name}' with {header.numVerts} vertices, {header.numTriangles} triangles, {header.numFrames} frame(s)"); - - // TODO: convert Quake poses into Mesh blend shapes with AddBlendShapeFrame - ConvertVertices(poseVertices[0], boundsMin.ToVector3(), boundsMax.ToVector3(), out var vertices, out var normals); + 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); + 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)); - go.transform.localScale = Vector3.one * 0.01f; - var mf = go.AddComponent(); - mf.sharedMesh = mesh; - var mr = go.AddComponent(); - mr.material = new Material(Shader.Find("Universal Render Pipeline/Simple Lit")); + + 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 += 1f; + xPos += 0.5f; return 1; } - private static void ConvertVertices(QTriVertex[] triVerts, Vector3 boundsMin, Vector3 boundsMax, out Vector3[] vertices, out Vector3[] normals) + 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) { - byte[] v = triVerts[i].v; - Vector3 vec = new Vector3(v[0] / 255f, v[1] / 255f, v[2] / 255f); - vertices[i] = boundsMin + Vector3.Scale(boundsMax - boundsMin, vec); + vertices[i] = Vector3.Scale(triVerts[i].ToVector3(), scale); normals[i] = QLightNormals.Get(triVerts[i].lightNormalIndex); } } @@ -86,4 +108,30 @@ public partial class RenderModule 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); + } + } }