using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; public class GameState { private readonly UniQuake uq; private GameObject worldGameObject; private readonly Dictionary entities = new Dictionary(); public GameState(UniQuake uniQuake) { uq = uniQuake; } public void Destroy() { DestroyEntities(); DestroyWorld(); } public void NewMap(BrushModel worldModel) { Destroy(); Resources.UnloadUnusedAssets(); worldGameObject = new GameObject(worldModel.Name); // The first sub-model contains all of the static geometry var subModel = worldModel.GetSubModel(0); var subModelGO = CreateBrushGameObject(subModel); subModelGO.transform.SetParent(worldGameObject.transform); } private GameObject CreateBrushGameObject(BrushModel.SubModel subModel) { var subModelGO = new GameObject(subModel.Name); foreach (var surfaceMesh in subModel.SurfaceMeshes) { var meshGO = new GameObject(surfaceMesh.Mesh.name); meshGO.transform.SetParent(subModelGO.transform); var mf = meshGO.AddComponent(); var mr = meshGO.AddComponent(); mf.sharedMesh = surfaceMesh.Mesh; // TODO FIXME This is wrong for brush model entities uq.CurrentStyle.SetupWorldModelRenderer(mr); // TODO FIXME this currently leaks Materials uint texNum = surfaceMesh.Texture.TextureNum; if (uq.GameAssets.TryGetTexture(texNum, out var texture)) { uint fbNum = surfaceMesh.Texture.FullBrightNum; uq.GameAssets.TryGetTexture(fbNum, out var fullBright); uq.CurrentStyle.SetWorldModelTextures(mr.material, texture, fullBright, null); } } return subModelGO; } private void DestroyWorld() { if (worldGameObject != null) { Object.Destroy(worldGameObject); worldGameObject = null; } } public Entity GetEntity(int entityNum) { if (!entities.TryGetValue(entityNum, out var entity)) { entity = new Entity(entityNum, uq.CurrentStyle); entities.Add(entityNum, entity); } return entity; } public void SetEntityAliasModel(int entityNum, string modelName) { var entity = GetEntity(entityNum); if (!uq.GameAssets.TryGetAliasModel(modelName, out var aliasModel)) { Debug.LogWarning($"Unknown alias model name: {modelName}"); return; } entity.SetAliasModel(aliasModel); } public void SetEntityBrushModel(int entityNum, string modelName) { var entity = GetEntity(entityNum); if (!uq.GameAssets.TryGetBrushModel(modelName, out var brushModel)) { Debug.LogWarning($"Unknown brush model name: {modelName}"); return; } var brushModelGO = new GameObject(brushModel.Name); for (int i = 0; i < brushModel.SubModelCount; ++i) { var subModelGO = CreateBrushGameObject(brushModel.GetSubModel(i)); subModelGO.transform.SetParent(brushModelGO.transform); } entity.SetBrushModel(brushModelGO); } public void SetEntityWorldModel(int entityNum, int subModelNum) { var entity = GetEntity(entityNum); if (!uq.GameAssets.TryGetWorldSubModel(subModelNum, out var subModel)) { Debug.LogWarning($"Invalid world sub-model number: {subModelNum}"); return; } // TODO: these relatively complex world game objects are going to get destroyed and re-created all the time // as the player moves through the map and moves in and out of range of these entities. This can and should // be done more efficiently by creating the game objects only once and enabling/disabling them on demand. var worldModelGO = CreateBrushGameObject(subModel); entity.SetWorldModel(worldModelGO); } public void RemoveEntity(int entityNum) { if (entities.TryGetValue(entityNum, out var entity)) { entity.Destroy(); entities.Remove(entityNum); } } private void DestroyEntities() { foreach (var entity in entities.Values) { entity.Destroy(); } entities.Clear(); } }