Browse Source

First test converting models from Quake into Unity meshes, including normals and UVs. It's still far from perfect (back side UVs are not handled at all yet) but it's a good demonstration of what will be possible.

console
Nico de Poel 5 years ago
parent
commit
834ff9a794
  1. 6
      Assets/Scripts/Data/QExtensions.cs
  2. 179
      Assets/Scripts/Data/QLightNormals.cs
  3. 3
      Assets/Scripts/Data/QLightNormals.cs.meta
  4. 10
      Assets/Scripts/Modules/RenderModule.Interop.cs
  5. 73
      Assets/Scripts/Modules/RenderModule.cs
  6. 2
      engine/Quake/gl_model.h
  7. 6
      engine/UniQuake/gl_uniquake.c

6
Assets/Scripts/Data/QExtensions.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using UnityEngine;
public static class QExtensions public static class QExtensions
{ {
@ -18,4 +19,9 @@ public static class QExtensions
} }
return result; return result;
} }
public static Vector3 ToVector3(this QVec3 vec)
{
return new Vector3(vec.x, vec.y, vec.z);
}
} }

179
Assets/Scripts/Data/QLightNormals.cs

@ -0,0 +1,179 @@
using UnityEngine;
public static class QLightNormals
{
public static Vector3 Get(byte index)
{
if (index >= Normals.Length)
return Vector3.zero;
return Normals[index];
}
// Converted from anorms.h
private static readonly Vector3[] Normals =
{
new Vector3(-0.525731f, 0.000000f, 0.850651f),
new Vector3(-0.442863f, 0.238856f, 0.864188f),
new Vector3(-0.295242f, 0.000000f, 0.955423f),
new Vector3(-0.309017f, 0.500000f, 0.809017f),
new Vector3(-0.162460f, 0.262866f, 0.951056f),
new Vector3(0.000000f, 0.000000f, 1.000000f),
new Vector3(0.000000f, 0.850651f, 0.525731f),
new Vector3(-0.147621f, 0.716567f, 0.681718f),
new Vector3(0.147621f, 0.716567f, 0.681718f),
new Vector3(0.000000f, 0.525731f, 0.850651f),
new Vector3(0.309017f, 0.500000f, 0.809017f),
new Vector3(0.525731f, 0.000000f, 0.850651f),
new Vector3(0.295242f, 0.000000f, 0.955423f),
new Vector3(0.442863f, 0.238856f, 0.864188f),
new Vector3(0.162460f, 0.262866f, 0.951056f),
new Vector3(-0.681718f, 0.147621f, 0.716567f),
new Vector3(-0.809017f, 0.309017f, 0.500000f),
new Vector3(-0.587785f, 0.425325f, 0.688191f),
new Vector3(-0.850651f, 0.525731f, 0.000000f),
new Vector3(-0.864188f, 0.442863f, 0.238856f),
new Vector3(-0.716567f, 0.681718f, 0.147621f),
new Vector3(-0.688191f, 0.587785f, 0.425325f),
new Vector3(-0.500000f, 0.809017f, 0.309017f),
new Vector3(-0.238856f, 0.864188f, 0.442863f),
new Vector3(-0.425325f, 0.688191f, 0.587785f),
new Vector3(-0.716567f, 0.681718f, -0.147621f),
new Vector3(-0.500000f, 0.809017f, -0.309017f),
new Vector3(-0.525731f, 0.850651f, 0.000000f),
new Vector3(0.000000f, 0.850651f, -0.525731f),
new Vector3(-0.238856f, 0.864188f, -0.442863f),
new Vector3(0.000000f, 0.955423f, -0.295242f),
new Vector3(-0.262866f, 0.951056f, -0.162460f),
new Vector3(0.000000f, 1.000000f, 0.000000f),
new Vector3(0.000000f, 0.955423f, 0.295242f),
new Vector3(-0.262866f, 0.951056f, 0.162460f),
new Vector3(0.238856f, 0.864188f, 0.442863f),
new Vector3(0.262866f, 0.951056f, 0.162460f),
new Vector3(0.500000f, 0.809017f, 0.309017f),
new Vector3(0.238856f, 0.864188f, -0.442863f),
new Vector3(0.262866f, 0.951056f, -0.162460f),
new Vector3(0.500000f, 0.809017f, -0.309017f),
new Vector3(0.850651f, 0.525731f, 0.000000f),
new Vector3(0.716567f, 0.681718f, 0.147621f),
new Vector3(0.716567f, 0.681718f, -0.147621f),
new Vector3(0.525731f, 0.850651f, 0.000000f),
new Vector3(0.425325f, 0.688191f, 0.587785f),
new Vector3(0.864188f, 0.442863f, 0.238856f),
new Vector3(0.688191f, 0.587785f, 0.425325f),
new Vector3(0.809017f, 0.309017f, 0.500000f),
new Vector3(0.681718f, 0.147621f, 0.716567f),
new Vector3(0.587785f, 0.425325f, 0.688191f),
new Vector3(0.955423f, 0.295242f, 0.000000f),
new Vector3(1.000000f, 0.000000f, 0.000000f),
new Vector3(0.951056f, 0.162460f, 0.262866f),
new Vector3(0.850651f, -0.525731f, 0.000000f),
new Vector3(0.955423f, -0.295242f, 0.000000f),
new Vector3(0.864188f, -0.442863f, 0.238856f),
new Vector3(0.951056f, -0.162460f, 0.262866f),
new Vector3(0.809017f, -0.309017f, 0.500000f),
new Vector3(0.681718f, -0.147621f, 0.716567f),
new Vector3(0.850651f, 0.000000f, 0.525731f),
new Vector3(0.864188f, 0.442863f, -0.238856f),
new Vector3(0.809017f, 0.309017f, -0.500000f),
new Vector3(0.951056f, 0.162460f, -0.262866f),
new Vector3(0.525731f, 0.000000f, -0.850651f),
new Vector3(0.681718f, 0.147621f, -0.716567f),
new Vector3(0.681718f, -0.147621f, -0.716567f),
new Vector3(0.850651f, 0.000000f, -0.525731f),
new Vector3(0.809017f, -0.309017f, -0.500000f),
new Vector3(0.864188f, -0.442863f, -0.238856f),
new Vector3(0.951056f, -0.162460f, -0.262866f),
new Vector3(0.147621f, 0.716567f, -0.681718f),
new Vector3(0.309017f, 0.500000f, -0.809017f),
new Vector3(0.425325f, 0.688191f, -0.587785f),
new Vector3(0.442863f, 0.238856f, -0.864188f),
new Vector3(0.587785f, 0.425325f, -0.688191f),
new Vector3(0.688191f, 0.587785f, -0.425325f),
new Vector3(-0.147621f, 0.716567f, -0.681718f),
new Vector3(-0.309017f, 0.500000f, -0.809017f),
new Vector3(0.000000f, 0.525731f, -0.850651f),
new Vector3(-0.525731f, 0.000000f, -0.850651f),
new Vector3(-0.442863f, 0.238856f, -0.864188f),
new Vector3(-0.295242f, 0.000000f, -0.955423f),
new Vector3(-0.162460f, 0.262866f, -0.951056f),
new Vector3(0.000000f, 0.000000f, -1.000000f),
new Vector3(0.295242f, 0.000000f, -0.955423f),
new Vector3(0.162460f, 0.262866f, -0.951056f),
new Vector3(-0.442863f, -0.238856f, -0.864188f),
new Vector3(-0.309017f, -0.500000f, -0.809017f),
new Vector3(-0.162460f, -0.262866f, -0.951056f),
new Vector3(0.000000f, -0.850651f, -0.525731f),
new Vector3(-0.147621f, -0.716567f, -0.681718f),
new Vector3(0.147621f, -0.716567f, -0.681718f),
new Vector3(0.000000f, -0.525731f, -0.850651f),
new Vector3(0.309017f, -0.500000f, -0.809017f),
new Vector3(0.442863f, -0.238856f, -0.864188f),
new Vector3(0.162460f, -0.262866f, -0.951056f),
new Vector3(0.238856f, -0.864188f, -0.442863f),
new Vector3(0.500000f, -0.809017f, -0.309017f),
new Vector3(0.425325f, -0.688191f, -0.587785f),
new Vector3(0.716567f, -0.681718f, -0.147621f),
new Vector3(0.688191f, -0.587785f, -0.425325f),
new Vector3(0.587785f, -0.425325f, -0.688191f),
new Vector3(0.000000f, -0.955423f, -0.295242f),
new Vector3(0.000000f, -1.000000f, 0.000000f),
new Vector3(0.262866f, -0.951056f, -0.162460f),
new Vector3(0.000000f, -0.850651f, 0.525731f),
new Vector3(0.000000f, -0.955423f, 0.295242f),
new Vector3(0.238856f, -0.864188f, 0.442863f),
new Vector3(0.262866f, -0.951056f, 0.162460f),
new Vector3(0.500000f, -0.809017f, 0.309017f),
new Vector3(0.716567f, -0.681718f, 0.147621f),
new Vector3(0.525731f, -0.850651f, 0.000000f),
new Vector3(-0.238856f, -0.864188f, -0.442863f),
new Vector3(-0.500000f, -0.809017f, -0.309017f),
new Vector3(-0.262866f, -0.951056f, -0.162460f),
new Vector3(-0.850651f, -0.525731f, 0.000000f),
new Vector3(-0.716567f, -0.681718f, -0.147621f),
new Vector3(-0.716567f, -0.681718f, 0.147621f),
new Vector3(-0.525731f, -0.850651f, 0.000000f),
new Vector3(-0.500000f, -0.809017f, 0.309017f),
new Vector3(-0.238856f, -0.864188f, 0.442863f),
new Vector3(-0.262866f, -0.951056f, 0.162460f),
new Vector3(-0.864188f, -0.442863f, 0.238856f),
new Vector3(-0.809017f, -0.309017f, 0.500000f),
new Vector3(-0.688191f, -0.587785f, 0.425325f),
new Vector3(-0.681718f, -0.147621f, 0.716567f),
new Vector3(-0.442863f, -0.238856f, 0.864188f),
new Vector3(-0.587785f, -0.425325f, 0.688191f),
new Vector3(-0.309017f, -0.500000f, 0.809017f),
new Vector3(-0.147621f, -0.716567f, 0.681718f),
new Vector3(-0.425325f, -0.688191f, 0.587785f),
new Vector3(-0.162460f, -0.262866f, 0.951056f),
new Vector3(0.442863f, -0.238856f, 0.864188f),
new Vector3(0.162460f, -0.262866f, 0.951056f),
new Vector3(0.309017f, -0.500000f, 0.809017f),
new Vector3(0.147621f, -0.716567f, 0.681718f),
new Vector3(0.000000f, -0.525731f, 0.850651f),
new Vector3(0.425325f, -0.688191f, 0.587785f),
new Vector3(0.587785f, -0.425325f, 0.688191f),
new Vector3(0.688191f, -0.587785f, 0.425325f),
new Vector3(-0.955423f, 0.295242f, 0.000000f),
new Vector3(-0.951056f, 0.162460f, 0.262866f),
new Vector3(-1.000000f, 0.000000f, 0.000000f),
new Vector3(-0.850651f, 0.000000f, 0.525731f),
new Vector3(-0.955423f, -0.295242f, 0.000000f),
new Vector3(-0.951056f, -0.162460f, 0.262866f),
new Vector3(-0.864188f, 0.442863f, -0.238856f),
new Vector3(-0.951056f, 0.162460f, -0.262866f),
new Vector3(-0.809017f, 0.309017f, -0.500000f),
new Vector3(-0.864188f, -0.442863f, -0.238856f),
new Vector3(-0.951056f, -0.162460f, -0.262866f),
new Vector3(-0.809017f, -0.309017f, -0.500000f),
new Vector3(-0.681718f, 0.147621f, -0.716567f),
new Vector3(-0.681718f, -0.147621f, -0.716567f),
new Vector3(-0.850651f, 0.000000f, -0.525731f),
new Vector3(-0.688191f, 0.587785f, -0.425325f),
new Vector3(-0.587785f, 0.425325f, -0.688191f),
new Vector3(-0.425325f, 0.688191f, -0.587785f),
new Vector3(-0.425325f, -0.688191f, -0.587785f),
new Vector3(-0.587785f, -0.425325f, -0.688191f),
new Vector3(-0.688191f, -0.587785f, -0.425325f),
};
}

