Browse Source

First test to upload model data (brush models and alias models) from Quake to Unity

console
Nico de Poel 5 years ago
parent
commit
ec8951fa7c
  1. 21
      Assets/Scripts/DataDefs/QExtensions.cs
  2. 3
      Assets/Scripts/DataDefs/QExtensions.cs.meta
  3. 22
      Assets/Scripts/DataDefs/QMath.cs
  4. 3
      Assets/Scripts/DataDefs/QMath.cs.meta
  5. 174
      Assets/Scripts/DataDefs/QModel.cs
  6. 11
      Assets/Scripts/DataDefs/QModel.cs.meta
  7. 58
      Assets/Scripts/ModCalls.cs
  8. 3
      Assets/Scripts/ModCalls.cs.meta
  9. 7
      Assets/Scripts/UniQuake.cs
  10. 2
      engine/code/gl_mesh.c
  11. 2
      engine/code/gl_model.c
  12. 2
      engine/code/gl_model.h
  13. 23
      engine/projects/uniquake/mod_uniquake.c
  14. 3
      engine/projects/uniquake/uniquake.c
  15. 3
      engine/projects/uniquake/uniquake.h
  16. 1
      engine/projects/uniquake/uniquake.vcxproj
  17. 3
      engine/projects/uniquake/uniquake.vcxproj.filters

21
Assets/Scripts/DataDefs/QExtensions.cs

@ -0,0 +1,21 @@
using System;
using System.Runtime.InteropServices;
public static class QExtensions
{
public static TStruct[] ToStructArray<TStruct>(this IntPtr ptr, int count)
{
if (ptr == IntPtr.Zero)
return null;
TStruct[] result = new TStruct[count];
int size = Marshal.SizeOf<TStruct>();
IntPtr current = ptr;
for (int i = 0; i < count; ++i)
{
result[i] = Marshal.PtrToStructure<TStruct>(current);
current = new IntPtr(current.ToInt64() + size);
}
return result;
}
}

3
Assets/Scripts/DataDefs/QExtensions.cs.meta

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c414049936614c2ab8169a12cf45f430
timeCreated: 1617358962

22
Assets/Scripts/DataDefs/QMath.cs

@ -0,0 +1,22 @@
using System;
using System.Runtime.InteropServices;
/// <summary>
/// Managed equivalent of vec3_t
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct QVec3
{
public float x;
public float y;
public float z;
}
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct QVec4i
{
public int x;
public int y;
public int z;
public int w;
}

3
Assets/Scripts/DataDefs/QMath.cs.meta

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 23d0952cd4454f4cbd94d4c4d1ae2667
timeCreated: 1617282271

174
Assets/Scripts/DataDefs/QModel.cs

