Browse Source

Further mesh conversion research: animation frames are now converted into blend shape animations, which can be played back either with or without smoothing.

console
Nico de Poel 5 years ago
parent
commit
61a845338e
  1. 6
      Assets/Scripts/Data/QExtensions.cs
  2. 33
      Assets/Scripts/Modules/AliasModelAnimator.cs
  3. 3
      Assets/Scripts/Modules/AliasModelAnimator.cs.meta
  4. 76
      Assets/Scripts/Modules/RenderModule.cs

6
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);
}
}

33
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<SkinnedMeshRenderer>();
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;
}
}
}

3
Assets/Scripts/Modules/AliasModelAnimator.cs.meta

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7ff59a2f3571489c92ba4dc81d3df990
timeCreated: 1618590871

76
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<MeshFilter>();
mf.sharedMesh = mesh;
var mr = go.AddComponent<MeshRenderer>();
mr.material = new Material(Shader.Find("Universal Render Pipeline/Simple Lit"));
if (header.numFrames > 1)
{
var mr = go.AddComponent<SkinnedMeshRenderer>();
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<AliasModelAnimator>();
}
else
{
var mf = go.AddComponent<MeshFilter>();
mf.sharedMesh = mesh;
var mr = go.AddComponent<MeshRenderer>();
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);
}
}
}
Loading…
Cancel
Save