private List <SectionCoord> SectionsInRadius(SectionCoord coord, float radius) { int dist = Mathf.CeilToInt(radius * 2 + 1); List <SectionCoord> sections = new List <SectionCoord>(dist * dist); int x, z, dir; x = z = 0; dir = dist = 1; while (dist <= 2 * radius + 1) { while (2 * x * dir < dist) { if (x * x + z * z < radius * radius + 1) { sections.Add(new SectionCoord(coord.x + x, coord.z + z)); } x += dir; } while (2 * z * dir < dist) { if (x * x + z * z < radius * radius + 1) { sections.Add(new SectionCoord(coord.x + x, coord.z + z)); } z += dir; } dir *= -1; dist++; } return(sections); }
private BiomeCenter GenerateBiomeCenter(SectionCoord coord) { BiomeCenter center = new BiomeCenter(); center.coord = coord; NotRandom.RNG rng = new NotRandom.RNG(NotRandom.Hash2Int(seedHash, center.coord.GetHashCode())); float nx = center.coord.x * biomeCenterSpacing + ((rng.Value() * maxBiomeCenterOffset * 2) - maxBiomeCenterOffset); float nz = center.coord.z * biomeCenterSpacing + ((rng.Value() * maxBiomeCenterOffset * 2) - maxBiomeCenterOffset); center.center = new Vector3(nx, 1f, nz); int index = (int)(rng.ValueUInt() % totalBiomeFrequency); for (int i = 0; i < biomes.Count; i++) { if (index >= 0 && index < biomes[i].relativeFrequency) { center.biome = biomes[i]; break; } index -= biomes[i].relativeFrequency; } return(center); }
private float[,,] GenerateAlphamaps(SectionCoord coord, out List <Biome> containedBiomes) { List <float[, ]> alphamapList = new List <float[, ]>(); containedBiomes = new List <Biome>(); Biome b; List <BiomeStrength> locBiomes; float[,] alphamap; float nx, nz; for (int x = 0; x < genSettings.alphaMapRes; x++) { for (int z = 0; z < genSettings.alphaMapRes; z++) { nx = coord.x - 0.5f + (float)x / (genSettings.alphaMapRes - 1); nz = coord.z - 0.5f + (float)z / (genSettings.alphaMapRes - 1); nx *= genSettings.length; nz *= genSettings.length; locBiomes = GetBiomes(new Vector3(nx, 0f, nz)); for (int i = 0; i < locBiomes.Count; i++) { b = locBiomes[i].biome; if (b == null) { continue; } if (!containedBiomes.Contains(b)) { containedBiomes.Add(b); alphamapList.Add(new float[genSettings.alphaMapRes, genSettings.alphaMapRes]); } alphamapList[containedBiomes.IndexOf(b)][z, x] = locBiomes[i].strength; } } } float[,,] alphamaps = new float[genSettings.alphaMapRes, genSettings.alphaMapRes, containedBiomes.Count]; for (int i = 0; i < containedBiomes.Count; i++) { alphamap = alphamapList[i]; for (int x = 0; x < genSettings.alphaMapRes; x++) { for (int z = 0; z < genSettings.alphaMapRes; z++) { alphamaps[x, z, i] = alphamap[x, z]; } } } return(alphamaps); }
public override bool Equals(object obj) { if (!(obj is SectionCoord)) { return(false); } SectionCoord other = (SectionCoord)obj; return(other.x == x && other.z == z); }
private void CalculateBiomeBounds(SectionCoord coord) { BiomeCenter target = SafeGetBiomeCenter(coord); if (target.boundsCalculated) { return; } List <BiomeCenter> neighbors = new List <BiomeCenter>(); List <SectionCoord> coords; Dictionary <SectionCoord, Plane> properBounds = new Dictionary <SectionCoord, Plane>(); List <Plane> weakBounds = new List <Plane>(); Plane plane; Vector3 self = target.center; Vector3 other, middle; coords = SectionsInRadius(coord, 2.75f); for (int i = 1; i < coords.Count; i++) { neighbors.Add(SafeGetBiomeCenter(coords[i])); } for (int i = 0; i < neighbors.Count; i++) { other = neighbors[i].center; middle = (self + other) / 2f; if (neighbors[i].boundsCalculated) { plane = neighbors[i].properBounds[coord].flipped; properBounds.Add(neighbors[i].coord, plane); } else { plane = new Plane((self - other).normalized, middle); properBounds.Add(neighbors[i].coord, plane); } weakBounds.Add(new Plane(plane.normal, middle - plane.normal * (biomeBlend / 2f))); } SimplifyConvexPlanes(weakBounds, self); lock (target) { if (!target.boundsCalculated) { target.properBounds = properBounds; target.weakBounds = weakBounds; target.boundsCalculated = true; } } }
private float[,] GenerateHeightmap(SectionCoord coord) { float[,] heightmap = new float[genSettings.heightMapRes, genSettings.heightMapRes]; Biome b; float height, weight, nx, nz, bx, bz, totalHeight, totalWeight; List <BiomeStrength> biomes; for (int x = 0; x < genSettings.heightMapRes; x++) { for (int z = 0; z < genSettings.heightMapRes; z++) { nx = coord.x - 0.5f + (float)x / (genSettings.heightMapRes - 1); nz = coord.z - 0.5f + (float)z / (genSettings.heightMapRes - 1); bx = nx * genSettings.length; bz = nz * genSettings.length; nx *= genSettings.length / noiseScale; nz *= genSettings.length / noiseScale; biomes = GetBiomes(new Vector3(bx, 0f, bz)); totalHeight = 0f; totalWeight = 0f; for (int i = 0; i < biomes.Count; i++) { b = biomes[i].biome; if (b == null) { continue; } weight = biomes[i].strength; totalWeight += weight; height = 0f; for (int j = 0; j < b.heightmapOctaves; j++) { height += Mathf.Pow(2f, -j) * Mathf.PerlinNoise( nx * Mathf.Pow(2, j) + offsets[j, 0], nz * Mathf.Pow(2, j) + offsets[j, 1]); } height /= (2f - Mathf.Pow(2, -(b.heightmapOctaves - 1))); height = Mathf.Pow(height, b.heightExponent); height = height * (b.maxHeight - b.minHeight) + b.minHeight; totalHeight += height * weight; } heightmap[z, x] = totalHeight / totalWeight; } } return(heightmap); }
public float TerrainHeightAt(Vector3 vLoc) { SectionCoord coord = SectionFor(vLoc); if (!SectionExists(coord)) { return(Mathf.NegativeInfinity); } TerrainSection sec = terrains[coord]; if (sec == null || sec.terrain == null) { return(Mathf.NegativeInfinity); } return(sec.terrain.SampleHeight(vLoc) + sec.terrain.GetPosition().y); }
private IEnumerator Remove_CR() { while (toRemove.Count > 0) { for (int i = 0; i < maxSimultaneousGens && toRemove.Count > 0; i++) { SectionCoord coord = toRemove.Dequeue(); TerrainSection sec = terrains[coord]; if (sec != null && sec.terrain != null) { Destroy(sec.terrain.gameObject); terrains.Remove(coord); } } yield return(null); } removeRunning = false; }
private BiomeCenter SafeGetBiomeCenter(SectionCoord coord) { BiomeCenter bc, test; if (!biomeCenters.TryGetValue(coord, out bc)) { bc = GenerateBiomeCenter(coord); lock (biomeCenters) { if (!biomeCenters.TryGetValue(coord, out test)) { biomeCenters.Add(coord, bc); } else { bc = test; } } } return(bc); }
private IEnumerator Create_CR() { while (toCreate.Count > 0) { while (((!detectMaxSimultaneousGens && numGenThreads < maxSimultaneousGens) || (detectMaxSimultaneousGens && numGenThreads < (SystemInfo.processorCount / 2 + 1))) && toCreate.Count > 0) { numGenThreads++; SectionCoord coord = toCreate.Dequeue(); StartCoroutine(GenerateSection_CR(coord)); generating.Add(coord); yield return(null); } yield return(new WaitUntil(() => (!detectMaxSimultaneousGens && numGenThreads < maxSimultaneousGens) || (detectMaxSimultaneousGens && numGenThreads < (SystemInfo.processorCount / 2 + 1)))); } createRunning = false; }
public List <BiomeStrength> GetBiomes(Vector3 vLoc) { List <BiomeStrength> output = new List <BiomeStrength>(biomes.Count); Vector3 loc = new Vector3(vLoc.x, 0f, vLoc.z); Vector3 biomeLoc = loc / biomeCenterSpacing; SectionCoord coord = new SectionCoord(Mathf.RoundToInt(biomeLoc.x), Mathf.RoundToInt(biomeLoc.z)); BiomeCenter center = SafeGetBiomeCenter(coord); float weakDist, dist, totalStrength; if (!center.boundsCalculated) { CalculateBiomeBounds(coord); } weakDist = Mathf.Infinity; for (int k = 0; weakDist > biomeBlend && k < center.weakBounds.Count; k++) { dist = center.weakBounds[k].GetDistanceToPoint(loc); if (dist < weakDist) { weakDist = dist; } } if (weakDist > biomeBlend) { output.Add(new BiomeStrength(center.biome, 1f)); return(output); } List <SectionCoord> coords = SectionsInRadius(coord, 2.75f); for (int i = 0; i < coords.Count; i++) { center = SafeGetBiomeCenter(coords[i]); if (!center.boundsCalculated) { CalculateBiomeBounds(coords[i]); } weakDist = Mathf.Infinity; for (int k = 0; weakDist >= 0.0f && k < center.weakBounds.Count; k++) { dist = center.weakBounds[k].GetDistanceToPoint(loc); if (dist < weakDist) { weakDist = dist; } } if (weakDist > biomeBlend) { output.Clear(); output.Add(new BiomeStrength(center.biome, 1f)); return(output); } if (weakDist >= 0.0f) { int j = 0; for (; j < output.Count; j++) { if (output[j].biome == center.biome) { output[j].strength += (weakDist / biomeBlend); break; } } if (j == output.Count) { output.Add(new BiomeStrength(center.biome, weakDist / biomeBlend)); } } } totalStrength = 0f; for (int i = 0; i < output.Count; i++) { output[i].strength = Mathf.Pow(Mathf.Clamp01(output[i].strength), 2f); totalStrength += output[i].strength; } for (int i = 0; i < output.Count; i++) { output[i].strength /= totalStrength; } return(output); }
public TerrainSection(SectionCoord coord = new SectionCoord(), Terrain terrain = null) { this.coord = coord; this.terrain = terrain; }
private IEnumerator GenerateSection_CR(SectionCoord coord) { TerrainSection sec; if (terrains.TryGetValue(coord, out sec) && sec != null && sec.terrain != null) { if (numGenThreads > 0) { numGenThreads--; } yield break; } if (sec == null) { sec = new TerrainSection(coord); } float[,] heightmap = null; float[,,] alphamaps = null; int bx, bz; float height; Vector3 targLoc; GameObject go; BiomeData bd; List <SectionCoord> biomeCenters; List <Biome> containedBiomes = null; List <DetailPrototypeData> detailPrototypeDatas = null; List <int[, ]> detailMaps = null; List <TreePrototypeData> treePrototypeDatas = null; List <TreeInstance> treeInstances = null; Thread heightThread = new Thread(() => { #if DEBUG_THREADS UnityEngine.Profiling.Profiler.BeginThreadProfiling( "Heightmap", "" + coord.x + ":" + coord.z); #endif heightmap = GenerateHeightmap(coord); #if DEBUG_THREADS UnityEngine.Profiling.Profiler.EndThreadProfiling(); #endif }); Thread alphaThread = new Thread(() => { #if DEBUG_THREADS UnityEngine.Profiling.Profiler.BeginThreadProfiling( "Alphamap", "" + coord.x + ":" + coord.z); #endif alphamaps = GenerateAlphamaps(coord, out containedBiomes); #if DEBUG_THREADS UnityEngine.Profiling.Profiler.EndThreadProfiling(); #endif }); Thread detailThread = new Thread(() => { #if DEBUG_THREADS UnityEngine.Profiling.Profiler.BeginThreadProfiling( "Detailmap", "" + coord.x + ":" + coord.z); #endif detailMaps = GenerateDetailMaps(coord, out detailPrototypeDatas); #if DEBUG_THREADS UnityEngine.Profiling.Profiler.EndThreadProfiling(); #endif }); Thread treeThread = new Thread(() => { #if DEBUG_THREADS UnityEngine.Profiling.Profiler.BeginThreadProfiling( "Treemap", "" + coord.x + ":" + coord.z); #endif treeInstances = GenerateTreeInstances(coord, out treePrototypeDatas); #if DEBUG_THREADS UnityEngine.Profiling.Profiler.EndThreadProfiling(); #endif }); yield return(null); treeThread.Start(); yield return(null); heightThread.Start(); yield return(null); alphaThread.Start(); yield return(null); detailThread.Start(); yield return(new WaitUntil(() => (heightmap != null && alphamaps != null && detailMaps != null && treeInstances != null && !loadingSection))); TerrainLayer[] terrainLayers = new TerrainLayer[containedBiomes.Count]; loadingSection = true; sectionLoading = coord; TerrainData data = new TerrainData(); data.heightmapResolution = genSettings.heightMapRes; yield return(null); data.alphamapResolution = genSettings.alphaMapRes; data.SetDetailResolution(genSettings.detailMapRes, genSettings.detailMapResPerPatch); data.size = new Vector3(genSettings.length, genSettings.height, genSettings.length); yield return(null); data.SetHeights(0, 0, heightmap); yield return(null); for (int i = 0; i < containedBiomes.Count; i++) { terrainLayers[i] = containedBiomes[i].terrainLayer; } DetailPrototype[] detailPrototypes = null; if (detailPrototypeDatas.Count > 0) { detailPrototypes = new DetailPrototype[detailPrototypeDatas.Count]; } for (int i = 0; i < detailPrototypeDatas.Count; i++) { DetailPrototype dp = detailPrototypes[i] = new DetailPrototype(); DetailPrototypeData dpd = detailPrototypeDatas[i]; dp.bendFactor = dpd.bendFactor; dp.dryColor = dpd.dryColor; dp.healthyColor = dpd.healthyColor; dp.maxHeight = dpd.maxHeight; dp.maxWidth = dpd.maxWidth; dp.minHeight = dpd.minHeight; dp.minWidth = dpd.minWidth; dp.noiseSpread = dpd.noiseSpread; if (dpd.prototype != null) { dp.prototype = dpd.prototype; dp.usePrototypeMesh = true; } else { dp.prototypeTexture = dpd.prototypeTexture; dp.usePrototypeMesh = false; } dp.renderMode = dpd.renderMode; } yield return(null); data.terrainLayers = terrainLayers; data.SetAlphamaps(0, 0, alphamaps); yield return(null); TreePrototype[] treePrototypes = null; if (treePrototypeDatas.Count > 0) { treePrototypes = new TreePrototype[treePrototypeDatas.Count]; } for (int i = 0; i < treePrototypeDatas.Count; i++) { TreePrototype tp = treePrototypes[i] = new TreePrototype(); tp.bendFactor = treePrototypeDatas[i].bendFactor; tp.prefab = treePrototypeDatas[i].prefab; } yield return(null); if (detailPrototypes != null) { data.detailPrototypes = detailPrototypes; yield return(null); for (int i = 0; i < detailMaps.Count; i++) { data.SetDetailLayer(0, 0, i, detailMaps[i]); } yield return(null); } if (treePrototypes != null) { data.treePrototypes = treePrototypes; yield return(null); data.SetTreeInstances(treeInstances.ToArray(), true); } yield return(null); data.RefreshPrototypes(); yield return(null); GameObject obj = Terrain.CreateTerrainGameObject(data); sec.terrain = obj.GetComponent <Terrain>(); sec.terrain.treeBillboardDistance = 150; sec.terrain.materialTemplate = terrainMaterial; sec.terrain.allowAutoConnect = true; obj.transform.position = new Vector3(coord.x * genSettings.length - genSettings.length / 2, 0f, coord.z * genSettings.length - genSettings.length / 2); yield return(null); sec.terrain.Flush(); yield return(null); bx = Mathf.RoundToInt((coord.x * genSettings.length) / biomeCenterSpacing); bz = Mathf.RoundToInt((coord.z * genSettings.length) / biomeCenterSpacing); biomeCenters = SectionsInRadius(new SectionCoord(bx, bx), 2); for (int i = 0; i < biomeCenters.Count; i++) { BiomeCenter center = SafeGetBiomeCenter(biomeCenters[i]); height = sec.terrain.SampleHeight(center.center); targLoc = new Vector3(center.center.x, height, center.center.z + sec.terrain.GetPosition().y); if (sec.terrain.terrainData.bounds.Contains(targLoc - sec.terrain.transform.position)) { if (center.biome.ambientPrefab != null) { go = Instantiate(center.biome.ambientPrefab, targLoc, Quaternion.identity, sec.terrain.transform); } else { go = new GameObject(); go.transform.position = targLoc; go.transform.parent = sec.terrain.transform; } bd = go.AddComponent <TerrainManager.BiomeData>(); bd.biome = center.biome; bd.center = targLoc; bd.boundaries = center.properBounds.Values.ToList(); SimplifyConvexPlanes(bd.boundaries, bd.center); } } yield return(null); terrains.Add(coord, sec); if (numGenThreads > 0) { numGenThreads--; } generating.Remove(coord); if (coord.Equals(sectionLoading)) { loadingSection = false; } }
private bool SectionExists(SectionCoord coord) { return(terrains.ContainsKey(coord) && terrains[coord].terrain != null && !toRemove.Contains(coord)); }
private List <TreeInstance> GenerateTreeInstances(SectionCoord coord, out List <TreePrototypeData> treePrototypes) { treePrototypes = new List <TreePrototypeData>(); List <TreeInstance> treeInstances = new List <TreeInstance>(); float cellSize = minTreeDistance / Mathf.Sqrt(2); int numCells = Mathf.CeilToInt(genSettings.length / cellSize); int unprocessed = -1; int noTree = -2; int[,] trees = new int[numCells, numCells]; Vector3[,] points = new Vector3[numCells, numCells]; NotRandom.RNG rng = new NotRandom.RNG(NotRandom.Hash2Int(seedHash, coord.GetHashCode())); List <Vector3> toProcess = new List <Vector3>(); List <Vector3> processed = new List <Vector3>(); List <Vector3> selected = new List <Vector3>(); List <TreePrototypeData> selectedTree = new List <TreePrototypeData>(); Vector3 test, next; List <BiomeStrength> biomes; Biome b = null; int p, ax, az, anx, anz, annx, annz, range, tree, totalTreeFreq; int maxX, maxZ, maxRange; float bx, bz, a, r, str; bool canTree; next = new Vector3(rng.Value() * (genSettings.length - minBorderTreeDistance * 2) + minBorderTreeDistance, 0f, rng.Value() * (genSettings.length - minBorderTreeDistance * 2) + minBorderTreeDistance); anx = Mathf.FloorToInt(next.x / cellSize); anz = Mathf.FloorToInt(next.z / cellSize); maxRange = Mathf.CeilToInt(maxMinTreeDistance / cellSize); range = Mathf.CeilToInt(minTreeDistance / cellSize); trees[anx, anz] = unprocessed; points[anx, anz] = next; toProcess.Add(next); while (toProcess.Count > 0) { p = (int)(rng.ValueUInt() % toProcess.Count); test = toProcess[p]; toProcess.RemoveAt(p); ax = Mathf.FloorToInt(test.x / cellSize); az = Mathf.FloorToInt(test.z / cellSize); canTree = true; maxX = Mathf.Min(maxRange, numCells - 1 - ax); maxZ = Mathf.Min(maxRange, numCells - 1 - az); for (int x = Mathf.Max(-maxRange, -ax); canTree && x <= maxX; x++) { for (int z = Mathf.Max(-maxRange, -az); canTree && z <= maxZ; z++) { anx = ax + x; anz = az + z; tree = trees[anx, anz]; if (tree > 0 && Vector3.Distance(test, points[anx, anz]) < selectedTree[tree - 1].minDistance) { canTree = false; } } } if (canTree) { bx = (coord.x - 0.5f) * genSettings.length + test.x; bz = (coord.z - 0.5f) * genSettings.length + test.z; biomes = GetBiomes(new Vector3(bx, 0f, bz)); if (biomes.Count == 1) { b = biomes[0].biome; str = biomes[0].strength; } else { b = null; str = 0f; for (int i = 0; i < biomes.Count; i++) { if (b == null) { b = biomes[i].biome; str = biomes[i].strength; } else { if (biomes[i].strength > str) { b = biomes[i].biome; str = biomes[i].strength; } } } } if (str < minBiomeTreeStrength || b == null || b.treePrototypes == null || b.treePrototypes.Count == 0) { canTree = false; } } if (canTree && b != null) { totalTreeFreq = 0; for (int i = 0; i < b.treePrototypes.Count; i++) { totalTreeFreq += b.treePrototypes[i].relativeFrequency; } int index = (int)(rng.ValueUInt() % totalTreeFreq); for (int i = 0; i < b.treePrototypes.Count; i++) { if (index >= 0 && index < b.treePrototypes[i].relativeFrequency) { selectedTree.Add(b.treePrototypes[i]); selected.Add(test); trees[ax, az] = selectedTree.Count; break; } index -= b.treePrototypes[i].relativeFrequency; } } for (int i = 0; i < treeTestPoints; i++) { a = rng.Value(); r = rng.Value(); r = r * minTreeDistance + minTreeDistance; next = new Vector3(test.x + r * Mathf.Cos(a * 2 * Mathf.PI), 0f, test.z + r * Mathf.Sin(a * 2 * Mathf.PI)); if (next.x < minBorderTreeDistance || next.z < minBorderTreeDistance || next.x > genSettings.length - minBorderTreeDistance || next.z > genSettings.length - minBorderTreeDistance) { continue; } anx = Mathf.FloorToInt(next.x / cellSize); anz = Mathf.FloorToInt(next.z / cellSize); if (trees[anx, anz] != 0) { continue; } canTree = true; maxX = Mathf.Min(range, numCells - 1 - anx); maxZ = Mathf.Min(range, numCells - 1 - anz); for (int x = Mathf.Max(-range, -anx); canTree && x <= maxX; x++) { for (int z = Mathf.Max(-range, -anz); canTree && z <= maxZ; z++) { annx = anx + x; annz = anz + z; if (trees[annx, annx] != 0 && Vector3.Distance(next, points[annx, annz]) < minTreeDistance) { canTree = false; } } } if (canTree) { toProcess.Add(next); trees[anx, anz] = unprocessed; points[anx, anz] = next; } } processed.Add(test); if (trees[ax, az] < 1) { trees[ax, az] = noTree; } } TreeInstance ti; TreePrototypeData tpd; for (int i = 0; i < selected.Count; i++) { tpd = selectedTree[i]; if (!treePrototypes.Contains(tpd)) { treePrototypes.Add(tpd); } ti = new TreeInstance(); ti.color = tpd.color; ti.lightmapColor = tpd.lightmapColor; ti.prototypeIndex = treePrototypes.IndexOf(tpd); ti.position = selected[i] / genSettings.length; ti.heightScale = rng.Value() * (tpd.maxHeightScale - tpd.minHeightScale) + tpd.minHeightScale; ti.widthScale = rng.Value() * (tpd.maxWidthScale - tpd.minWidthScale) + tpd.minWidthScale; ti.rotation = rng.Value() * 2 * Mathf.PI; treeInstances.Add(ti); } return(treeInstances); }
private List <int[, ]> GenerateDetailMaps(SectionCoord coord, out List <DetailPrototypeData> detailPrototypes) { List <int[, ]> detailmaps = new List <int[, ]>(); detailPrototypes = new List <DetailPrototypeData>(); float nx, nz, bx, bz, density; List <BiomeStrength> biomes = null; Biome b; DetailPrototypeData dpd; for (int x = 0; x < genSettings.detailMapRes; x++) { for (int z = 0; z < genSettings.detailMapRes; z++) { nx = coord.x - 0.5f + (float)x / (genSettings.detailMapRes - 1); nz = coord.z - 0.5f + (float)z / (genSettings.detailMapRes - 1); bx = nx * genSettings.length; bz = nz * genSettings.length; nx *= genSettings.length / detailNoiseScale; nz *= genSettings.length / detailNoiseScale; biomes = GetBiomes(new Vector3(bx, 0f, bz)); for (int i = 0; i < biomes.Count; i++) { b = biomes[i].biome; if (b == null) { continue; } for (int j = 0; j < b.detailPrototypes.Count; j++) { dpd = b.detailPrototypes[j]; if (!detailPrototypes.Contains(dpd)) { detailPrototypes.Add(dpd); detailmaps.Add(new int[genSettings.detailMapRes, genSettings.detailMapRes]); } density = 0f; for (int k = 0; k < numDetailOctaves; k++) { density += Mathf.Pow(2f, -k) * Mathf.PerlinNoise( nx * Mathf.Pow(2, k) + detailOffsets[k, 0], nz * Mathf.Pow(2, k) + detailOffsets[k, 1]); } density /= (2f - Mathf.Pow(2, -(numDetailOctaves - 1))); detailmaps[detailPrototypes.IndexOf(dpd)][z, x] = Mathf.FloorToInt(((density * (dpd.maxDensity - dpd.minDensity)) + dpd.minDensity) * Mathf.Pow(biomes[i].strength, 2f)); } } } } return(detailmaps); }