using System; using System.Runtime.InteropServices; using AOT; using UnityEngine; using UnityEngine.Profiling; public partial class RenderModule: CallbackHandler { private const int MaxAliasFrames = 256; // Should match MAXALIASFRAMES private void BuildCallbacks() { var callbacks = new Callbacks { target = TargetPtr, UploadAliasModel = CreateCallback(Callback_UploadAliasModel), UploadBrushModel = CreateCallback(Callback_UploadBrushModel), UploadWorldModel = CreateCallback(Callback_UploadWorldModel), UploadTexture = CreateCallback(Callback_UploadTexture), UploadLightmap = CreateCallback(Callback_UploadLightmap), SetupView = CreateCallback(Callback_SetupView), }; RegisterCallbacks(callbacks); } /// /// This matches unity_modcalls_t from mod_uniquake.c in native code. /// [StructLayout(LayoutKind.Sequential, Pack = 0)] private class Callbacks { public IntPtr target; public IntPtr UploadAliasModel; public IntPtr UploadBrushModel; public IntPtr UploadWorldModel; public IntPtr UploadTexture; public IntPtr UploadLightmap; public IntPtr SetupView; } [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] private delegate int UploadAliasModelCallback(IntPtr target, [MarshalAs(UnmanagedType.LPStr)] string name, QAliasHeader header, QAliasFrameType frameType, IntPtr frames, [MarshalAs(UnmanagedType.LPArray, SizeConst = MaxAliasFrames)] IntPtr[] poseVerts, IntPtr triangles, IntPtr stVerts); [MonoPInvokeCallback(typeof(UploadAliasModelCallback))] private static int Callback_UploadAliasModel(IntPtr target, string name, QAliasHeader header, QAliasFrameType frameType, IntPtr frames, IntPtr[] poseVerts, IntPtr triangles, IntPtr stVerts) { if (header == null) { Debug.LogWarning($"Uploading invalid alias model, name = {name}"); return 0; } Profiler.BeginSample("UploadAliasModel"); if (header.numFrames > MaxAliasFrames) header.numFrames = MaxAliasFrames; if (frames != IntPtr.Zero) header.frames = frames.ToStructArray(header.numFrames); header.numPoses = 0; for (int i = 0; i < header.numFrames; ++i) { header.numPoses += header.frames[i].numPoses; } var poseVertices = new QTriVertex[header.numPoses][]; for (int i = 0; i < header.numPoses; ++i) { poseVertices[i] = poseVerts[i].ToStructArray(header.numVerts); } var glTextures = new QGLTexture[QConstants.MaxSkins][]; var fbTextures = new QGLTexture[QConstants.MaxSkins][]; for (int i = 0; i < QConstants.MaxSkins; ++i) { glTextures[i] = header.glTextures[i * 4].ToStructArray(4); fbTextures[i] = header.fbTextures[i * 4].ToStructArray(4); } int result = GetSelf(target).UploadAliasModel( name, header, frameType, poseVertices, triangles.ToStructArray(header.numTriangles), stVerts.ToStructArray(header.numVerts), glTextures, fbTextures); Profiler.EndSample(); return result; } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate int UploadBrushModelCallback(IntPtr target, QModel model); [MonoPInvokeCallback(typeof(UploadBrushModelCallback))] private static int Callback_UploadBrushModel(IntPtr target, QModel model) { if (model == null || model.type != QModelType.Brush) return -1; Profiler.BeginSample("UploadBrushModel"); int result = GetSelf(target).UploadBrushModel(model); Profiler.EndSample(); return result; } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate int UploadWorldModelCallback(IntPtr target, QModel model); [MonoPInvokeCallback(typeof(UploadBrushModelCallback))] private static int Callback_UploadWorldModel(IntPtr target, QModel model) { if (model == null || model.type != QModelType.Brush) return -1; Profiler.BeginSample("UploadWorldModel"); int result = GetSelf(target).UploadWorldModel(model); Profiler.EndSample(); return result; } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate bool UploadTextureCallback(IntPtr target, QGLTexture texture, IntPtr data, ref uint texNum); [MonoPInvokeCallback(typeof(UploadTextureCallback))] private static bool Callback_UploadTexture(IntPtr target, QGLTexture texture, IntPtr data, ref uint texNum) { Profiler.BeginSample("UploadTexture"); byte[] dataBytes = new byte[texture.width * texture.height * 4]; // 32 bits per pixel Marshal.Copy(data, dataBytes, 0, dataBytes.Length); bool result = GetSelf(target).UploadTexture(texture, dataBytes, ref texNum); Profiler.EndSample(); return result; } private readonly byte[] lightmapBytes = new byte[QConstants.LightmapBlockWidth * QConstants.LightmapBlockHeight * 4]; // 32 bits per pixel [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate bool UploadLightmapCallback(IntPtr target, int lmap, IntPtr data); [MonoPInvokeCallback(typeof(UploadLightmapCallback))] private static bool Callback_UploadLightmap(IntPtr target, int lmap, IntPtr data) { Profiler.BeginSample("UploadLightmap"); // TODO: this is a fairly pointless additional data copy step; we could probably make this faster by wrapping the IntPtr directly into a NativeArray var self = GetSelf(target); Marshal.Copy(data, self.lightmapBytes, 0, self.lightmapBytes.Length); bool result = self.UploadLightmap(lmap, QConstants.LightmapBlockWidth, QConstants.LightmapBlockHeight, self.lightmapBytes); Profiler.EndSample(); return result; } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void SetupViewCallback(IntPtr target, ref QVec3 origin, ref QVec3 angles, ref QLeaf viewLeaf); [MonoPInvokeCallback(typeof(SetupViewCallback))] private static void Callback_SetupView(IntPtr target, ref QVec3 origin, ref QVec3 angles, ref QLeaf viewLeaf) { Profiler.BeginSample("SetupView"); GetSelf(target).SetupView(origin, angles, viewLeaf); Profiler.EndSample(); } }