3
Assets/Scripts/Data/QLightNormals.cs.meta

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: db4ebdce7b464932b60da167ed311249
timeCreated: 1618581844

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

@ -31,12 +31,15 @@ public partial class RenderModule: CallbackHandler<RenderModule>
} }
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private delegate int UploadAliasModelCallback(IntPtr target, [MarshalAs(UnmanagedType.LPStr)] string name,
private delegate int UploadAliasModelCallback(IntPtr target,
[MarshalAs(UnmanagedType.LPStr)] string name, QVec3 boundsMin, QVec3 boundsMax,
QAliasHeader header, IntPtr frames, [MarshalAs(UnmanagedType.LPArray, SizeConst = MaxAliasFrames)] QAliasHeader header, IntPtr frames, [MarshalAs(UnmanagedType.LPArray, SizeConst = MaxAliasFrames)]
IntPtr[] poseVerts, IntPtr triangles, IntPtr stVerts); IntPtr[] poseVerts, IntPtr triangles, IntPtr stVerts);
[MonoPInvokeCallback(typeof(UploadAliasModelCallback))] [MonoPInvokeCallback(typeof(UploadAliasModelCallback))]
private static int Callback_UploadAliasModel(IntPtr target, string name, QAliasHeader header, IntPtr frames,
private static int Callback_UploadAliasModel(IntPtr target,
string name, QVec3 boundsMin, QVec3 boundsMax,
QAliasHeader header, IntPtr frames,
IntPtr[] poseVerts, IntPtr triangles, IntPtr stVerts) IntPtr[] poseVerts, IntPtr triangles, IntPtr stVerts)
{ {
if (header == null) if (header == null)
@ -54,7 +57,8 @@ public partial class RenderModule: CallbackHandler<RenderModule>
poseVertices[i] = poseVerts[i].ToStructArray<QTriVertex>(header.numVerts); poseVertices[i] = poseVerts[i].ToStructArray<QTriVertex>(header.numVerts);
} }
return GetSelf(target).UploadAliasModel(name, header, poseVertices,
return GetSelf(target).UploadAliasModel(
name, boundsMin, boundsMax, header, poseVertices,
triangles.ToStructArray<QTriangle>(header.numTriangles), triangles.ToStructArray<QTriangle>(header.numTriangles),
stVerts.ToStructArray<QSTVert>(header.numVerts)); stVerts.ToStructArray<QSTVert>(header.numVerts));
} }

