package rtg.world.gen; import java.util.WeakHashMap; import net.minecraft.util.math.ChunkPos; import net.minecraft.world.biome.Biome; import rtg.api.util.TimedHashMap; import rtg.api.util.noise.CellNoise; import rtg.api.util.noise.OpenSimplexNoise; import rtg.api.world.RTGWorld; import rtg.util.TimeTracker; import rtg.world.biome.BiomeAnalyzer; import rtg.world.biome.IBiomeProviderRTG; import rtg.world.biome.realistic.RealisticBiomeBase; import rtg.world.biome.realistic.RealisticBiomePatcher; /** * * @author Zeno410 */ class LandscapeGenerator { private final int sampleSize = 8; private final int sampleArraySize; private final int[] biomeData; private float[] [] weightings; private final RTGWorld rtgWorld; private final OpenSimplexNoise simplex; private final CellNoise cell; private float [] weightedBiomes = new float [256]; private BiomeAnalyzer analyzer = new BiomeAnalyzer(); private TimedHashMap<ChunkPos,ChunkLandscape> storage = new TimedHashMap<>(60 * 1000); private RealisticBiomePatcher biomePatcher = new RealisticBiomePatcher(); private final WeakHashMap<ChunkLandscape,float[]> cache = new WeakHashMap(); LandscapeGenerator(RTGWorld rtgWorld) { this.rtgWorld = rtgWorld; sampleArraySize = sampleSize * 2 + 5; biomeData = new int[sampleArraySize * sampleArraySize]; this.simplex = rtgWorld.simplex; this.cell = rtgWorld.cell; setWeightings(); } private void setWeightings() { weightings = new float [sampleArraySize * sampleArraySize][256]; for (int i = 0; i < 16; i++) { for (int j=0; j<16; j++) { TimeTracker.manager.start("Weighting"); float limit = (float)Math.pow((56f*56f),.7); // float limit = 56f; for (int mapX = 0 ; mapX < sampleArraySize; mapX ++) { for (int mapZ = 0 ; mapZ < sampleArraySize; mapZ ++) { float xDist = (i - chunkCoordinate(mapX)); float zDist = (j - chunkCoordinate(mapZ)); float distanceSquared = xDist*xDist + zDist*zDist; //float distance = (float)Math.sqrt(distanceSquared); float distance = (float)Math.pow(distanceSquared,.7); float weight = 1f - distance/limit; if (weight <0) weight = 0; weightings[mapX*sampleArraySize + mapZ][i * 16 + j] = weight; } } } } } private int chunkCoordinate(int biomeMapCoordinate) { return (biomeMapCoordinate - sampleSize)*8; } int getBiomeDataAt(IBiomeProviderRTG cmr, int cx, int cz) { int cx2 = cx&15; int cz2 = cz&15; ChunkLandscape target = this.landscape(cmr, cx-cx2, cz-cz2); return Biome.getIdForBiome(target.biome[cx2*16+cz2].baseBiome); } /* * All of the 'cx' and 'cz' parameters have been flipped when passing them. * Prior to flipping, the terrain was being XZ-chunk-flipped. - WhichOnesPink */ synchronized ChunkLandscape landscape(IBiomeProviderRTG cmr, int cx, int cz) { ChunkPos chunkPos = new ChunkPos(cx, cz); ChunkLandscape preExisting = this.storage.get(chunkPos); if (preExisting != null) return preExisting; ChunkLandscape result = new ChunkLandscape(); getNewerNoise(cmr, cx, cz, result); int [] biomeIndices= cmr.getBiomesGens(cx, cz,16,16); analyzer.newRepair(biomeIndices, result.biome, this.biomeData, this.sampleSize, result.noise, result.river); //-cmr.getRiverStrength(cx * 16 + 7, cy * 16 + 7)); storage.put(chunkPos, result); return result; } private synchronized void getNewerNoise(IBiomeProviderRTG cmr, int cx, int cz, ChunkLandscape landscape) { // get area biome map TimeTracker.manager.start("RTG Noise"); TimeTracker.manager.start("Biome Layout"); for(int i = -sampleSize; i < sampleSize + 5; i++) { for(int j = -sampleSize; j < sampleSize + 5; j++) { biomeData[(i + sampleSize) * sampleArraySize + (j + sampleSize)] = Biome.getIdForBiome(cmr.getBiomeDataAt(cx + ((i * 8)), cz + ((j * 8))).baseBiome); } } TimeTracker.manager.stop("Biome Layout"); float river; // fill the old smallRender for (int i = 0; i < 16; i++) { for (int j=0; j<16; j++) { TimeTracker.manager.start("Weighting"); float totalWeight = 0; for (int mapX = 0 ; mapX < sampleArraySize; mapX ++) { for (int mapZ = 0 ; mapZ < sampleArraySize; mapZ ++) { float weight = weightings[mapX*sampleArraySize + mapZ][i * 16 +j]; if (weight > 0) { totalWeight += weight; weightedBiomes[biomeData[mapX*sampleArraySize + mapZ]] += weight; } } } // normalize biome weights for (int biomeIndex = 0; biomeIndex < weightedBiomes.length; biomeIndex ++) { weightedBiomes[biomeIndex] /= totalWeight; } landscape.noise[i * 16 + j] = 0f; TimeTracker.manager.stop("Weighting"); TimeTracker.manager.start("Generating"); river = cmr.getRiverStrength(cx + i, cz + j); landscape.river[i * 16 + j] = -river; float totalBorder = 0f; for(int k = 0; k < 256; k++) { if(weightedBiomes[k] > 0f) { totalBorder += weightedBiomes[k]; RealisticBiomeBase realisticBiome = RealisticBiomeBase.getBiome(k); // Do we need to patch the biome? if (realisticBiome == null) { realisticBiome = biomePatcher.getPatchedRealisticBiome( "NULL biome (" + k + ") found when getting newer noise."); } landscape.noise[i * 16 + j] += realisticBiome.rNoise(this.rtgWorld, cx + i, cz + j, weightedBiomes[k], river + 1f) * weightedBiomes[k]; // 0 for the next column weightedBiomes[k] = 0f; } } if (totalBorder <.999||totalBorder>1.001) throw new RuntimeException("" + totalBorder); TimeTracker.manager.stop("Generating"); } } //fill biomes array with biomeData TimeTracker.manager.start("Biome Layout"); for (int i = 0; i < 16; i++) { for (int j=0; j<16; j++) { landscape.biome[i*16+j] = cmr.getBiomeDataAt(cx + (((i-7) * 8+4)), cz + (((j-7) * 8+4))); } } TimeTracker.manager.stop("Biome Layout"); } public float [] noiseFor(IBiomeProviderRTG cmr, int worldX, int worldZ) { ChunkPos location = new ChunkPos(worldX,worldZ); ChunkLandscape landscape = storage.get(location); float [] result = cache.get(landscape); if (result != null) return result; // not found; we have to make it; result = new float[256]; final int adjust = 24;// seems off? but decorations aren't matching their chunks. TimeTracker.manager.start("Biome Layout"); for (int bx = -4; bx <= 4; bx++) { for (int bz = -4; bz <= 4; bz++) { result[getBiomeDataAt(cmr, worldX + adjust + bx * 4, worldZ + adjust + bz * 4)] += 0.01234569f; } } TimeTracker.manager.stop("Biome Layout"); TimeTracker.manager.stop("Features"); cache.put(landscape, result); return result; } }