Browse Source

Some cleanup and organization:

- Removed a bunch of dead testing code
- Provide a camera to the UniQuake script which the render module can then manipulate
- Removed unused fields from QModel, which also fixes yet another text decoding issue in Zerstörer (the ö appears in the map's entity data)
- Shut down Quake engine OnApplicationQuit, which allows clean shutdown using the Play button in the Unity editor
- Use QTexture pointer as key for grouping surfaces, which removes the need for a bunch of redundant data marshaling
- Added disposal function for brush models
console
Nico de Poel 5 years ago
parent
commit
331aa4bccc
  1. 6
      Assets/Scripts/Bootstrap.cs
  2. 16
      Assets/Scripts/Data/QModel.cs
  3. 86
      Assets/Scripts/Modules/BrushModel.cs
  4. 18
      Assets/Scripts/Modules/RenderModule.cs
  5. 10
      Assets/Scripts/UniQuake.cs

6
Assets/Scripts/Bootstrap.cs

@ -23,6 +23,7 @@ public class Bootstrap : MonoBehaviour
uq.BaseGame = MissionPack.Quake; uq.BaseGame = MissionPack.Quake;
uq.ModDirectory = mod; uq.ModDirectory = mod;
uq.AdditionalArguments = ParseArgs(); uq.AdditionalArguments = ParseArgs();
uq.Camera = Camera.main; // This can be any one of four cameras for split-screen, each with its own culling layer
} }
if (GUILayout.Button("Start Scourge of Armagon!")) if (GUILayout.Button("Start Scourge of Armagon!"))
@ -31,6 +32,7 @@ public class Bootstrap : MonoBehaviour
uq.BaseGame = MissionPack.Hipnotic; uq.BaseGame = MissionPack.Hipnotic;
uq.ModDirectory = mod; uq.ModDirectory = mod;
uq.AdditionalArguments = ParseArgs(); uq.AdditionalArguments = ParseArgs();
uq.Camera = Camera.main;
} }
if (GUILayout.Button("Start Dissolution of Eternity!")) if (GUILayout.Button("Start Dissolution of Eternity!"))
@ -39,6 +41,7 @@ public class Bootstrap : MonoBehaviour
uq.BaseGame = MissionPack.Rogue; uq.BaseGame = MissionPack.Rogue;
uq.ModDirectory = mod; uq.ModDirectory = mod;
uq.AdditionalArguments = ParseArgs(); uq.AdditionalArguments = ParseArgs();
uq.Camera = Camera.main;
} }
GUILayout.Label("Mod directory:"); GUILayout.Label("Mod directory:");
@ -64,6 +67,9 @@ public class Bootstrap : MonoBehaviour
private string[] ParseArgs() private string[] ParseArgs()
{ {
if (args == null)
return new string[0];
return args.Split(new[] {' ', '\t', '\r', '\n'}, StringSplitOptions.RemoveEmptyEntries); return args.Split(new[] {' ', '\t', '\r', '\n'}, StringSplitOptions.RemoveEmptyEntries);
} }
} }

16
Assets/Scripts/Data/QModel.cs

@ -70,21 +70,7 @@ public class QModel
public int numTextures; public int numTextures;
public IntPtr textures; // Array of texture_t pointers 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 bool visWarn;
public int bspVersion;
// VBO data from QuakeSpasm; unused by UniQuake
public uint meshvbo;
public uint meshIndexesVbo;
public int vboIndexOfs;
public int vboXyzOfs;
public int vboStofs;
public IntPtr userCache;
// Rest of the fields are unused
} }
/// <summary> /// <summary>

