using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; public partial class RenderModule { private readonly UniQuake uq; private readonly List brushModels = new List(); private readonly List aliasModels = new List(); private readonly Dictionary textures = new Dictionary(); private BrushModel worldModel; public RenderModule(UniQuake uniQuake) { uq = uniQuake; BuildCallbacks(); zPos = globalZPos; globalZPos -= 256f; } public override void Destroy() { base.Destroy(); foreach (var brushModel in brushModels) { // brushModel.Dispose(); // TODO: reactivate when done testing in editor } brushModels.Clear(); worldModel = null; foreach (var aliasModel in aliasModels) { // aliasModel.Dispose(); // TODO: reactivate when done testing in editor } aliasModels.Clear(); foreach (var texture in textures.Values) { // Object.Destroy(texture); // TODO: reactivate when done testing in editor } textures.Clear(); } private float xPos = -2048f; private float zPos = 0f; private static float globalZPos = 0f; private int UploadAliasModel(string name, QAliasHeader header, QAliasFrameType frameType, QTriVertex[][] poseVertices, QTriangle[] triangles, QSTVert[] stVertices, QGLTexture[][] glTextures, QGLTexture[][] fbTextures) { var sb = new System.Text.StringBuilder(); foreach (var frame in header.frames) { sb.AppendLine($"- {frame.name} ({frame.numPoses} poses)"); } Debug.Log($"Alias model '{name}' (frame type {frameType}) with {header.numVerts} vertices, {header.numTriangles} triangles, {header.numFrames} frame(s), {header.numPoses} pose(s):\n{sb}"); string modelName = System.IO.Path.GetFileNameWithoutExtension(name); AliasModel aliasModel = new AliasModel(modelName, frameType); aliasModel.ImportMeshData(header, poseVertices, triangles, stVertices); aliasModels.Add(aliasModel); var go = new GameObject(modelName); go.transform.SetPositionAndRotation(new Vector3(xPos, 0, zPos), Quaternion.identity); aliasModel.Animate(0, out Mesh mesh, out float blendWeight); var material = new Material(Shader.Find("Universal Render Pipeline/Simple Lit")); uint texNum = glTextures[0][0].texNum; if (texNum > 0 && textures.ContainsKey(texNum)) { material.mainTexture = textures[texNum]; } Material fbMaterial = null; if (fbTextures != null && fbTextures[0] != null && fbTextures[0][0] != null) { var fbTexNum = fbTextures[0][0].texNum; if (fbTexNum > 0 && textures.ContainsKey(fbTexNum)) { fbMaterial = new Material(Shader.Find("Universal Render Pipeline/Unlit")); fbMaterial.mainTexture = textures[fbTexNum]; fbMaterial.SetFloat("_Surface", 1); // Transparent fbMaterial.SetFloat("_Blend", 2); // Additive } } if (header.numPoses > 1) { var mr = go.AddComponent(); if (fbMaterial != null) mr.materials = new[] { material, fbMaterial }; else mr.material = material; mr.sharedMesh = mesh; mr.shadowCastingMode = ShadowCastingMode.Off; mr.receiveShadows = false; mr.lightProbeUsage = LightProbeUsage.Off; mr.reflectionProbeUsage = ReflectionProbeUsage.Off; var animator = go.AddComponent(); animator.aliasModel = aliasModel; } else { var mf = go.AddComponent(); mf.sharedMesh = mesh; var mr = go.AddComponent(); if (fbMaterial != null) mr.materials = new[] { material, fbMaterial }; else mr.material = material; mr.shadowCastingMode = ShadowCastingMode.Off; mr.receiveShadows = false; mr.lightProbeUsage = LightProbeUsage.Off; mr.reflectionProbeUsage = ReflectionProbeUsage.Off; } xPos += 128f; return aliasModels.Count; } private int UploadBrushModel(QModel model) { Debug.Log($"Brush model '{model.name}' with {model.numVertices} vertices, {model.numEdges} edges, {model.numSurfaces} surfaces"); var brushModel = new BrushModel(model.name); brushModel.ImportMeshData(model); brushModels.Add(brushModel); return brushModels.Count; } private uint nextTexNum = 0x10001; private bool UploadTexture(QGLTexture texture, byte[] data, ref uint texNum) { Debug.Log($"Texture '{texture.name}' with dimensions {texture.width}x{texture.height}, data size = {data.Length} bytes"); if (texture.width == 0 || texture.height == 0) return false; if (texNum == 0) { // Assign a new texture number while (textures.ContainsKey(nextTexNum)) ++nextTexNum; texNum = nextTexNum++; } var tex = new Texture2D((int)texture.width, (int)texture.height, TextureFormat.RGBA32, texture.flags.HasFlag(QTexPrefs.Mipmap)); tex.name = texture.name; tex.SetPixelData(data, 0); tex.Apply(); textures[texNum] = tex; return true; } private void SetupView(QVec3 origin, QVec3 angles, QLeaf viewLeaf) { var cam = uq.Camera; if (cam == null) return; cam.transform.position = origin.ToVector3().ToUnity(); cam.transform.rotation = angles.ToUnityRotation(); } }