/// <summary> /// Performs a fully standalone in-place location layout. /// This method is used only by editor layouts, not by streaming world. /// </summary> private void LayoutLocation(ref DFLocation location) { #if SHOW_LAYOUT_TIMES // Start timing System.Diagnostics.Stopwatch stopwatch = System.Diagnostics.Stopwatch.StartNew(); long startTime = stopwatch.ElapsedMilliseconds; #endif // Get city dimensions int width = location.Exterior.ExteriorData.Width; int height = location.Exterior.ExteriorData.Height; // Create billboard batch game objects for this location //TextureAtlasBuilder miscBillboardAtlas = null; summary.NatureBillboardBatch = null; DaggerfallBillboardBatch lightsBillboardBatch = null; DaggerfallBillboardBatch animalsBillboardBatch = null; //DaggerfallBillboardBatch miscBillboardBatch = null; if (dfUnity.Option_BatchBillboards) { //miscBillboardAtlas = dfUnity.MaterialReader.MiscBillboardAtlas; int natureArchive = ClimateSwaps.GetNatureArchive(CurrentNatureSet, CurrentSeason); summary.NatureBillboardBatch = GameObjectHelper.CreateBillboardBatchGameObject(natureArchive, transform); lightsBillboardBatch = GameObjectHelper.CreateBillboardBatchGameObject(TextureReader.LightsTextureArchive, transform); animalsBillboardBatch = GameObjectHelper.CreateBillboardBatchGameObject(TextureReader.AnimalsTextureArchive, transform); //miscBillboardBatch = GameObjectHelper.CreateBillboardBatchGameObject(miscBillboardAtlas.AtlasMaterial, transform); } // Import blocks for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (dfUnity.Option_BatchBillboards) { // Set block origin for billboard batches // This causes next additions to be offset by this position Vector3 blockOrigin = new Vector3((x * RMBLayout.RMBSide), 0, (y * RMBLayout.RMBSide)); summary.NatureBillboardBatch.BlockOrigin = blockOrigin; lightsBillboardBatch.BlockOrigin = blockOrigin; animalsBillboardBatch.BlockOrigin = blockOrigin; //miscBillboardBatch.BlockOrigin = blockOrigin; } string blockName = dfUnity.ContentReader.BlockFileReader.CheckName(dfUnity.ContentReader.MapFileReader.GetRmbBlockName(ref location, x, y)); GameObject go = GameObjectHelper.CreateRMBBlockGameObject( blockName, x, y, dfUnity.Option_RMBGroundPlane, dfUnity.Option_CityBlockPrefab, summary.NatureBillboardBatch, lightsBillboardBatch, animalsBillboardBatch, null, //miscBillboardAtlas, null, //miscBillboardBatch, CurrentNatureSet, CurrentSeason); go.transform.parent = this.transform; go.transform.position = new Vector3((x * RMBLayout.RMBSide), 0, (y * RMBLayout.RMBSide)); } } // Apply batches if (summary.NatureBillboardBatch) { summary.NatureBillboardBatch.Apply(); } if (lightsBillboardBatch) { lightsBillboardBatch.Apply(); } if (animalsBillboardBatch) { animalsBillboardBatch.Apply(); } //if (miscBillboardBatch) miscBillboardBatch.Apply(); // Enumerate start marker game objects EnumerateStartMarkers(); #if SHOW_LAYOUT_TIMES // Show timer long totalTime = stopwatch.ElapsedMilliseconds - startTime; DaggerfallUnity.LogMessage(string.Format("Time to layout location: {0}ms", totalTime), true); #endif }
// Update terrain data // Spreads loading across several frames to reduce gameplay stalls // This can also be done using true multi-threading, but at much greater // complexity for only minor visible gains. // Only yields after initial init complete private IEnumerator UpdateTerrains() { // First stage updates terrain heightmaps for (int i = 0; i < terrainArray.Length; i++) { if (terrainArray[i].active && terrainArray[i].updateHeights) { UpdateTerrainHeights(terrainArray[i]); terrainArray[i].updateHeights = false; if (!init) { yield return(new WaitForEndOfFrame()); } } } // Wait for physics update when streaming if (!init) { yield return(new WaitForFixedUpdate()); } // Second stage updates terrain nature for (int i = 0; i < terrainArray.Length; i++) { if (terrainArray[i].active && terrainArray[i].updateNature) { UpdateTerrainNature(terrainArray[i]); terrainArray[i].updateNature = false; if (!init) { yield return(new WaitForEndOfFrame()); } } } // Get key for central player terrain int playerKey = TerrainHelper.MakeTerrainKey(MapPixelX, MapPixelY); // Third stage updates location if present // Vast majority of terrains will not have a location // Locations are not optimised as yet and are quite heavy on drawcalls for (int i = 0; i < terrainArray.Length; i++) { // Get key for this terrain int key = TerrainHelper.MakeTerrainKey(terrainArray[i].mapPixelX, terrainArray[i].mapPixelY); if (terrainArray[i].active && terrainArray[i].hasLocation) { // Create location if not present if (!locationDict.ContainsKey(key)) { // Create location object DFLocation location; GameObject locationObject = CreateLocationGameObject(i, out location); if (!locationObject) { continue; } // Add location object to dictionary locationDict.Add(key, locationObject); // Create location beacon // This is parented to location and shares its lifetime if (AddLocationBeacon) { const float beaconHeight = 900f; const float beaconOffset = (MapsFile.WorldMapTerrainDim * MeshReader.GlobalScale) / 2f; GameObject locationMarker = (GameObject)GameObject.Instantiate(Resources.Load <GameObject>("LocationBeacon")); locationMarker.hideFlags = HideFlags.HideAndDontSave; locationMarker.transform.parent = locationObject.transform; locationMarker.transform.localPosition = new Vector3(beaconOffset, beaconHeight, beaconOffset); } // Add one nature batch for entire location // This is parented to location and shares its lifetime GameObject natureBatchObject = new GameObject("NatureBatch"); natureBatchObject.hideFlags = HideFlags.HideAndDontSave; natureBatchObject.transform.parent = locationObject.transform; natureBatchObject.transform.localPosition = Vector3.zero; DaggerfallBillboardBatch natureBatch = natureBatchObject.AddComponent <DaggerfallBillboardBatch>(); int natureArchive = ClimateSwaps.GetNatureArchive(LocalPlayerGPS.ClimateSettings.NatureSet, dfUnity.WorldTime.Now.SeasonValue); natureBatch.SetMaterial(natureArchive); // RMB blocks are laid out in centre of terrain to align with ground int width = location.Exterior.ExteriorData.Width; int height = location.Exterior.ExteriorData.Height; float offsetX = ((8 * RMBLayout.RMBSide) - (width * RMBLayout.RMBSide)) / 2; float offsetZ = ((8 * RMBLayout.RMBSide) - (height * RMBLayout.RMBSide)) / 2; Vector3 origin = new Vector3(offsetX, 2.0f * MeshReader.GlobalScale, offsetZ); // Perform layout and yield after each block is placed DaggerfallLocation dfLocation = locationObject.GetComponent <DaggerfallLocation>(); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { // Set origin for billboard batch add // This causes next additions to be offset by this position Vector3 blockOrigin = origin + new Vector3((x * RMBLayout.RMBSide), 0, (y * RMBLayout.RMBSide)); natureBatch.origin = blockOrigin; // Add block and yield string blockName = dfUnity.ContentReader.BlockFileReader.CheckName(dfUnity.ContentReader.MapFileReader.GetRmbBlockName(ref location, x, y)); GameObject go = RMBLayout.CreateGameObject(blockName, true, natureBatch); go.hideFlags = HideFlags.HideAndDontSave; go.transform.parent = locationObject.transform; go.transform.localPosition = blockOrigin; dfLocation.ApplyClimateSettings(); if (!init) { yield return(new WaitForEndOfFrame()); } } } // If this is the player terrain we may need to reposition player if (playerKey == key && repositionPlayer) { // Position to location and use start marker for large cities bool useStartMarker = (dfLocation.Summary.LocationType == DFRegion.LocationTypes.TownCity); PositionPlayerToLocation(MapPixelX, MapPixelY, dfLocation, origin, width, height, useStartMarker); repositionPlayer = false; } // Apply nature batch natureBatch.Apply(); } } else if (terrainArray[i].active) { if (playerKey == key && repositionPlayer) { PositionPlayerToTerrain(MapPixelX, MapPixelY, Vector3.zero); repositionPlayer = false; } } } // If this is an init we can use the load time to unload unused assets // Keeps memory usage much lower over time if (init) { Resources.UnloadUnusedAssets(); } // Finish by collecting stale data and setting neighbours CollectTerrains(); CollectLocations(); UpdateNeighbours(); }
// Drops nature flats based on random chance scaled by simple rules public static void LayoutNatureBillboards(DaggerfallTerrain dfTerrain, DaggerfallBillboardBatch dfBillboardBatch, float terrainScale, int terrainDist) { const float maxSteepness = 50f; // 50 const float slopeSinkRatio = 70f; // Sink flats slightly into ground as slope increases to prevent floaty trees. const float baseChanceOnDirt = 0.2f; // 0.2 const float baseChanceOnGrass = 0.9f; // 0.4 const float baseChanceOnStone = 0.05f; // 0.05 // Location Rect is expanded slightly to give extra clearance around locations const int natureClearance = 4; Rect rect = dfTerrain.MapData.locationRect; if (rect.x > 0 && rect.y > 0) { rect.xMin -= natureClearance; rect.xMax += natureClearance; rect.yMin -= natureClearance; rect.yMax += natureClearance; } // Chance scaled based on map pixel height // This tends to produce sparser lowlands and denser highlands // Adjust or remove clamp range to influence nature generation float elevationScale = (dfTerrain.MapData.worldHeight / 128f); elevationScale = Mathf.Clamp(elevationScale, 0.4f, 1.0f); // Chance scaled by base climate type float climateScale = 1.0f; DFLocation.ClimateSettings climate = MapsFile.GetWorldClimateSettings(dfTerrain.MapData.worldClimate); switch (climate.ClimateType) { case DFLocation.ClimateBaseType.Desert: // Just lower desert for now climateScale = 0.25f; break; } float chanceOnDirt = baseChanceOnDirt * elevationScale * climateScale; float chanceOnGrass = baseChanceOnGrass * elevationScale * climateScale; float chanceOnStone = baseChanceOnStone * elevationScale * climateScale; // Get terrain Terrain terrain = dfTerrain.gameObject.GetComponent <Terrain>(); if (!terrain) { return; } // Get terrain data TerrainData terrainData = terrain.terrainData; if (!terrainData) { return; } // Remove exiting billboards dfBillboardBatch.Clear(); MeshReplacement.ClearNatureGameObjects(terrain); // Seed random with terrain key Random.InitState(MakeTerrainKey(dfTerrain.MapPixelX, dfTerrain.MapPixelY)); // Just layout some random flats spread evenly across entire map pixel area // Flats are aligned with tiles, max 16129 billboards per batch Vector2 tilePos = Vector2.zero; int tDim = MapsFile.WorldMapTileDim; int hDim = DaggerfallUnity.Instance.TerrainSampler.HeightmapDimension; float scale = terrainData.heightmapScale.x * (float)hDim / (float)tDim; float maxTerrainHeight = DaggerfallUnity.Instance.TerrainSampler.MaxTerrainHeight; float beachLine = DaggerfallUnity.Instance.TerrainSampler.BeachElevation; for (int y = 0; y < tDim; y++) { for (int x = 0; x < tDim; x++) { // Reject based on steepness float steepness = terrainData.GetSteepness((float)x / tDim, (float)y / tDim); if (steepness > maxSteepness) { continue; } // Reject if inside location rect (expanded slightly to give extra clearance around locations) tilePos.x = x; tilePos.y = y; if (rect.x > 0 && rect.y > 0 && rect.Contains(tilePos)) { continue; } // Chance also determined by tile type int tile = dfTerrain.MapData.tilemapSamples[x, y] & 0x3F; if (tile == 1) { // Dirt if (Random.Range(0f, 1f) > chanceOnDirt) { continue; } } else if (tile == 2) { // Grass if (Random.Range(0f, 1f) > chanceOnGrass) { continue; } } else if (tile == 3) { // Stone if (Random.Range(0f, 1f) > chanceOnStone) { continue; } } else { // Anything else continue; } int hx = (int)Mathf.Clamp(hDim * ((float)x / (float)tDim), 0, hDim - 1); int hy = (int)Mathf.Clamp(hDim * ((float)y / (float)tDim), 0, hDim - 1); float height = dfTerrain.MapData.heightmapSamples[hy, hx] * maxTerrainHeight; // x & y swapped in heightmap for TerrainData.SetHeights() // Reject if too close to water if (height < beachLine) { continue; } // Sample height and position billboard Vector3 pos = new Vector3(x * scale, 0, y * scale); float height2 = terrain.SampleHeight(pos + terrain.transform.position); pos.y = height2 - (steepness / slopeSinkRatio); // Add to batch unless a mesh replacement is found int record = Random.Range(1, 32); if (terrainDist > 1 || !MeshReplacement.ImportNatureGameObject(dfBillboardBatch.TextureArchive, record, terrain, x, y)) { dfBillboardBatch.AddItem(record, pos); } else if (!NatureMeshUsed) { NatureMeshUsed = true; // Signal that nature mesh has been used to initiate extra terrain updates } } } // Apply new batch dfBillboardBatch.Apply(); }
// Drops nature flats based on random chance scaled by simple rules public static void LayoutNatureBillboards(DaggerfallTerrain dfTerrain, DaggerfallBillboardBatch dfBillboardBatch, float terrainScale) { const float maxSteepness = 50f; // 50 const float chanceOnDirt = 0.2f; // 0.2 const float chanceOnGrass = 0.9f; // 0.4 const float chanceOnStone = 0.05f; // 0.05 // Get terrain Terrain terrain = dfTerrain.gameObject.GetComponent <Terrain>(); if (!terrain) { return; } // Get terrain data TerrainData terrainData = terrain.terrainData; if (!terrainData) { return; } // Remove exiting billboards dfBillboardBatch.Clear(); // Seed random with terrain key UnityEngine.Random.seed = MakeTerrainKey(dfTerrain.MapPixelX, dfTerrain.MapPixelY); // Just layout some random flats spread evenly across entire map pixel area // Flats are aligned with tiles, max 127x127 in billboard batch Vector2 tilePos = Vector2.zero; float scale = terrainData.heightmapScale.x; int dim = TerrainHelper.terrainTileDim - 1; for (int y = 0; y < dim; y++) { for (int x = 0; x < dim; x++) { // Reject based on steepness float steepness = terrainData.GetSteepness((float)x / dim, (float)y / dim); if (steepness > maxSteepness) { continue; } // Reject if inside location rect // Rect is expanded slightly to give extra clearance around locations tilePos.x = x; tilePos.y = y; const int natureClearance = 4; Rect rect = dfTerrain.MapData.locationRect; if (rect.x > 0 && rect.y > 0) { rect.xMin -= natureClearance; rect.xMin += natureClearance; rect.yMin -= natureClearance; rect.yMax += natureClearance; if (rect.Contains(tilePos)) { continue; } } // Chance scaled based on map pixel height // This tends to produce sparser lowlands and denser highlands // Adjust or remove clamp range to influence nature generation float elevationScale = (dfTerrain.MapData.worldHeight / 128f); elevationScale = Mathf.Clamp(elevationScale, 0.4f, 1.0f); // Chance scaled by base climate type float climateScale = 1.0f; DFLocation.ClimateSettings climate = MapsFile.GetWorldClimateSettings(dfTerrain.MapData.worldClimate); switch (climate.ClimateType) { case DFLocation.ClimateBaseType.Desert: // Just lower desert for now climateScale = 0.25f; break; } // Chance also determined by tile type WorldSample sample = TerrainHelper.GetSample(ref dfTerrain.MapData.samples, x, y); if (sample.record == 1) { // Dirt if (UnityEngine.Random.Range(0f, 1f) > chanceOnDirt * elevationScale * climateScale) { continue; } } else if (sample.record == 2) { // Grass if (UnityEngine.Random.Range(0f, 1f) > chanceOnGrass * elevationScale * climateScale) { continue; } } else if (sample.record == 3) { // Stone if (UnityEngine.Random.Range(0f, 1f) > chanceOnStone * elevationScale * climateScale) { continue; } } else { // Anything else continue; } // Sample height and position billboard Vector3 pos = new Vector3(x * scale, 0, y * scale); float height = terrain.SampleHeight(pos + terrain.transform.position); pos.y = height; // Reject if too close to water float beachLine = DaggerfallUnity.Instance.TerrainSampler.BeachElevation * terrainScale; if (height < beachLine) { continue; } // Add to batch int record = UnityEngine.Random.Range(1, 32); dfBillboardBatch.AddItem(record, pos); } } // Apply new batch dfBillboardBatch.Apply(); }
// Drops nature flats based on random chance scaled by simple rules public static void LayoutNatureBillboards(DaggerfallTerrain dfTerrain, DaggerfallBillboardBatch dfBillboardBatch, float terrainScale) { const float maxSteepness = 50f; // 50 const float chanceOnDirt = 0.2f; // 0.2 const float chanceOnGrass = 0.9f; // 0.4 const float chanceOnStone = 0.05f; // 0.05 // Get terrain Terrain terrain = dfTerrain.gameObject.GetComponent<Terrain>(); if (!terrain) return; // Get terrain data TerrainData terrainData = terrain.terrainData; if (!terrainData) return; // Remove exiting billboards dfBillboardBatch.Clear(); // Seed random with terrain key UnityEngine.Random.seed = MakeTerrainKey(dfTerrain.MapPixelX, dfTerrain.MapPixelY); // Just layout some random flats spread evenly across entire map pixel area // Flats are aligned with tiles, max 127x127 in billboard batch Vector2 tilePos = Vector2.zero; float scale = terrainData.heightmapScale.x; int dim = TerrainHelper.terrainTileDim - 1; for (int y = 0; y < dim; y++) { for (int x = 0; x < dim; x++) { // Reject based on steepness float steepness = terrainData.GetSteepness((float)x / dim, (float)y / dim); if (steepness > maxSteepness) continue; // Reject if inside location rect // Rect is expanded slightly to give extra clearance around locations tilePos.x = x; tilePos.y = y; const int natureClearance = 4; Rect rect = dfTerrain.MapData.locationRect; if (rect.x > 0 && rect.y > 0) { rect.xMin -= natureClearance; rect.xMin += natureClearance; rect.yMin -= natureClearance; rect.yMax += natureClearance; if (rect.Contains(tilePos)) continue; } // Chance scaled based on map pixel height // This tends to produce sparser lowlands and denser highlands // Adjust or remove clamp range to influence nature generation float elevationScale = (dfTerrain.MapData.worldHeight / 128f); elevationScale = Mathf.Clamp(elevationScale, 0.4f, 1.0f); // Chance scaled by base climate type float climateScale = 1.0f; DFLocation.ClimateSettings climate = MapsFile.GetWorldClimateSettings(dfTerrain.MapData.worldClimate); switch (climate.ClimateType) { case DFLocation.ClimateBaseType.Desert: // Just lower desert for now climateScale = 0.25f; break; } // Chance also determined by tile type WorldSample sample = TerrainHelper.GetSample(ref dfTerrain.MapData.samples, x, y); if (sample.record == 1) { // Dirt if (UnityEngine.Random.Range(0f, 1f) > chanceOnDirt * elevationScale * climateScale) continue; } else if (sample.record == 2) { // Grass if (UnityEngine.Random.Range(0f, 1f) > chanceOnGrass * elevationScale * climateScale) continue; } else if (sample.record == 3) { // Stone if (UnityEngine.Random.Range(0f, 1f) > chanceOnStone * elevationScale * climateScale) continue; } else { // Anything else continue; } // Sample height and position billboard Vector3 pos = new Vector3(x * scale, 0, y * scale); float height = terrain.SampleHeight(pos + terrain.transform.position); pos.y = height; // Reject if too close to water float beachLine = TerrainHelper.scaledBeachElevation * terrainScale; if (height < beachLine - beachLine * 0.1f) continue; // Add to batch int record = UnityEngine.Random.Range(1, 32); dfBillboardBatch.AddItem(record, pos); } } // Apply new batch dfBillboardBatch.Apply(); }
private IEnumerator UpdateLocation(int index, bool allowYield) { int key = TerrainHelper.MakeTerrainKey(terrainArray[index].mapPixelX, terrainArray[index].mapPixelY); int playerKey = TerrainHelper.MakeTerrainKey(MapPixelX, MapPixelY); bool isPlayerTerrain = (key == playerKey); if (terrainArray[index].active && terrainArray[index].hasLocation && terrainArray[index].updateLocation) { // Disable update flag terrainArray[index].updateLocation = false; // Create location object DFLocation location; GameObject locationObject = CreateLocationGameObject(index, out location); if (locationObject) { // Add location object to dictionary LocationDesc locationDesc = new LocationDesc(); locationDesc.locationObject = locationObject; locationDesc.mapPixelX = terrainArray[index].mapPixelX; locationDesc.mapPixelY = terrainArray[index].mapPixelY; locationList.Add(locationDesc); // Create billboard batch game objects for this location // Streaming world always batches for performance, regardless of options int natureArchive = ClimateSwaps.GetNatureArchive(LocalPlayerGPS.ClimateSettings.NatureSet, dfUnity.WorldTime.Now.SeasonValue); TextureAtlasBuilder miscBillboardAtlas = dfUnity.MaterialReader.MiscBillboardAtlas; DaggerfallBillboardBatch natureBillboardBatch = GameObjectHelper.CreateBillboardBatchGameObject(natureArchive, locationObject.transform); DaggerfallBillboardBatch lightsBillboardBatch = GameObjectHelper.CreateBillboardBatchGameObject(TextureReader.LightsTextureArchive, locationObject.transform); DaggerfallBillboardBatch animalsBillboardBatch = GameObjectHelper.CreateBillboardBatchGameObject(TextureReader.AnimalsTextureArchive, locationObject.transform); DaggerfallBillboardBatch miscBillboardBatch = GameObjectHelper.CreateBillboardBatchGameObject(miscBillboardAtlas.AtlasMaterial, locationObject.transform); // Set hide flags natureBillboardBatch.hideFlags = HideFlags.HideAndDontSave; lightsBillboardBatch.hideFlags = HideFlags.HideAndDontSave; animalsBillboardBatch.hideFlags = HideFlags.HideAndDontSave; miscBillboardBatch.hideFlags = HideFlags.HideAndDontSave; // RMB blocks are laid out in centre of terrain to align with ground int width = location.Exterior.ExteriorData.Width; int height = location.Exterior.ExteriorData.Height; float offsetX = ((8 * RMBLayout.RMBSide) - (width * RMBLayout.RMBSide)) / 2; float offsetZ = ((8 * RMBLayout.RMBSide) - (height * RMBLayout.RMBSide)) / 2; Vector3 origin = new Vector3(offsetX, 2.0f * MeshReader.GlobalScale, offsetZ); // Get location data DaggerfallLocation dfLocation = locationObject.GetComponent <DaggerfallLocation>(); // Perform layout and yield after each block is placed for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { // Set block origin for billboard batches // This causes next additions to be offset by this position Vector3 blockOrigin = origin + new Vector3((x * RMBLayout.RMBSide), 0, (y * RMBLayout.RMBSide)); natureBillboardBatch.BlockOrigin = blockOrigin; lightsBillboardBatch.BlockOrigin = blockOrigin; animalsBillboardBatch.BlockOrigin = blockOrigin; miscBillboardBatch.BlockOrigin = blockOrigin; // Add block and yield string blockName = dfUnity.ContentReader.BlockFileReader.CheckName(dfUnity.ContentReader.MapFileReader.GetRmbBlockName(ref location, x, y)); GameObject go = GameObjectHelper.CreateRMBBlockGameObject( blockName, false, dfUnity.Option_CityBlockPrefab, natureBillboardBatch, lightsBillboardBatch, animalsBillboardBatch, miscBillboardAtlas, miscBillboardBatch); go.hideFlags = HideFlags.HideAndDontSave; go.transform.parent = locationObject.transform; go.transform.localPosition = blockOrigin; dfLocation.ApplyClimateSettings(); if (allowYield) { yield return(new WaitForEndOfFrame()); } } } // If this is the player terrain we may need to reposition player if (isPlayerTerrain && repositionPlayer) { // Position to location and use start marker for large cities bool useStartMarker = (dfLocation.Summary.LocationType == DFRegion.LocationTypes.TownCity); PositionPlayerToLocation(MapPixelX, MapPixelY, dfLocation, origin, width, height, useStartMarker); repositionPlayer = false; } // Apply billboard batches natureBillboardBatch.Apply(); lightsBillboardBatch.Apply(); animalsBillboardBatch.Apply(); miscBillboardBatch.Apply(); } } else if (terrainArray[index].active) { if (playerKey == key && repositionPlayer) { PositionPlayerToTerrain(MapPixelX, MapPixelY, Vector3.zero); repositionPlayer = false; } } }
/// <summary> /// Performs a fully standalone in-place location layout. /// This method is used only by editor layouts, not by streaming world. /// </summary> private void LayoutLocation(ref DFLocation location) { // Get city dimensions int width = location.Exterior.ExteriorData.Width; int height = location.Exterior.ExteriorData.Height; // Create billboard batch game objects for this location TextureAtlasBuilder miscBillboardAtlas = null; summary.NatureBillboardBatch = null; DaggerfallBillboardBatch lightsBillboardBatch = null; DaggerfallBillboardBatch animalsBillboardBatch = null; DaggerfallBillboardBatch miscBillboardBatch = null; if (dfUnity.Option_BatchBillboards) { miscBillboardAtlas = dfUnity.MaterialReader.MiscBillboardAtlas; int natureArchive = ClimateSwaps.GetNatureArchive(CurrentNatureSet, CurrentSeason); summary.NatureBillboardBatch = GameObjectHelper.CreateBillboardBatchGameObject(natureArchive, transform); lightsBillboardBatch = GameObjectHelper.CreateBillboardBatchGameObject(TextureReader.LightsTextureArchive, transform); animalsBillboardBatch = GameObjectHelper.CreateBillboardBatchGameObject(TextureReader.AnimalsTextureArchive, transform); miscBillboardBatch = GameObjectHelper.CreateBillboardBatchGameObject(miscBillboardAtlas.AtlasMaterial, transform); } // Import blocks for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (dfUnity.Option_BatchBillboards) { // Set block origin for billboard batches // This causes next additions to be offset by this position Vector3 blockOrigin = new Vector3((x * RMBLayout.RMBSide), 0, (y * RMBLayout.RMBSide)); summary.NatureBillboardBatch.BlockOrigin = blockOrigin; lightsBillboardBatch.BlockOrigin = blockOrigin; animalsBillboardBatch.BlockOrigin = blockOrigin; miscBillboardBatch.BlockOrigin = blockOrigin; } string blockName = dfUnity.ContentReader.BlockFileReader.CheckName(dfUnity.ContentReader.MapFileReader.GetRmbBlockName(ref location, x, y)); GameObject go = GameObjectHelper.CreateRMBBlockGameObject( blockName, dfUnity.Option_RMBGroundPlane, dfUnity.Option_CityBlockPrefab, summary.NatureBillboardBatch, lightsBillboardBatch, animalsBillboardBatch, miscBillboardAtlas, miscBillboardBatch, CurrentNatureSet, CurrentSeason); go.transform.parent = this.transform; go.transform.position = new Vector3((x * RMBLayout.RMBSide), 0, (y * RMBLayout.RMBSide)); } } // Apply batches if (summary.NatureBillboardBatch) { summary.NatureBillboardBatch.Apply(); } if (lightsBillboardBatch) { lightsBillboardBatch.Apply(); } if (animalsBillboardBatch) { animalsBillboardBatch.Apply(); } if (miscBillboardBatch) { miscBillboardBatch.Apply(); } // Enumerate start marker game objects EnumerateStartMarkers(); }