73
Assets/Scripts/Modules/RenderModule.cs

@ -10,9 +10,80 @@ public partial class RenderModule
BuildCallbacks(); BuildCallbacks();
} }
private int UploadAliasModel(string name, QAliasHeader header, QTriVertex[][] poseVertices, QTriangle[] triangles, QSTVert[] stVertices)
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)"); 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);
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.SetUVs(0, uvs);
mesh.Optimize(); // This ensures that triangles will be properly fused and organized in the best possible way
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"));
xPos += 1f;
return 1; return 1;
} }
private static void ConvertVertices(QTriVertex[] triVerts, Vector3 boundsMin, Vector3 boundsMax, out Vector3[] vertices, out Vector3[] normals)
{
int numVerts = triVerts.Length;
vertices = new Vector3[numVerts];
normals = new Vector3[numVerts];
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);
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);
}
}
} }

2
engine/Quake/gl_model.h

@ -516,6 +516,6 @@ byte *Mod_NoVisPVS (qmodel_t *model);
void Mod_SetExtraFlags (qmodel_t *mod); void Mod_SetExtraFlags (qmodel_t *mod);
int UQ_GL_UploadAliasModel(const char *name, aliashdr_t *aliashdr, trivertx_t **poseVerts, mtriangle_t *triangles, stvert_t *stVerts);
int UQ_GL_UploadAliasModel(qmodel_t *model, aliashdr_t *aliashdr, trivertx_t **poseVerts, mtriangle_t *triangles, stvert_t *stVerts);
#endif // __MODEL__ #endif // __MODEL__

