private List <string> GetLANDTextureFilePaths(LANDRecord LAND) { // Don't return anything if the LAND doesn't have height data or texture data. if ((LAND.VHGT == null) || (LAND.VTEX == null)) { return(null); } var textureFilePaths = new List <string>(); var distinctTextureIndices = LAND.VTEX.textureIndices.Distinct().ToList(); for (int i = 0; i < distinctTextureIndices.Count; i++) { short textureIndex = (short)((short)distinctTextureIndices[i] - 1); if (textureIndex < 0) { textureFilePaths.Add(defaultLandTextureFilePath); continue; } else { var LTEX = dataReader.FindLTEXRecord(textureIndex); var textureFilePath = LTEX.DATA.value; textureFilePaths.Add(textureFilePath); } } return(textureFilePaths); }
public InRangeCellInfo StartInstantiatingCell(CELLRecord CELL) { Debug.Assert(CELL != null); string cellObjName = null; LANDRecord LAND = null; if (!CELL.isInterior) { cellObjName = "cell " + CELL.gridCoords.ToString(); LAND = dataReader.FindLANDRecord(CELL.gridCoords); } else { cellObjName = CELL.NAME.value; } var cellObj = new GameObject(cellObjName); cellObj.tag = "Cell"; var cellObjectsContainer = new GameObject("objects"); cellObjectsContainer.transform.parent = cellObj.transform; var cellObjectsCreationCoroutine = InstantiateCellObjectsCoroutine(CELL, LAND, cellObj, cellObjectsContainer); temporalLoadBalancer.AddTask(cellObjectsCreationCoroutine); return(new InRangeCellInfo(cellObj, cellObjectsContainer, CELL, cellObjectsCreationCoroutine)); }
public InRangeCellInfo StartInstantiatingCell(CELLRecord cell) { Debug.Assert(cell != null); string cellObjName = null; LANDRecord land = null; if (!cell.IsInterior) { cellObjName = string.Format("cell-{0:00}x{1:00}", cell.GridId.x, cell.GridId.y); land = _data.FindLANDRecord(cell.GridId); } else { cellObjName = cell.Name; } var cellObj = new GameObject(cellObjName) { tag = "Cell" }; var cellObjectsContainer = new GameObject("objects"); cellObjectsContainer.transform.parent = cellObj.transform; var cellObjectsCreationCoroutine = InstantiateCellObjectsCoroutine(cell, land, cellObj, cellObjectsContainer); _loadBalancer.AddTask(cellObjectsCreationCoroutine); return(new InRangeCellInfo(cellObj, cellObjectsContainer, cell, cellObjectsCreationCoroutine)); }
private void ReadRecords() { var recordList = new List <Record>(); // add items #if false for (short itemId = 0; itemId < TileData.LandData.Length; itemId++) { recordList.Add(new STATRecord(true, itemId)); } #endif for (short itemId = 0; itemId < TileData.ItemData.Length; itemId++) { recordList.Add(new STATRecord(false, itemId)); } // add tiles for (uint chunkY = 0; chunkY < _tileData.ChunkHeight / CELL_PACK; chunkY++) { for (uint chunkX = 0; chunkX < _tileData.ChunkWidth / CELL_PACK; chunkX++) { var land = new LANDRecord(_tileData, chunkX, chunkY); recordList.Add(land); recordList.Add(new CELLRecord(_tileData, land, chunkX, chunkY)); } } records = recordList.ToArray(); }
void ReadGroup(Header header, bool loadAll, StreamSink.DataInfo info) { if (GroupByLabel == null) { GroupByLabel = new Dictionary <string, RecordGroup>(); } if (!GroupByLabel.TryGetValue(header.Label, out var group)) { GroupByLabel.Add(header.Label, group = new RecordGroup(_streamSink, FilePath)); } else { group = new RecordGroup(_streamSink, FilePath) { Next = group } }; if (_r[0] != null) { using (_r[0]) CELLRecord.ReadFixed(_r[0], header, group); } if (_r[1] != null) { using (_r[1]) LANDRecord.ReadTerrain(_r[1], header, group); } //if (_r[2] != null) using (_r[2]) DYNARecord.ReadNonfixed(_r[2], header, group); } }
List <string> GetLANDTextureFilePaths(LANDRecord land) { // Don't return anything if the LAND doesn't have height data or texture data. if (land.VTEX == null) { return(null); } return(land.VTEX.Distinct().Select(x => $"bitmap/{x}").ToList()); }
/// <summary> /// A coroutine that instantiates the terrain for, and all objects in, a cell. /// </summary> private IEnumerator InstantiateCellObjectsCoroutine(CELLRecord CELL, LANDRecord LAND, GameObject cellObj, GameObject cellObjectsContainer) { // Start pre-loading all required textures for the terrain. if (LAND != null) { var landTextureFilePaths = GetLANDTextureFilePaths(LAND); if (landTextureFilePaths != null) { foreach (var landTextureFilePath in landTextureFilePaths) { textureManager.PreloadTextureFileAsync(landTextureFilePath); } } yield return(null); } // Extract information about referenced objects. var refCellObjInfos = GetRefCellObjInfos(CELL); yield return(null); // Start pre-loading all required files for referenced objects. The NIF manager will load the textures as well. foreach (var refCellObjInfo in refCellObjInfos) { if (refCellObjInfo.modelFilePath != null) { nifManager.PreloadNifFileAsync(refCellObjInfo.modelFilePath); } } yield return(null); // Instantiate terrain. if (LAND != null) { var instantiateLANDTaskEnumerator = InstantiateLANDCoroutine(LAND, cellObj); // Run the LAND instantiation coroutine. while (instantiateLANDTaskEnumerator.MoveNext()) { // Yield every time InstantiateLANDCoroutine does to avoid doing too much work in one frame. yield return(null); } // Yield after InstantiateLANDCoroutine has finished to avoid doing too much work in one frame. yield return(null); } // Instantiate objects. foreach (var refCellObjInfo in refCellObjInfos) { InstantiateCellObject(CELL, cellObjectsContainer, refCellObjInfo); yield return(null); } }
/// <summary> /// A coroutine that instantiates the terrain for, and all objects in, a cell. /// </summary> IEnumerator InstantiateCellObjectsCoroutine(CELLRecord cell, LANDRecord land, GameObject cellObj, GameObject cellObjectsContainer) { cell.Read(); // Start pre-loading all required textures for the terrain. if (land != null) { land.Read(); var landTextureFilePaths = GetLANDTextureFilePaths(land); if (landTextureFilePaths != null) { foreach (var landTextureFilePath in landTextureFilePaths) { _assetPack.PreloadTextureTask(landTextureFilePath); } } yield return(null); } // Extract information about referenced objects. var refCellObjInfos = GetRefCellObjInfos(cell); yield return(null); // Start pre-loading all required files for referenced objects. The NIF manager will load the textures as well. foreach (var refCellObjInfo in refCellObjInfos) { if (refCellObjInfo.ModelFilePath != null) { _assetPack.PreloadObjectTask(refCellObjInfo.ModelFilePath); } } yield return(null); // Instantiate terrain. if (land != null) { var instantiateLANDTaskEnumerator = InstantiateLANDCoroutine(land, cellObj); // Run the LAND instantiation coroutine. while (instantiateLANDTaskEnumerator.MoveNext()) { // Yield every time InstantiateLANDCoroutine does to avoid doing too much work in one frame. yield return(null); } // Yield after InstantiateLANDCoroutine has finished to avoid doing too much work in one frame. yield return(null); } // Instantiate objects. foreach (var refCellObjInfo in refCellObjInfos) { InstantiateCellObject(cell, cellObjectsContainer, refCellObjInfo); yield return(null); } }
List <string> GetLANDTextureFilePaths(LANDRecord land) { // Don't return anything if the LAND doesn't have data. if (land.Tiles == null) { return(null); } var textureFilePaths = new List <string>(); var distinctTextureIds = land.Tiles.Select(x => x.TextureId).Distinct().ToList(); for (var i = 0; i < distinctTextureIds.Count; i++) { var textureIndex = distinctTextureIds[i]; textureFilePaths.Add($"tex{textureIndex}"); } return(textureFilePaths); }
List <string> GetLANDTextureFilePaths(LANDRecord land) { // Don't return anything if the LAND doesn't have height data or texture data. if (land.VTEX == null) { return(null); } var textureFilePaths = new List <string>(); var distinctTextureIndices = land.VTEX.Value.TextureIndicesT3.Distinct().ToList(); for (var i = 0; i < distinctTextureIndices.Count; i++) { var textureIndex = ((short)distinctTextureIndices[i] - 1); if (textureIndex < 0) { textureFilePaths.Add(_defaultLandTextureFilePath); continue; } var ltex = _data.FindLTEXRecord(textureIndex); var textureFilePath = ltex.ICON.Value; textureFilePaths.Add(textureFilePath); } return(textureFilePaths); }
/// <summary> /// Creates terrain representing a LAND record. /// </summary> private IEnumerator InstantiateLANDCoroutine(LANDRecord LAND, GameObject parent) { Debug.Assert(LAND != null); // Don't create anything if the LAND doesn't have height data. if (LAND.VHGT == null) { yield break; } // Return before doing any work to provide an IEnumerator handle to the coroutine. yield return(null); const int LAND_SIDE_LENGTH_IN_SAMPLES = 65; var heights = new float[LAND_SIDE_LENGTH_IN_SAMPLES, LAND_SIDE_LENGTH_IN_SAMPLES]; // Read in the heights in Morrowind units. const int VHGTIncrementToMWUnits = 8; float rowOffset = LAND.VHGT.referenceHeight; for (int y = 0; y < LAND_SIDE_LENGTH_IN_SAMPLES; y++) { rowOffset += LAND.VHGT.heightOffsets[y * LAND_SIDE_LENGTH_IN_SAMPLES]; heights[y, 0] = VHGTIncrementToMWUnits * rowOffset; float colOffset = rowOffset; for (int x = 1; x < LAND_SIDE_LENGTH_IN_SAMPLES; x++) { colOffset += LAND.VHGT.heightOffsets[(y * LAND_SIDE_LENGTH_IN_SAMPLES) + x]; heights[y, x] = VHGTIncrementToMWUnits * colOffset; } } // Change the heights to percentages. float minHeight, maxHeight; ArrayUtils.GetExtrema(heights, out minHeight, out maxHeight); for (int y = 0; y < LAND_SIDE_LENGTH_IN_SAMPLES; y++) { for (int x = 0; x < LAND_SIDE_LENGTH_IN_SAMPLES; x++) { heights[y, x] = Utils.ChangeRange(heights[y, x], minHeight, maxHeight, 0, 1); } } // Texture the terrain. SplatPrototype[] splatPrototypes = null; float[,,] alphaMap = null; const int LAND_TEXTURE_INDICES_COUNT = 256; var textureIndices = (LAND.VTEX != null) ? LAND.VTEX.textureIndices : new ushort[LAND_TEXTURE_INDICES_COUNT]; // Create splat prototypes. var splatPrototypeList = new List <SplatPrototype>(); var texInd2splatInd = new Dictionary <ushort, int>(); for (int i = 0; i < textureIndices.Length; i++) { short textureIndex = (short)((short)textureIndices[i] - 1); if (!texInd2splatInd.ContainsKey((ushort)textureIndex)) { // Load terrain texture. string textureFilePath; if (textureIndex < 0) { textureFilePath = defaultLandTextureFilePath; } else { var LTEX = dataReader.FindLTEXRecord(textureIndex); textureFilePath = LTEX.DATA.value; } var texture = textureManager.LoadTexture(textureFilePath); // Yield after loading each texture to avoid doing too much work on one frame. yield return(null); // Create the splat prototype. var splat = new SplatPrototype(); splat.texture = texture; splat.smoothness = 0; splat.metallic = 0; splat.tileSize = new Vector2(6, 6); // Update collections. var splatIndex = splatPrototypeList.Count; splatPrototypeList.Add(splat); texInd2splatInd.Add((ushort)textureIndex, splatIndex); } } splatPrototypes = splatPrototypeList.ToArray(); // Create the alpha map. int VTEX_ROWS = 16; int VTEX_COLUMNS = VTEX_ROWS; alphaMap = new float[VTEX_ROWS, VTEX_COLUMNS, splatPrototypes.Length]; for (int y = 0; y < VTEX_ROWS; y++) { var yMajor = y / 4; var yMinor = y - (yMajor * 4); for (int x = 0; x < VTEX_COLUMNS; x++) { var xMajor = x / 4; var xMinor = x - (xMajor * 4); var texIndex = (short)((short)textureIndices[(yMajor * 64) + (xMajor * 16) + (yMinor * 4) + xMinor] - 1); if (texIndex >= 0) { var splatIndex = texInd2splatInd[(ushort)texIndex]; alphaMap[y, x, splatIndex] = 1; } else { alphaMap[y, x, 0] = 1; } } } // Yield before creating the terrain GameObject because it takes a while. yield return(null); // Create the terrain. var heightRange = maxHeight - minHeight; var terrainPosition = new Vector3(Convert.exteriorCellSideLengthInMeters * LAND.gridCoords.x, minHeight / Convert.meterInMWUnits, Convert.exteriorCellSideLengthInMeters * LAND.gridCoords.y); var heightSampleDistance = Convert.exteriorCellSideLengthInMeters / (LAND_SIDE_LENGTH_IN_SAMPLES - 1); var terrain = GameObjectUtils.CreateTerrain(heights, heightRange / Convert.meterInMWUnits, heightSampleDistance, splatPrototypes, alphaMap, terrainPosition); terrain.GetComponent <Terrain>().materialType = Terrain.MaterialType.BuiltInLegacyDiffuse; terrain.transform.parent = parent.transform; }
/// <summary> /// Creates terrain representing a LAND record. /// </summary> IEnumerator InstantiateLANDCoroutine(LANDRecord land, GameObject parent) { Debug.Assert(land != null); // Don't create anything if the LAND doesn't have data. if (land.Heights == null || land.Tiles == null) { yield break; } // Return before doing any work to provide an IEnumerator handle to the coroutine. yield return(null); const int LAND_SIDELENGTH = 8 * DataFile.CELL_PACK; var heights = new float[LAND_SIDELENGTH, LAND_SIDELENGTH]; // Read in the heights in units. const int VHGTIncrementToUnits = 1; for (var y = 0; y < LAND_SIDELENGTH; y++) { for (var x = 0; x < LAND_SIDELENGTH; x++) { var height = land.Heights[(y * LAND_SIDELENGTH) + x]; heights[y, x] = height * VHGTIncrementToUnits; } } // Change the heights to percentages. heights.GetExtrema(out float minHeight, out float maxHeight); for (var y = 0; y < LAND_SIDELENGTH; y++) { for (var x = 0; x < LAND_SIDELENGTH; x++) { heights[y, x] = Utils.ChangeRange(heights[y, x], minHeight, maxHeight, 0, 1); } } // Texture the terrain. SplatPrototype[] splatPrototypes = null; float[,,] alphaMap = null; var textureIndices = land.Tiles.Select(x => x.TextureId).ToArray(); // Create splat prototypes. var splatPrototypeList = new List <SplatPrototype>(); var texInd2SplatInd = new Dictionary <short, int>(); for (var i = 0; i < textureIndices.Length; i++) { var textureIndex = textureIndices[i]; if (!texInd2SplatInd.ContainsKey(textureIndex)) { // Load terrain texture. var textureFilePath = $"tex{textureIndex}"; var texture = _asset.LoadTexture(textureFilePath); // Yield after loading each texture to avoid doing too much work on one frame. yield return(null); // Create the splat prototype. var splat = new SplatPrototype { texture = texture, smoothness = 0, metallic = 0, tileSize = new Vector2(6, 6) }; // Update collections. var splatIndex = splatPrototypeList.Count; splatPrototypeList.Add(splat); texInd2SplatInd.Add(textureIndex, splatIndex); } } splatPrototypes = splatPrototypeList.ToArray(); // Create the alpha map. var VTEX_ROWS = 16; var VTEX_COLUMNS = VTEX_ROWS; alphaMap = new float[VTEX_ROWS, VTEX_COLUMNS, splatPrototypes.Length]; for (var y = 0; y < VTEX_ROWS; y++) { var yMajor = y / 4; var yMinor = y - (yMajor * 4); for (var x = 0; x < VTEX_COLUMNS; x++) { var xMajor = x / 4; var xMinor = x - (xMajor * 4); var texIndex = textureIndices[(yMajor * 64) + (xMajor * 16) + (yMinor * 4) + xMinor]; if (texIndex >= 0) { var splatIndex = texInd2SplatInd[texIndex]; alphaMap[y, x, splatIndex] = 1; } else { alphaMap[y, x, 0] = 1; } } } // Yield before creating the terrain GameObject because it takes a while. yield return(null); // Create the terrain. var heightRange = maxHeight - minHeight; var terrainPosition = new Vector3(ConvertUtils.ExteriorCellSideLengthInMeters * land.GridId.x, minHeight / ConvertUtils.MeterInUnits, ConvertUtils.ExteriorCellSideLengthInMeters * land.GridId.y); var heightSampleDistance = ConvertUtils.ExteriorCellSideLengthInMeters / LAND_SIDELENGTH; var terrain = GameObjectUtils.CreateTerrain(0, heights, heightRange / ConvertUtils.MeterInUnits, heightSampleDistance, splatPrototypes, alphaMap, terrainPosition); terrain.GetComponent <Terrain>().materialType = Terrain.MaterialType.BuiltInLegacyDiffuse; terrain.transform.parent = parent.transform; terrain.isStatic = true; }
/// <summary> /// Creates terrain representing a LAND record. /// </summary> IEnumerator InstantiateLANDCoroutine(LANDRecord land, GameObject parent) { Debug.Assert(land != null); // Don't create anything if the LAND doesn't have height data. if (land.VHGT.HeightData == null) { yield break; } // Return before doing any work to provide an IEnumerator handle to the coroutine. yield return(null); const int LAND_SIDELENGTH_IN_SAMPLES = 65; var heights = new float[LAND_SIDELENGTH_IN_SAMPLES, LAND_SIDELENGTH_IN_SAMPLES]; // Read in the heights in Morrowind units. const int VHGTIncrementToUnits = 8; var rowOffset = land.VHGT.ReferenceHeight; for (var y = 0; y < LAND_SIDELENGTH_IN_SAMPLES; y++) { rowOffset += land.VHGT.HeightData[y * LAND_SIDELENGTH_IN_SAMPLES]; heights[y, 0] = rowOffset * VHGTIncrementToUnits; var colOffset = rowOffset; for (var x = 1; x < LAND_SIDELENGTH_IN_SAMPLES; x++) { colOffset += land.VHGT.HeightData[(y * LAND_SIDELENGTH_IN_SAMPLES) + x]; heights[y, x] = colOffset * VHGTIncrementToUnits; } } // Change the heights to percentages. heights.GetExtrema(out var minHeight, out var maxHeight); for (var y = 0; y < LAND_SIDELENGTH_IN_SAMPLES; y++) { for (var x = 0; x < LAND_SIDELENGTH_IN_SAMPLES; x++) { heights[y, x] = Utils.ChangeRange(heights[y, x], minHeight, maxHeight, 0, 1); } } // Texture the terrain. SplatPrototype[] splatPrototypes = null; const int LAND_TEXTUREINDICES = 256; var textureIndices = land.VTEX != null ? land.VTEX.Value.TextureIndicesT3 : new ushort[LAND_TEXTUREINDICES]; // Create splat prototypes. var splatPrototypeList = new List <SplatPrototype>(); var texInd2SplatInd = new Dictionary <ushort, int>(); for (var i = 0; i < textureIndices.Length; i++) { var textureIndex = (int)(textureIndices[i] - 1); if (!texInd2SplatInd.ContainsKey((ushort)textureIndex)) { // Load terrain texture. string textureFilePath; if (textureIndex < 0) { textureFilePath = _defaultLandTextureFilePath; } else { var LTEX = _data.FindLTEXRecord(textureIndex); textureFilePath = LTEX.ICON.Value; } var texture = _asset.LoadTexture(textureFilePath); // Yield after loading each texture to avoid doing too much work on one frame. yield return(null); // Create the splat prototype. var splat = new SplatPrototype { texture = texture, smoothness = 0, metallic = 0, tileSize = new Vector2(6, 6) }; // Update collections. var splatIndex = splatPrototypeList.Count; splatPrototypeList.Add(splat); texInd2SplatInd.Add((ushort)textureIndex, splatIndex); } } splatPrototypes = splatPrototypeList.ToArray(); // Create the alpha map. var VTEX_ROWS = 16; var VTEX_COLUMNS = VTEX_ROWS; float[,,] alphaMap = null; alphaMap = new float[VTEX_ROWS, VTEX_COLUMNS, splatPrototypes.Length]; for (var y = 0; y < VTEX_ROWS; y++) { var yMajor = y / 4; var yMinor = y - (yMajor * 4); for (var x = 0; x < VTEX_COLUMNS; x++) { var xMajor = x / 4; var xMinor = x - (xMajor * 4); var texIndex = ((short)textureIndices[(yMajor * 64) + (xMajor * 16) + (yMinor * 4) + xMinor] - 1); if (texIndex >= 0) { var splatIndex = texInd2SplatInd[(ushort)texIndex]; alphaMap[y, x, splatIndex] = 1; } else { alphaMap[y, x, 0] = 1; } } } // Yield before creating the terrain GameObject because it takes a while. yield return(null); // Create the terrain. var heightRange = maxHeight - minHeight; var terrainPosition = new Vector3(ConvertUtils.ExteriorCellSideLengthInMeters * land.GridId.x, minHeight / ConvertUtils.MeterInUnits, ConvertUtils.ExteriorCellSideLengthInMeters * land.GridId.y); var heightSampleDistance = ConvertUtils.ExteriorCellSideLengthInMeters / (LAND_SIDELENGTH_IN_SAMPLES - 1); var terrain = GameObjectUtils.CreateTerrain(-1, heights, heightRange / ConvertUtils.MeterInUnits, heightSampleDistance, splatPrototypes, alphaMap, terrainPosition); terrain.GetComponent <Terrain>().materialType = Terrain.MaterialType.BuiltInLegacyDiffuse; terrain.transform.parent = parent.transform; terrain.isStatic = true; }
/// <summary> /// A coroutine that instantiates the terrain for, and all objects in, a cell. /// </summary> private IEnumerator InstantiateCellObjectsCoroutine(CELLRecord CELL, LANDRecord LAND, GameObject cellObj, GameObject cellObjectsContainer) { // Return before doing any work to provide an IEnumerator handle to the coroutine. yield return(null); // Instantiate terrain. if (LAND != null) { var instantiateLANDTaskEnumerator = InstantiateLANDCoroutine(LAND, cellObj); // Run the LAND instantiation coroutine. while (instantiateLANDTaskEnumerator.MoveNext()) { // Yield every time InstantiateLANDCoroutine does to avoid doing too much work in one frame. yield return(null); } // Yield after InstantiateLANDCoroutine has finished to avoid doing too much work in one frame. yield return(null); } // Extract information about referenced objects. Do this all at once because it's fast. RefCellObjInfo[] refCellObjInfos = new RefCellObjInfo[CELL.refObjDataGroups.Count]; for (int i = 0; i < CELL.refObjDataGroups.Count; i++) { var refObjInfo = new RefCellObjInfo(); refObjInfo.refObjDataGroup = CELL.refObjDataGroups[i]; // Get the record the RefObjDataGroup references. dataReader.MorrowindESMFile.objectsByIDString.TryGetValue(refObjInfo.refObjDataGroup.NAME.value, out refObjInfo.referencedRecord); if (refObjInfo.referencedRecord != null) { var modelFileName = ESM.RecordUtils.GetModelFileName(refObjInfo.referencedRecord); // If the model file name is valid, store the model file path. if ((modelFileName != null) && (modelFileName != "")) { refObjInfo.modelFilePath = "meshes\\" + modelFileName; } } refCellObjInfos[i] = refObjInfo; } yield return(null); // Start loading all required assets in background threads. foreach (var refCellObjInfo in refCellObjInfos) { if (refCellObjInfo.modelFilePath != null) { theNIFManager.PreLoadNIFAsync(refCellObjInfo.modelFilePath); yield return(null); } } yield return(null); // Instantiate objects when they are done loading, or if they don't need to load. int instantiatedObjectCount = 0; while (instantiatedObjectCount < refCellObjInfos.Length) { foreach (var refCellObjInfo in refCellObjInfos) { // Ignore objects that have already been instantiated. if (refCellObjInfo.isInstantiated) { continue; } // If the referenced object has a model and it has just finished pre-loading, instantiate the model. if (refCellObjInfo.modelFilePath != null) { Debug.Assert(!refCellObjInfo.isDonePreLoading); // Update isDonePreloading. refCellObjInfo.isDonePreLoading = theNIFManager.IsDonePreLoading(refCellObjInfo.modelFilePath); // If the model just finished pre-loading, instantiate it. if (refCellObjInfo.isDonePreLoading) { InstantiatePreLoadedCellObject(CELL, cellObjectsContainer, refCellObjInfo); refCellObjInfo.isInstantiated = true; instantiatedObjectCount++; yield return(null); } } else // If the referenced object doesn't have a model, there is no loading to be done, so try to instantiate it. { InstantiatePreLoadedCellObject(CELL, cellObjectsContainer, refCellObjInfo); refCellObjInfo.isInstantiated = true; instantiatedObjectCount++; yield return(null); } } // Yield after every foreach to prevent spinning if all models are loading. yield return(null); } }
/// <summary> /// Creates terrain representing a LAND record. /// </summary> IEnumerator InstantiateLANDCoroutine(LANDRecord land, GameObject parent) { Assert(land != null); // Don't create anything if the LAND doesn't have height data. if (land.VHGT == null) { yield break; } // Return before doing any work to provide an IEnumerator handle to the coroutine. yield return(null); const int LAND_STRIDE = 64; var heights = new float[LAND_STRIDE, LAND_STRIDE]; // Read in the heights in Unity units. const int VHGTIncrementToUnits = 8; for (var y = 0; y < LAND_STRIDE; y++) { for (var x = 0; x < LAND_STRIDE; x++) { var vhgt = land.VHGT[(y * LAND_STRIDE) + x]; heights[y, x] = vhgt * VHGTIncrementToUnits; } } // Change the heights to percentages. heights.GetExtrema(out var minHeight, out var maxHeight); for (var y = 0; y < LAND_STRIDE; y++) { for (var x = 0; x < LAND_STRIDE; x++) { heights[y, x] = Utils.ChangeRange(heights[y, x], minHeight, maxHeight, 0, 1); } } // Texture the terrain. TerrainLayer[] terrainLayers = null; var textureIndices = land.VTEX ?? new ushort[LAND_STRIDE * LAND_STRIDE]; // Create splat prototypes. var terrainLayerList = new List <TerrainLayer>(); var texInd2SplatInd = new Dictionary <ushort, int>(); for (var i = 0; i < textureIndices.Length; i++) { var textureIndex = textureIndices[i]; if (!texInd2SplatInd.ContainsKey(textureIndex)) { // Load terrain texture. var texture = _assetPack.LoadTexture($"bitmap/{textureIndex}"); // Yield after loading each texture to avoid doing too much work on one frame. yield return(null); // Create the splat prototype. var layer = new TerrainLayer { diffuseTexture = texture, smoothness = 0, metallic = 0, tileSize = new Vector2(16, 16) }; // Update collections. var splatIndex = terrainLayerList.Count; terrainLayerList.Add(layer); texInd2SplatInd.Add(textureIndex, splatIndex); } } terrainLayers = terrainLayerList.ToArray(); // Create the alpha map. var alphaMap = new float[LAND_STRIDE, LAND_STRIDE, terrainLayers.Length]; for (var y = 0; y < LAND_STRIDE; y++) { for (var x = 0; x < LAND_STRIDE; x++) { var texIndex = textureIndices[x + y * LAND_STRIDE]; if (texIndex >= 0) { alphaMap[y, x, texInd2SplatInd[texIndex]] = 1; } else { alphaMap[y, x, 0] = 1; } } } // Yield before creating the terrain GameObject because it takes a while. yield return(null); // Create the terrain. var heightRange = maxHeight - minHeight; var terrainPosition = new Vector3(ConvertUtils.ExteriorCellSideLengthInMeters * land.GridId.x, minHeight / ConvertUtils.MeterInUnits, ConvertUtils.ExteriorCellSideLengthInMeters * land.GridId.y); var heightSampleDistance = ConvertUtils.ExteriorCellSideLengthInMeters / (LAND_STRIDE - 1); var terrain = GameObjectUtils.CreateTerrain(-1, heights, heightRange / ConvertUtils.MeterInUnits, heightSampleDistance, terrainLayers, alphaMap, terrainPosition, null); terrain.GetComponent <Terrain>().materialType = Terrain.MaterialType.BuiltInLegacyDiffuse; terrain.transform.parent = parent.transform; terrain.isStatic = true; }