@ -0,0 +1,174 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
/// <summary>
/// Managed equivalent of model_t
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 0, CharSet = CharSet.Ansi)]
public class QModel
{
private const int MaxPath = 64; // Should correspond to MAX_QPATH
private const int MaxMapHulls = 4; // Should correspond to MAX_MAP_HULLS
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MaxPath)] public string name;
public bool needLoad;
public QModelType type;
public int numFrames;
public QSyncType syncType;
public int flags;
// Volume occupied by the model graphics
public QVec3 mins, maxs;
public float radius;
// Solid volume for clipping
public bool clipBox;
public QVec3 clipMins, clipMaxs;
// Brush model
public int firstModelSurface, numModelSurfaces;
public int numSubModels;
public IntPtr subModels; // Array of dmodel_t
public int numPlanes;
public IntPtr planes; // Array of mplane_t
public int numLeafs;
public IntPtr leafs; // Array of mleaf_t
public int numVertices;
public IntPtr vertices; // Array of mvertex_t
public int numEdges;
public IntPtr edges; // Array of medge_t
public int numNodes;
public IntPtr nodes; // Array of mnode_t
public int numTexInfo;
public IntPtr texInfo; // Array of mtexinfo_t
public int numSurfaces;
public IntPtr surfaces; // Array of msurface_t
public int numSurfEdges;
public IntPtr surfEdges; // Array of int
public int numClipNodes;
public IntPtr clipNodes; // Array of dclipnode_t
public int numMarkSurfaces;
public IntPtr markSurfaces; // Array of msurface_t pointers
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MaxMapHulls)] public QHull[] hulls;
public int numTextures;
public IntPtr textures; // Array of texture_t pointers
public IntPtr visData; // Array of bytes
public IntPtr lightData; // Array of bytes
[MarshalAs(UnmanagedType.LPStr)] public string entities;
public IntPtr userCache;
}
/// <summary>
/// Managed equivalent of aliashdr_t
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public class QAliasHeader
{
private const int MaxSkins = 32; // Should correspond to MAX_SKINS
public int ident;
public int version;
public QVec3 scale;
public QVec3 scaleOrigin;
public float boundingRadius;
public QVec3 eyePosition;
public int numSkins;
public int skinWidth;
public int skinHeight;
public int numVerts;
public int numTriangles;
public int numFrames;
public QSyncType syncType;
public int flags;
public float size;
public int numPoses;
public int poseVerts;
public int poseData;
public int commands;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MaxSkins)] public QVec4i[] glTextureNum;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MaxSkins)] public int[] texels;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MaxSkins)] public bool[] skinLuma;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MaxSkins)] public bool[] skinLuma8bit;
// Actually variable sized, but we receive an additional pointer to the first element so we can manually marshal the entire array
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] public QAliasFrameDesc[] frames;
}
/// <summary>
/// Managed equivalent of modtype_t
/// </summary>
public enum QModelType
{
Brush,
Sprite,
Alias,
}
/// <summary>
/// Managed equivalent of synctype_t
/// </summary>
public enum QSyncType
{
Sync = 0,
Rand
}
/// <summary>
/// Managed equivalent of hull_t
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct QHull
{
public IntPtr clipNodes; // Array of dclipnode_t
public IntPtr planes; // Array of mplane_t
public int firstClipNode;
public int lastClipNode;
public QVec3 clipMins;
public QVec3 clipMaxs;
}
/// <summary>
/// Managed equivalent of maliasframedesc_t
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 0, CharSet = CharSet.Ansi)]
public struct QAliasFrameDesc
{
public int firstPose;
public int numPoses;
public float interval;
public QTriVertex bboxMin;
public QTriVertex bboxMax;
public int frame;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)] public string name;
}
/// <summary>
/// Managed equivalent of trivertx_t
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct QTriVertex
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public byte[] v;
public byte lightNormalIndex;
}

11
Assets/Scripts/DataDefs/QModel.cs.meta

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5c427bd710e259c40813a869b8d12150
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

58
Assets/Scripts/ModCalls.cs

@ -0,0 +1,58 @@
using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using AOT;
using UnityEngine;
public class ModCalls : CallbackHandler<ModCalls>
{
private readonly UniQuake uq;
public ModCalls(UniQuake uniQuake)
{
uq = uniQuake;
var callbacks = new Callbacks
{
target = SelfPtr,
ModUploadModel = CreateCallback<ModUploadModelCallback>(Callback_ModUploadModel),
};
RegisterCallbacks(callbacks);
}
/// <summary>
/// This matches struct unity_syscalls_s from uniquake.h in native code.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 0)]
private class Callbacks
{
public IntPtr target;
public IntPtr ModUploadModel;
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void ModUploadModelCallback(IntPtr target, QModel model, QAliasHeader header, IntPtr frames);
[MonoPInvokeCallback(typeof(ModUploadModelCallback))]
private static void Callback_ModUploadModel(IntPtr target, QModel model, QAliasHeader header, IntPtr frames)
{
if (header != null)
header.frames = frames.ToStructArray<QAliasFrameDesc>(header.numFrames);
GetSelf(target).UploadModel(model, header);
}
private void UploadModel(QModel model, QAliasHeader header)
{
Debug.Log($"Model '{model?.name}' of type {model?.type}, num frames = {header?.numFrames}");
if (header?.frames != null)
{
string str = string.Join(", ", header.frames.Select(f => f.name));
Debug.Log($"Frame list: {str}");
}
}
}

3
Assets/Scripts/ModCalls.cs.meta

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: aa8562de402f45e896cda0a84a21342c
timeCreated: 1617282485

7
Assets/Scripts/UniQuake.cs

@ -9,6 +9,7 @@ public class UniQuake: MonoBehaviour
private QuakeParms quakeParms;
private SysCalls sysCalls;
private ModCalls modCalls;
void Start()
{
@ -18,6 +19,7 @@ public class UniQuake: MonoBehaviour
"-window",
"-width", "1440",
"-height", "1080",
"+developer", "1",
};
quakeParms = new QuakeParms
@ -29,8 +31,9 @@ public class UniQuake: MonoBehaviour
quakeParms.AllocateMemory(MemSize);
sysCalls = new SysCalls(this);
modCalls = new ModCalls(this);
UniQuake_Init(quakeParms, sysCalls.ToIntPtr);
UniQuake_Init(quakeParms, sysCalls.ToIntPtr, modCalls.ToIntPtr);
}
void Update()
@ -50,7 +53,7 @@ public class UniQuake: MonoBehaviour
}
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
private static extern void UniQuake_Init(QuakeParms parms, IntPtr syscalls);
private static extern void UniQuake_Init(QuakeParms parms, IntPtr sysCalls, IntPtr modCalls);
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
private static extern void UniQuake_Update(float deltaTime);

