@ -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 )