6
engine/UniQuake/gl_uniquake.c

@ -10,14 +10,14 @@ typedef struct unity_glcalls_s
{ {
void *target; void *target;
int(*UploadAliasModel)(void *target, const char *name, aliashdr_t *aliashdr, maliasframedesc_t *frames, trivertx_t **poseVerts, mtriangle_t *triangles, stvert_t *stVerts);
int(*UploadAliasModel)(void *target, const char *name, vec3_t mins, vec3_t maxs, aliashdr_t *aliashdr, maliasframedesc_t *frames, trivertx_t **poseVerts, mtriangle_t *triangles, stvert_t *stVerts);
} unity_glcalls_t; } unity_glcalls_t;
const unity_glcalls_t *unity_glcalls; const unity_glcalls_t *unity_glcalls;
int UQ_GL_UploadAliasModel(const char *name, aliashdr_t *aliashdr, trivertx_t **poseVerts, mtriangle_t *triangles, stvert_t *stVerts)
int UQ_GL_UploadAliasModel(qmodel_t *model, aliashdr_t *aliashdr, trivertx_t **poseVerts, mtriangle_t *triangles, stvert_t *stVerts)
{ {
// C# doesn't really understand this idea of a variable-length embedded array of structs, // C# doesn't really understand this idea of a variable-length embedded array of structs,
// so we pass along the pointer to the first frame struct which allows us to manually marshal the entire array. // so we pass along the pointer to the first frame struct which allows us to manually marshal the entire array.
return unity_glcalls->UploadAliasModel(unity_glcalls->target, name, aliashdr, &aliashdr->frames[0], poseVerts, triangles, stVerts);
return unity_glcalls->UploadAliasModel(unity_glcalls->target, model->name, model->mins, model->maxs, aliashdr, &aliashdr->frames[0], poseVerts, triangles, stVerts);
} }
Loading…
Cancel
Save