86
Assets/Scripts/Modules/BrushModel.cs

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using UnityEngine.Rendering; using UnityEngine.Rendering;
@ -38,7 +39,7 @@ public class BrushModel
var headNode = subModels[modelIdx].GetHeadNode(model); var headNode = subModels[modelIdx].GetHeadNode(model);
var surfaceGroups = new Dictionary<(string, int), List<QSurface>>();
var surfaceGroups = new Dictionary<(IntPtr, int), List<QSurface>>();
GroupSurfaces(headNode, surfaces, surfaceGroups); GroupSurfaces(headNode, surfaces, surfaceGroups);
foreach (var group in surfaceGroups) foreach (var group in surfaceGroups)
@ -52,86 +53,38 @@ public class BrushModel
} }
} }
private void GroupSurfaces(QNode node, QSurface[] surfaces, Dictionary<(string, int), List<QSurface>> surfaceGroups)
public void Dispose()
{ {
if (node.contents < 0) // Leaf node
return;
for (int surfIdx = 0; surfIdx < node.numSurfaces; ++surfIdx)
foreach (var mesh in meshes)
{ {
var surface = surfaces[node.firstSurface + surfIdx];
string texName = surface.TextureInfo.Texture.name;
int lightNum = surface.lightmapTextureNum;
var key = (texName, lightNum);
if (!surfaceGroups.ContainsKey(key))
surfaceGroups[key] = new List<QSurface>();
surfaceGroups[key].Add(surface);
UnityEngine.Object.Destroy(mesh);
} }
foreach (var childNode in node.Children)
{
GroupSurfaces(childNode, surfaces, surfaceGroups);
}
meshes.Clear();
} }
private void CreateNodeMeshes(QNode node, QSurface[] surfaces, GameObject parentGO)
private void GroupSurfaces(QNode node, QSurface[] surfaces, Dictionary<(IntPtr, int), List<QSurface>> surfaceGroups)
{ {
if (node.contents < 0) // Leaf node if (node.contents < 0) // Leaf node
return; return;
var nodeGO = new GameObject("Node");
nodeGO.transform.SetParent(parentGO.transform);
for (int surfIdx = 0; surfIdx < node.numSurfaces; ++surfIdx) for (int surfIdx = 0; surfIdx < node.numSurfaces; ++surfIdx)
{ {
var surface = surfaces[node.firstSurface + surfIdx]; var surface = surfaces[node.firstSurface + surfIdx];
CreateSurfaceMeshes(surface, $"{node.firstSurface + surfIdx}", nodeGO);
}
foreach (var childNode in node.Children)
{
CreateNodeMeshes(childNode, surfaces, nodeGO);
}
}
IntPtr texPtr = surface.TextureInfo.texture;
int lightNum = surface.lightmapTextureNum;
var key = (texPtr, lightNum);
private void CreateSurfaceMeshes(QSurface surface, string key, GameObject nodeGO)
{
foreach (var polyVerts in surface.GetPolygons())
{
tempVertices.Clear();
tempTextureUVs.Clear();
tempLightmapUVs.Clear();
tempIndices.Clear();
if (!surfaceGroups.ContainsKey(key))
surfaceGroups[key] = new List<QSurface>();
for (int vertIdx = 0; vertIdx < polyVerts.Length; ++vertIdx)
{
tempVertices.Add(polyVerts[vertIdx].position.ToVector3().ToUnity());
tempTextureUVs.Add(polyVerts[vertIdx].textureUV.ToVector2());
tempLightmapUVs.Add(polyVerts[vertIdx].lightmapUV.ToVector2());
surfaceGroups[key].Add(surface);
} }
// Reconstruct triangle fan
for (ushort index = 2; index < polyVerts.Length; ++index)
foreach (var childNode in node.Children)
{ {
tempIndices.Add(0);
tempIndices.Add((ushort) (index - 1));
tempIndices.Add(index);
}
Mesh mesh = new Mesh();
mesh.name = $"Surface_{key}";
mesh.SetVertices(tempVertices);
mesh.SetUVs(0, tempTextureUVs);
mesh.SetUVs(1, tempLightmapUVs);
mesh.SetIndices(tempIndices, MeshTopology.Triangles, 0);
mesh.RecalculateNormals();
mesh.UploadMeshData(true);
meshes.Add(mesh);
CreateMeshObject(mesh, nodeGO);
GroupSurfaces(childNode, surfaces, surfaceGroups);
} }
} }
@ -182,13 +135,10 @@ public class BrushModel
private void CreateMeshObject(Mesh mesh, GameObject parentGO) private void CreateMeshObject(Mesh mesh, GameObject parentGO)
{ {
var meshGO = new GameObject(mesh.name);
meshGO.transform.SetParent(parentGO.transform);
var mf = meshGO.AddComponent<MeshFilter>();
var mf = parentGO.AddComponent<MeshFilter>();
mf.sharedMesh = mesh; mf.sharedMesh = mesh;
var mr = meshGO.AddComponent<MeshRenderer>();
var mr = parentGO.AddComponent<MeshRenderer>();
mr.sharedMaterial = debugMaterial; mr.sharedMaterial = debugMaterial;
mr.shadowCastingMode = ShadowCastingMode.Off; mr.shadowCastingMode = ShadowCastingMode.Off;
mr.receiveShadows = false; mr.receiveShadows = false;

18
Assets/Scripts/Modules/RenderModule.cs

@ -5,9 +5,12 @@ using UnityEngine.Rendering;
public partial class RenderModule public partial class RenderModule
{ {
private readonly UniQuake uq; private readonly UniQuake uq;
private readonly List<BrushModel> brushModels = new List<BrushModel>();
private readonly List<AliasModel> aliasModels = new List<AliasModel>(); private readonly List<AliasModel> aliasModels = new List<AliasModel>();
private readonly Dictionary<uint, Texture2D> textures = new Dictionary<uint, Texture2D>(); private readonly Dictionary<uint, Texture2D> textures = new Dictionary<uint, Texture2D>();
private BrushModel worldModel;
public RenderModule(UniQuake uniQuake) public RenderModule(UniQuake uniQuake)
{ {
uq = uniQuake; uq = uniQuake;
@ -21,6 +24,14 @@ public partial class RenderModule
{ {
base.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) foreach (var aliasModel in aliasModels)
{ {
// aliasModel.Dispose(); // TODO: reactivate when done testing in editor // aliasModel.Dispose(); // TODO: reactivate when done testing in editor
@ -116,7 +127,7 @@ public partial class RenderModule
} }
xPos += 128f; xPos += 128f;
return 1;
return aliasModels.Count;
} }
private int UploadBrushModel(QModel model) private int UploadBrushModel(QModel model)
@ -126,7 +137,8 @@ public partial class RenderModule
var brushModel = new BrushModel(model.name); var brushModel = new BrushModel(model.name);
brushModel.ImportMeshData(model); brushModel.ImportMeshData(model);
return 1;
brushModels.Add(brushModel);
return brushModels.Count;
} }
private uint nextTexNum = 0x10001; private uint nextTexNum = 0x10001;
@ -158,7 +170,7 @@ public partial class RenderModule
private void SetupView(QVec3 origin, QVec3 angles, QLeaf viewLeaf) private void SetupView(QVec3 origin, QVec3 angles, QLeaf viewLeaf)
{ {
var cam = Camera.main;
var cam = uq.Camera;
if (cam == null) if (cam == null)
return; return;

10
Assets/Scripts/UniQuake.cs

@ -16,6 +16,11 @@ public partial class UniQuake: MonoBehaviour
private bool initialized = false; private bool initialized = false;
private double startTime; private double startTime;
/// <summary>
/// Camera and viewport that this instance of Quake will be rendering to
/// </summary>
public Camera Camera { get; set; }
public MissionPack BaseGame { get; set; } public MissionPack BaseGame { get; set; }
public string ModDirectory { get; set; } public string ModDirectory { get; set; }
public string[] AdditionalArguments { get; set; } public string[] AdditionalArguments { get; set; }
@ -138,6 +143,11 @@ public partial class UniQuake: MonoBehaviour
Destroy(this); Destroy(this);
} }
private void OnApplicationQuit()
{
Shutdown();
}
private void OnDestroy() private void OnDestroy()
{ {
renderModule.Destroy(); renderModule.Destroy();

Loading…
Cancel
Save