Example #1
0
        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);
        }
Example #2
0
        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));
        }
Example #3
0
        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));
        }
Example #4
0
        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();
        }
Example #5
0
        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());
 }
Example #7
0
        /// <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);
            }
        }
Example #8
0
        /// <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);
            }
        }
Example #9
0
        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);
        }
Example #10
0
        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);
        }
Example #11
0
        /// <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;
        }
Example #12
0
        /// <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;
        }
Example #13
0
        /// <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;
        }
Example #14
0
        /// <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);
            }
        }
Example #15
0
        /// <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;
        }