2
engine/code/gl_mesh.c

@ -358,5 +358,7 @@ void GL_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr)
for (i=0 ; i<paliashdr->numposes ; i++)
for (j=0 ; j<numorder ; j++)
*verts++ = poseverts[i][vertexorder[j]];
Mod_UploadModel(m, paliashdr);
}

2
engine/code/gl_model.c

@ -1506,6 +1506,8 @@ void Mod_LoadBrushModel (model_t *mod, void *buffer)
mod = loadmodel;
}
}
Mod_UploadModel(mod, NULL);
}
/*

2
engine/code/gl_model.h

@ -451,4 +451,6 @@ void Mod_TouchModel (char *name);
mleaf_t *Mod_PointInLeaf (float *p, model_t *model);
byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model);
void Mod_UploadModel(model_t *model, aliashdr_t *aliashdr);
#endif // __MODEL__

23
engine/projects/uniquake/mod_uniquake.c

@ -0,0 +1,23 @@
#include "uniquake.h"
#include "../../code/quakedef.h"
#include "../../code/gl_model.h"
typedef struct unity_modcalls_s
{
void *target;
void(*ModUploadModel)(void *target, model_t *mdl, aliashdr_t *aliashdr, maliasframedesc_t *frames);
} unity_modcalls_t;
const unity_modcalls_t *unity_modcalls;
void Mod_UploadModel(model_t *mdl, aliashdr_t *aliashdr)
{
// C# doesn't really understand this idea of a variable-length embedded array of structs,
// so we pass along the pointer to the first element which allows us to manually marshal the entire array.
if (aliashdr)
unity_modcalls->ModUploadModel(unity_modcalls->target, mdl, aliashdr, &aliashdr->frames[0]);
else
unity_modcalls->ModUploadModel(unity_modcalls->target, mdl, aliashdr, NULL);
}

3
engine/projects/uniquake/uniquake.c

@ -2,9 +2,10 @@
#include "../../code/quakedef.h"
UNIQUAKE_API void UniQuake_Init(quakeparms_t *parms, const unity_syscalls_t *syscalls)
UNIQUAKE_API void UniQuake_Init(quakeparms_t *parms, const unity_syscalls_t *syscalls, const unity_modcalls_t *modcalls)
{
unity_syscalls = syscalls;
unity_modcalls = modcalls;
COM_InitArgv(parms->argc, parms->argv);
parms->argc = com_argc;

3
engine/projects/uniquake/uniquake.h

@ -29,3 +29,6 @@ typedef struct unity_syscalls_s
} unity_syscalls_t;
extern const unity_syscalls_t *unity_syscalls;
typedef struct unity_modcalls_s unity_modcalls_t;
extern const unity_modcalls_t *unity_modcalls;

1
engine/projects/uniquake/uniquake.vcxproj

@ -352,6 +352,7 @@
<ClCompile Include="..\..\jpeg-6\jmemmgr.c" />
<ClCompile Include="..\..\jpeg-6\jmemnobs.c" />
<ClCompile Include="..\..\jpeg-6\jutils.c" />
<ClCompile Include="mod_uniquake.c" />
<ClCompile Include="sys_uniquake.c" />
<ClCompile Include="uniquake.c" />
</ItemGroup>

3
engine/projects/uniquake/uniquake.vcxproj.filters

@ -530,6 +530,9 @@
<ClCompile Include="sys_uniquake.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="mod_uniquake.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\..\code\winquake.rc">

Loading…
Cancel
Save