Browse Source

Solved the UV correcting problem for backside triangles by pre-processing the model input data, whereby vertices on the seam are duplicated and adjusted. The rest of the mesh conversion process then remains as it was.

console
Nico de Poel 5 years ago
parent
commit
4d974bf6cd
  1. 5
      Assets/Scripts/Modules/RenderModule.Interop.cs
  2. 82
      Assets/Scripts/Modules/RenderModule.cs

5
Assets/Scripts/Modules/RenderModule.Interop.cs

@ -46,11 +46,14 @@ public partial class RenderModule: CallbackHandler<RenderModule>
return 0;
}
if (header.numFrames > MaxAliasFrames)
header.numFrames = MaxAliasFrames;
if (frames != IntPtr.Zero)
header.frames = frames.ToStructArray<QAliasFrameDesc>(header.numFrames);
var poseVertices = new QTriVertex[header.numFrames][];
for (int i = 0; i < header.numFrames && i < MaxAliasFrames; ++i)
for (int i = 0; i < header.numFrames; ++i)
{
poseVertices[i] = poseVerts[i].ToStructArray<QTriVertex>(header.numVerts);
}

82
Assets/Scripts/Modules/RenderModule.cs

@ -1,4 +1,5 @@
using UnityEngine;
using System;
using UnityEngine;
using UnityEngine.Rendering;
public partial class RenderModule
@ -18,12 +19,17 @@ public partial class RenderModule
{
Debug.Log($"Alias model '{name}' with {header.numVerts} vertices, {header.numTriangles} triangles, {header.numFrames} frame(s)");
// Massage the input data for easier conversion
PreprocessMeshData(header, triangles, ref poseVertices, ref stVertices);
// Triangle indices and UVs are the same for all animation frames
ConvertTriangles(triangles, out var indices);
ConvertUVs(stVertices, header.skinWidth, header.skinHeight, out var uvs);
string modelName = System.IO.Path.GetFileNameWithoutExtension(name);
AliasModel aliasModel = new AliasModel(modelName);
// Identify animation sequences and turn each one into a separate Mesh with a single blend shape animation
Mesh mesh;
string animName = null;
int startFrame = 0;
@ -82,6 +88,76 @@ public partial class RenderModule
return 1;
}
/// <summary>
/// Quake has a bit of a weird outdated mesh setup where skin textures are split into a front side and a back side.
/// Vertices on the seam between the front and back are used by triangles on both sides, but require a correction
/// to their UVs when rendering the backside. To handle this properly, we duplicate these vertices and correct the
/// UVs ahead of time. We do this as a separate pre-process step to keep things simple.
/// </summary>
private static void PreprocessMeshData(QAliasHeader header, QTriangle[] triangles,
ref QTriVertex[][] poseVertices, ref QSTVert[] stVerts)
{
int newVertCount = 0;
// First count how many new vertices we need to make, so we can preallocate the required arrays
for (int triIdx = 0; triIdx < header.numTriangles; ++triIdx)
{
if (triangles[triIdx].facesFront != 0)
continue;
for (int indIdx = 0; indIdx < 3; ++indIdx)
{
int index = triangles[triIdx].vertIndex[indIdx];
if (stVerts[index].onSeam == 0)
continue;
// Back-side vertex on seam, needs to be duplicated and corrected
++newVertCount;
}
}
if (newVertCount == 0)
return;
Array.Resize(ref stVerts, header.numVerts + newVertCount);
for (int frameIdx = 0; frameIdx < header.numFrames; ++frameIdx)
{
Array.Resize(ref poseVertices[frameIdx], header.numVerts + newVertCount);
}
int newVertIndex = header.numVerts;
// Now we go over all the triangles again and duplicate the vertices that are on a seam
for (int triIdx = 0; triIdx < header.numTriangles; ++triIdx)
{
if (triangles[triIdx].facesFront != 0)
continue;
for (int indIdx = 0; indIdx < 3; ++indIdx)
{
int vertIndex = triangles[triIdx].vertIndex[indIdx];
if (stVerts[vertIndex].onSeam == 0)
continue;
// Clone the ST value and correct it to map onto the backside of the skin
QSTVert stVertCopy = stVerts[vertIndex];
stVertCopy.s += header.skinWidth / 2;
stVerts[newVertIndex] = stVertCopy;
// Clone the vertex position data for all frames
for (int frameIdx = 0; frameIdx < header.numFrames; ++frameIdx)
{
QTriVertex triVertCopy = poseVertices[frameIdx][vertIndex];
poseVertices[frameIdx][newVertIndex] = triVertCopy;
}
triangles[triIdx].vertIndex[indIdx] = newVertIndex++;
}
}
header.numVerts += newVertCount;
}
private static void ConvertVertices(QAliasHeader header, QTriVertex[] triVerts, out Vector3[] vertices, out Vector3[] normals)
{
int numVerts = triVerts.Length;
@ -105,6 +181,7 @@ public partial class RenderModule
for (int i = 0; i < numTris; ++i)
{
// Quake triangles are wound clockwise, so we reverse them here
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];
@ -116,9 +193,6 @@ public partial class RenderModule
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)

Loading…
Cancel
Save