You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

365 lines
10 KiB

using System;
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
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = QConstants.MaxQPath)] public string name;
public uint pathId;
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 QVec3 ymins, ymaxs;
public QVec3 rmins, rmaxs;
// 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 QSubModel[] SubModels => subModels.ToStructArray<QSubModel>(numSubModels);
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 (which is just a single vec3_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 QSurface[] Surfaces => surfaces.ToStructArray<QSurface>(numSurfaces);
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 = QConstants.MaxMapHulls)] public QHull[] hulls;
public int numTextures;
public IntPtr textures; // Array of texture_t pointers
// Rest of the fields are unused
}
/// <summary>
/// Managed equivalent of aliashdr_t
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public class QAliasHeader
{
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;
// VBO data from QuakeSpasm; unused by UniQuake
public int numVertsVbo;
public IntPtr meshDesc;
public int numIndexes;
public IntPtr indexes;
public IntPtr vertexes;
public int numPoses;
public int poseVerts;
public int poseData;
public int commands;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = QConstants.MaxSkins * 4)] public IntPtr[] glTextures;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = QConstants.MaxSkins * 4)] public IntPtr[] fbTextures;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = QConstants.MaxSkins)] public int[] texels;
// 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 aliasframetype_t
/// </summary>
public enum QAliasFrameType
{
Single = 0,
Group,
}
/// <summary>
/// Managed equivalent of aliasskintype_t
/// </summary>
public enum QAliasSkinType
{
Single = 0,
Group,
}
/// <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;
}
/// <summary>
/// Managed equivalent of mtriangle_t
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct QTriangle
{
public int facesFront;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public int[] vertIndex;
}
/// <summary>
/// Managed equivalent of stvert_t
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct QSTVert
{
public int onSeam;
public int s, t;
}
/// <summary>
/// Managed equivalent of dmodel_t
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct QSubModel
{
public QVec3 mins, maxs;
public QVec3 origin;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = QConstants.MaxMapHulls)] public int[] headNode;
public int visLeafs;
public int firstFace, numFaces;
public QNode GetHeadNode(QModel model)
{
IntPtr nodePtr = IntPtr.Add(model.nodes, headNode[0] * Marshal.SizeOf<QNode>());
return Marshal.PtrToStructure<QNode>(nodePtr);
}
}
/// <summary>
/// Managed equivalent of mnode_t
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct QNode
{
// Common with leaf
public int contents; // 0 for nodes, negative for leafs
public int visFrame;
public QVec3 mins, maxs;
public IntPtr parent; // Pointer to mnode_t
// Node specific
public IntPtr plane; // Pointer to mplane_t
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public IntPtr[] children; // Array of pointers to mnode_t
public uint firstSurface;
public uint numSurfaces;
public QNode[] Children => new[]
{
Marshal.PtrToStructure<QNode>(children[0]),
Marshal.PtrToStructure<QNode>(children[1]),
};
}
/// <summary>
/// Managed equivalent of mleaf_t
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct QLeaf
{
// Common with node
public int contents; // 0 for nodes, negative for leafs
public int visFrame;
public QVec3 mins, maxs;
public IntPtr parent; // Pointer to mnode_t
// Leaf-specific data is unused
}
/// <summary>
/// Managed equivalent of msurface_t
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct QSurface
{
public int visFrame;
public bool culled;
public QVec3 mins, maxs;
public IntPtr plane; // Pointer to mplane_t
public int flags;
public int firstEdge;
public int numEdges;
public QVec2s textureMins;
public QVec2s extents;
public QVec2i lightST;
public IntPtr polys; // Pointer to glpoly_t
public IntPtr textureChain; // Pointer to msurface_t
public IntPtr texInfo; // Pointer to mtexinfo_t
public int vboFirstVert; // QuakeSpasm VBO stuff
public int dLightFrame;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = (QConstants.MaxDLights + 31) >> 5)] public uint[] dLightBits;
public int lightmapTextureNum;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = QConstants.MaxLightmaps)] public byte[] styles;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = QConstants.MaxLightmaps)] public int[] cachedLight;
public bool cachedDLight;
public IntPtr samples; // Pointer to byte
// This is safe as the Quake engine guarantees this pointer is never null (see: Mod_LoadFaces)
public QTexInfo TextureInfo => Marshal.PtrToStructure<QTexInfo>(texInfo);
public IEnumerable<QGLPolyVert[]> GetPolygons()
{
// This is so nasty. We have to deconstruct a linked list of variable-sized structs. Yuck.
int offset = Marshal.SizeOf<IntPtr>() * 2 + Marshal.SizeOf<int>();
IntPtr polyPtr = polys;
while (polyPtr != IntPtr.Zero)
{
QGLPoly polygon = Marshal.PtrToStructure<QGLPoly>(polyPtr);
QGLPolyVert[] vertices = IntPtr.Add(polyPtr, offset).ToStructArray<QGLPolyVert>(polygon.numVerts);
yield return vertices;
polyPtr = polygon.next;
}
}
}
/// <summary>
/// Managed equivalent of glpoly_t
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct QGLPoly
{
public IntPtr next; // Pointer to glpoly_t (next in linked list)
public IntPtr chain; // Pointer to glpoly_t (start of linked list)
public int numVerts;
// No need to include this field in the struct, as we calculate its offset and marshal the data manually
//[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public QGLPolyVert[] verts; // Variable sized
}
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct QGLPolyVert
{
public QVec3 position;
public QVec2 textureUV;
public QVec2 lightmapUV;
}
/// <summary>
/// Managed equivalent of mtexinfo_t
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct QTexInfo
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2 * 4)] public float[] vecs;
public float mipAdjust;
public IntPtr texture; // Pointer to texture_t
public int flags;
// This is safe as the Quake engine guarantees this pointer is never null (see: Mod_LoadTexinfo)
public QTexture Texture => Marshal.PtrToStructure<QTexture>(texture);
}