summaryrefslogtreecommitdiffstats
path: root/src/Generating/ProtIntGen.h
diff options
context:
space:
mode:
authorMattes D <github@xoft.cz>2014-11-04 15:45:08 +0100
committerMattes D <github@xoft.cz>2014-11-04 15:45:08 +0100
commit6201b3602c2f82fd9de68404a9e742c9cf3c6554 (patch)
treee086038a40a07eba6be7a10a18f9cc95527fc729 /src/Generating/ProtIntGen.h
parentMoved the chrono include into Globals. (diff)
parentWebAdmin: Fixed login template destination address. (diff)
downloadcuberite-6201b3602c2f82fd9de68404a9e742c9cf3c6554.tar
cuberite-6201b3602c2f82fd9de68404a9e742c9cf3c6554.tar.gz
cuberite-6201b3602c2f82fd9de68404a9e742c9cf3c6554.tar.bz2
cuberite-6201b3602c2f82fd9de68404a9e742c9cf3c6554.tar.lz
cuberite-6201b3602c2f82fd9de68404a9e742c9cf3c6554.tar.xz
cuberite-6201b3602c2f82fd9de68404a9e742c9cf3c6554.tar.zst
cuberite-6201b3602c2f82fd9de68404a9e742c9cf3c6554.zip
Diffstat (limited to '')
-rw-r--r--src/Generating/ProtIntGen.h1351
1 files changed, 1351 insertions, 0 deletions
diff --git a/src/Generating/ProtIntGen.h b/src/Generating/ProtIntGen.h
new file mode 100644
index 000000000..73ed27096
--- /dev/null
+++ b/src/Generating/ProtIntGen.h
@@ -0,0 +1,1351 @@
+
+// ProtIntGen.h
+
+// Declares the prototyping integer generators - cProtIntGen class and its descendants
+
+/*
+These classes generate 2D arrays of integers that have various interpretations. The main purpose of these
+classes is to provide fast prototyping for cIntGen classes - unlike cIntGen classes, these are not
+template-based and so they take care of the underlying sizes automatically. This makes them easier to chain
+and re-chain, since the size parameters don't need to be adjusted after each such case. Their performance is,
+however, slightly worse, which is why we use cIntGen classes in the final generator.
+
+Because there is no SizeX / SizeZ template param, the generators would have to either alloc memory for each
+underlying generator's values, or use a maximum-size buffer. We chose the latter, to avoid memory allocation
+overhead; this however means that there's (an arbitrary) limit to the size of the generated data.
+*/
+
+
+
+
+
+#pragma once
+
+// We need the biome group constants defined there:
+#include "IntGen.h"
+
+
+
+
+
+/** Interface that all the generator classes provide. */
+class cProtIntGen
+{
+protected:
+ /** Maximum size of the generated area.
+ Adjust the constant if you need larger areas, these are just so that we can use fixed-size buffers. */
+ static const int m_BufferSize = 900;
+
+public:
+
+ /** Type of the generic interface used for storing links to the underlying generators. */
+ typedef std::shared_ptr<cProtIntGen> Underlying;
+
+
+ /** Force a virtual destructor in all descendants.
+ Descendants contain virtual functions and are referred to via pointer-to-base, so they need a virtual destructor. */
+ virtual ~cProtIntGen() {}
+
+ /** Generates the array of specified size into a_Values, based on given min coords. */
+ virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) = 0;
+};
+
+
+
+
+
+/** Provides additional cNoise member and its helper functions. */
+class cProtIntGenWithNoise :
+ public cProtIntGen
+{
+ typedef cProtIntGen super;
+
+public:
+ cProtIntGenWithNoise(int a_Seed) :
+ m_Noise(a_Seed)
+ {
+ }
+
+protected:
+ cNoise m_Noise;
+
+ /** Chooses one of a_Val1 or a_Val2, based on m_Noise and the coordinates for querying the noise. */
+ int chooseRandomOne(int a_RndX, int a_RndZ, int a_Val1, int a_Val2)
+ {
+ int rnd = m_Noise.IntNoise2DInt(a_RndX, a_RndZ) / 7;
+ return ((rnd & 1) == 0) ? a_Val1 : a_Val2;
+ }
+
+ /** Chooses one of a_ValN, based on m_Noise and the coordinates for querying the noise. */
+ int chooseRandomOne(int a_RndX, int a_RndZ, int a_Val1, int a_Val2, int a_Val3, int a_Val4)
+ {
+ int rnd = m_Noise.IntNoise2DInt(a_RndX, a_RndZ) / 7;
+ switch (rnd % 4)
+ {
+ case 0: return a_Val1;
+ case 1: return a_Val2;
+ case 2: return a_Val3;
+ default: return a_Val4;
+ }
+ }
+};
+
+
+
+
+
+
+/** Generates a 2D array of random integers in the specified range [0 .. Range). */
+class cProtIntGenChoice :
+ public cProtIntGenWithNoise
+{
+ typedef cProtIntGenWithNoise super;
+
+public:
+ cProtIntGenChoice(int a_Seed, int a_Range) :
+ super(a_Seed),
+ m_Range(a_Range)
+ {
+ }
+
+
+ virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override
+ {
+ for (int z = 0; z < a_SizeZ; z++)
+ {
+ int BaseZ = a_MinZ + z;
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ a_Values[x + a_SizeX * z] = (super::m_Noise.IntNoise2DInt(a_MinX + x, BaseZ) / 7) % m_Range;
+ }
+ } // for z
+ }
+
+protected:
+ int m_Range;
+};
+
+
+
+
+
+
+/** Decides between the ocean and landmass biomes.
+Has a threshold (in percent) of how much land, the larger the threshold, the more land.
+Generates 0 for ocean, biome group ID for landmass. */
+class cProtIntGenLandOcean :
+ public cProtIntGenWithNoise
+{
+ typedef cProtIntGenWithNoise super;
+
+public:
+ cProtIntGenLandOcean(int a_Seed, int a_Threshold) :
+ super(a_Seed),
+ m_Threshold(a_Threshold)
+ {
+ }
+
+
+ virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override
+ {
+ for (int z = 0; z < a_SizeZ; z++)
+ {
+ int BaseZ = a_MinZ + z;
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ int rnd = (super::m_Noise.IntNoise2DInt(a_MinX + x, BaseZ) / 7);
+ a_Values[x + a_SizeX * z] = ((rnd % 100) < m_Threshold) ? ((rnd / 101) % bgLandOceanMax + 1) : 0;
+ }
+ }
+
+ // If the centerpoint of the world is within the area, set it to bgTemperate, always:
+ if ((a_MinX <= 0) && (a_MinZ <= 0) && (a_MinX + a_SizeX > 0) && (a_MinZ + a_SizeZ > 0))
+ {
+ a_Values[-a_MinX - a_MinZ * a_SizeX] = bgTemperate;
+ }
+ }
+
+protected:
+ int m_Threshold;
+};
+
+
+
+
+
+/** Zooms the underlying value array to twice the size. Uses random-neighbor for the pixels in-between.
+This means that the zoome out image is randomly distorted. Applying zoom several times provides all
+the distortion that the generators need. */
+class cProtIntGenZoom :
+ public cProtIntGenWithNoise
+{
+ typedef cProtIntGenWithNoise super;
+
+public:
+ cProtIntGenZoom(int a_Seed, Underlying a_UnderlyingGen) :
+ super(a_Seed),
+ m_UnderlyingGen(a_UnderlyingGen)
+ {
+ }
+
+
+ virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override
+ {
+ // Get the coords for the lower generator:
+ int lowerMinX = a_MinX >> 1;
+ int lowerMinZ = a_MinZ >> 1;
+ int lowerSizeX = a_SizeX / 2 + 2;
+ int lowerSizeZ = a_SizeZ / 2 + 2;
+ ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize);
+ ASSERT(lowerSizeX > 0);
+ ASSERT(lowerSizeZ > 0);
+
+ // Generate the underlying data with half the resolution:
+ int lowerData[m_BufferSize];
+ m_UnderlyingGen->GetInts(lowerMinX, lowerMinZ, lowerSizeX, lowerSizeZ, lowerData);
+ const int lowStepX = (lowerSizeX - 1) * 2;
+ int cache[m_BufferSize];
+
+ // Discreet-interpolate the values into twice the size:
+ for (int z = 0; z < lowerSizeZ - 1; ++z)
+ {
+ int idx = (z * 2) * lowStepX;
+ int PrevZ0 = lowerData[z * lowerSizeX];
+ int PrevZ1 = lowerData[(z + 1) * lowerSizeX];
+
+ for (int x = 0; x < lowerSizeX - 1; ++x)
+ {
+ int ValX1Z0 = lowerData[x + 1 + z * lowerSizeX];
+ int ValX1Z1 = lowerData[x + 1 + (z + 1) * lowerSizeX];
+ int RndX = (x + lowerMinX) * 2;
+ int RndZ = (z + lowerMinZ) * 2;
+ cache[idx] = PrevZ0;
+ cache[idx + lowStepX] = super::chooseRandomOne(RndX, RndZ + 1, PrevZ0, PrevZ1);
+ cache[idx + 1] = super::chooseRandomOne(RndX, RndZ - 1, PrevZ0, ValX1Z0);
+ cache[idx + 1 + lowStepX] = super::chooseRandomOne(RndX, RndZ, PrevZ0, ValX1Z0, PrevZ1, ValX1Z1);
+ idx += 2;
+ PrevZ0 = ValX1Z0;
+ PrevZ1 = ValX1Z1;
+ }
+ }
+
+ // Copy from Cache into a_Values; take into account the even/odd offsets in a_Min:
+ for (int z = 0; z < a_SizeZ; ++z)
+ {
+ memcpy(a_Values + z * a_SizeX, cache + (z + (a_MinZ & 1)) * lowStepX + (a_MinX & 1), a_SizeX * sizeof(int));
+ }
+ }
+
+protected:
+ Underlying m_UnderlyingGen;
+};
+
+
+
+
+
+/** Smoothes out some artifacts generated by the zooming - mostly single-pixel values.
+Compares each pixel to its neighbors and if the neighbors are equal, changes the pixel to their value. */
+class cProtIntGenSmooth :
+ public cProtIntGenWithNoise
+{
+ typedef cProtIntGenWithNoise super;
+
+public:
+ cProtIntGenSmooth(int a_Seed, Underlying a_Underlying) :
+ super(a_Seed),
+ m_Underlying(a_Underlying)
+ {
+ }
+
+
+ virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override
+ {
+ // Generate the underlying values:
+ int lowerSizeX = a_SizeX + 2;
+ int lowerSizeZ = a_SizeZ + 2;
+ ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize);
+ int lowerData[m_BufferSize];
+ m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, lowerSizeX, lowerSizeZ, lowerData);
+
+ // Smooth - for each square check if the surroundings are the same, if so, expand them diagonally.
+ // Also get rid of single-pixel irregularities (A-B-A):
+ for (int z = 0; z < a_SizeZ; z++)
+ {
+ int NoiseZ = a_MinZ + z;
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ int val = lowerData[x + 1 + (z + 1) * lowerSizeX];
+ int above = lowerData[x + 1 + z * lowerSizeX];
+ int below = lowerData[x + 1 + (z + 2) * lowerSizeX];
+ int left = lowerData[x + (z + 1) * lowerSizeX];
+ int right = lowerData[x + 2 + (z + 1) * lowerSizeX];
+
+ if ((left == right) && (above == below))
+ {
+ if (((super::m_Noise.IntNoise2DInt(a_MinX + x, NoiseZ) / 7) % 2) == 0)
+ {
+ val = left;
+ }
+ else
+ {
+ val = above;
+ }
+ }
+ else
+ {
+ if (left == right)
+ {
+ val = left;
+ }
+
+ if (above == below)
+ {
+ val = above;
+ }
+ }
+
+ a_Values[x + z * a_SizeX] = val;
+ }
+ }
+ }
+
+protected:
+ Underlying m_Underlying;
+};
+
+
+
+
+
+/** Converts land biomes at the edge of an ocean into the respective beach biome. */
+class cProtIntGenBeaches :
+ public cProtIntGen
+{
+ typedef cProtIntGen super;
+
+public:
+ cProtIntGenBeaches(Underlying a_Underlying) :
+ m_Underlying(a_Underlying)
+ {
+ }
+
+
+ virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override
+ {
+ // Map for biome -> its beach:
+ static const int ToBeach[] =
+ {
+ /* biOcean */ biOcean,
+ /* biPlains */ biBeach,
+ /* biDesert */ biBeach,
+ /* biExtremeHills */ biStoneBeach,
+ /* biForest */ biBeach,
+ /* biTaiga */ biColdBeach,
+ /* biSwampland */ biSwampland,
+ /* biRiver */ biRiver,
+ /* biNether */ biNether,
+ /* biEnd */ biEnd,
+ /* biFrozenOcean */ biColdBeach,
+ /* biFrozenRiver */ biColdBeach,
+ /* biIcePlains */ biColdBeach,
+ /* biIceMountains */ biColdBeach,
+ /* biMushroomIsland */ biMushroomShore,
+ /* biMushroomShore */ biMushroomShore,
+ /* biBeach */ biBeach,
+ /* biDesertHills */ biBeach,
+ /* biForestHills */ biBeach,
+ /* biTaigaHills */ biColdBeach,
+ /* biExtremeHillsEdge */ biStoneBeach,
+ /* biJungle */ biBeach,
+ /* biJungleHills */ biBeach,
+ /* biJungleEdge */ biBeach,
+ /* biDeepOcean */ biOcean,
+ /* biStoneBeach */ biStoneBeach,
+ /* biColdBeach */ biColdBeach,
+ /* biBirchForest */ biBeach,
+ /* biBirchForestHills */ biBeach,
+ /* biRoofedForest */ biBeach,
+ /* biColdTaiga */ biColdBeach,
+ /* biColdTaigaHills */ biColdBeach,
+ /* biMegaTaiga */ biStoneBeach,
+ /* biMegaTaigaHills */ biStoneBeach,
+ /* biExtremeHillsPlus */ biStoneBeach,
+ /* biSavanna */ biBeach,
+ /* biSavannaPlateau */ biBeach,
+ /* biMesa */ biMesa,
+ /* biMesaPlateauF */ biMesa,
+ /* biMesaPlateau */ biMesa,
+ };
+
+ // Generate the underlying values:
+ int lowerSizeX = a_SizeX + 2;
+ int lowerSizeZ = a_SizeZ + 2;
+ ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize);
+ int lowerValues[m_BufferSize];
+ m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, lowerSizeX, lowerSizeZ, lowerValues);
+
+ // Add beaches between ocean and biomes:
+ for (int z = 0; z < a_SizeZ; z++)
+ {
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ int val = lowerValues[x + 1 + (z + 1) * lowerSizeX];
+ int above = lowerValues[x + 1 + z * lowerSizeX];
+ int below = lowerValues[x + 1 + (z + 2) * lowerSizeX];
+ int left = lowerValues[x + (z + 1) * lowerSizeX];
+ int right = lowerValues[x + 2 + (z + 1) * lowerSizeX];
+ if (!IsBiomeOcean(val))
+ {
+ if (IsBiomeOcean(above) || IsBiomeOcean(below) || IsBiomeOcean(left) || IsBiomeOcean(right))
+ {
+ // First convert the value to a regular biome (drop the M flag), then modulo by our biome count:
+ val = ToBeach[(val % 128) % ARRAYCOUNT(ToBeach)];
+ }
+ }
+ a_Values[x + z * a_SizeX] = val;
+ }
+ }
+ }
+
+protected:
+ Underlying m_Underlying;
+};
+
+
+
+
+
+/** Generates the underlying numbers and then randomly changes some ocean group pixels into random land
+biome group pixels, based on the predefined chance. */
+class cProtIntGenAddIslands :
+ public cProtIntGenWithNoise
+{
+ typedef cProtIntGenWithNoise super;
+
+public:
+ typedef std::shared_ptr<cProtIntGen> Underlying;
+
+
+ cProtIntGenAddIslands(int a_Seed, int a_Chance, Underlying a_Underlying) :
+ super(a_Seed),
+ m_Chance(a_Chance),
+ m_Underlying(a_Underlying)
+ {
+ }
+
+
+ virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override
+ {
+ m_Underlying->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, a_Values);
+ for (int z = 0; z < a_SizeZ; z++)
+ {
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ if (a_Values[x + z * a_SizeX] == bgOcean)
+ {
+ int rnd = super::m_Noise.IntNoise2DInt(a_MinX + x, a_MinZ + z) / 7;
+ if (rnd % 1000 < m_Chance)
+ {
+ a_Values[x + z * a_SizeX] = (rnd / 1003) % bgLandOceanMax;
+ }
+ }
+ }
+ }
+ }
+
+protected:
+ /** Chance of each ocean pixel being converted, in permille. */
+ int m_Chance;
+
+ Underlying m_Underlying;
+};
+
+
+
+
+
+/** A filter that adds an edge biome group between two biome groups that need an edge between them. */
+class cProtIntGenBiomeGroupEdges :
+ public cProtIntGen
+{
+ typedef cProtIntGen super;
+
+public:
+ cProtIntGenBiomeGroupEdges(Underlying a_Underlying) :
+ m_Underlying(a_Underlying)
+ {
+ }
+
+
+ virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values)
+ {
+ // Generate the underlying biome groups:
+ int lowerSizeX = a_SizeX + 2;
+ int lowerSizeZ = a_SizeZ + 2;
+ ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize);
+ int lowerValues[m_BufferSize];
+ m_Underlying->GetInts(a_MinX, a_MinZ, lowerSizeX, lowerSizeZ, lowerValues);
+
+ // Change the biomes on incompatible edges into an edge biome:
+ for (int z = 0; z < a_SizeZ; z++)
+ {
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ int val = lowerValues[x + 1 + (z + 1) * lowerSizeX];
+ int Above = lowerValues[x + 1 + z * lowerSizeX];
+ int Below = lowerValues[x + 1 + (z + 2) * lowerSizeX];
+ int Left = lowerValues[x + (z + 1) * lowerSizeX];
+ int Right = lowerValues[x + 2 + (z + 1) * lowerSizeX];
+ switch (val)
+ {
+ // Desert should neighbor only oceans, desert and temperates; change to temperate when another:
+ case bgDesert:
+ {
+ if (
+ !isDesertCompatible(Above) ||
+ !isDesertCompatible(Below) ||
+ !isDesertCompatible(Left) ||
+ !isDesertCompatible(Right)
+ )
+ {
+ val = bgTemperate;
+ }
+ break;
+ } // case bgDesert
+
+ // Ice should not neighbor deserts; change to temperate:
+ case bgIce:
+ {
+ if (
+ (Above == bgDesert) ||
+ (Below == bgDesert) ||
+ (Left == bgDesert) ||
+ (Right == bgDesert)
+ )
+ {
+ val = bgTemperate;
+ }
+ break;
+ } // case bgIce
+ }
+ a_Values[x + z * a_SizeX] = val;
+ } // for x
+ } // for z
+ }
+
+protected:
+ Underlying m_Underlying;
+
+
+ inline bool isDesertCompatible(int a_BiomeGroup)
+ {
+ switch (a_BiomeGroup)
+ {
+ case bgOcean:
+ case bgDesert:
+ case bgTemperate:
+ {
+ return true;
+ }
+ default:
+ {
+ return false;
+ }
+ }
+ }
+};
+
+
+
+
+
+/** Turns biome group indices into real biomes.
+For each pixel, takes its biome group and chooses a random biome from that group; replaces the value with
+that biome. */
+class cProtIntGenBiomes :
+ public cProtIntGenWithNoise
+{
+ typedef cProtIntGenWithNoise super;
+
+public:
+ cProtIntGenBiomes(int a_Seed, Underlying a_Underlying) :
+ super(a_Seed),
+ m_Underlying(a_Underlying)
+ {
+ }
+
+
+ virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override
+ {
+ // Define the per-biome-group biomes:
+ static const int oceanBiomes[] =
+ {
+ biOcean, // biDeepOcean,
+ };
+
+ // Same as oceanBiomes, there are no rare oceanic biomes (mushroom islands are handled separately)
+ static const int rareOceanBiomes[] =
+ {
+ biOcean,
+ };
+
+ static const int desertBiomes[] =
+ {
+ biDesert, biDesert, biDesert, biDesert, biDesert, biDesert, biSavanna, biSavanna, biPlains,
+ };
+
+ static const int rareDesertBiomes[] =
+ {
+ biMesaPlateau, biMesaPlateauF,
+ };
+
+ static const int temperateBiomes[] =
+ {
+ biForest, biForest, biRoofedForest, biExtremeHills, biPlains, biBirchForest, biSwampland,
+ };
+
+ static const int rareTemperateBiomes[] =
+ {
+ biJungle, // Jungle is not strictly temperate, but let's piggyback it here
+ };
+
+ static const int mountainBiomes[] =
+ {
+ biExtremeHills, biForest, biTaiga, biPlains,
+ };
+
+ static const int rareMountainBiomes[] =
+ {
+ biMegaTaiga,
+ };
+
+ static const int iceBiomes[] =
+ {
+ biIcePlains, biIcePlains, biIcePlains, biIcePlains, biColdTaiga,
+ };
+
+ // Same as iceBiomes, there's no rare ice biome
+ static const int rareIceBiomes[] =
+ {
+ biIcePlains, biIcePlains, biIcePlains, biIcePlains, biColdTaiga,
+ };
+
+ static const cBiomesInGroups biomesInGroups[] =
+ {
+ /* bgOcean */ { static_cast<int>(ARRAYCOUNT(oceanBiomes)), oceanBiomes},
+ /* bgDesert */ { static_cast<int>(ARRAYCOUNT(desertBiomes)), desertBiomes},
+ /* bgTemperate */ { static_cast<int>(ARRAYCOUNT(temperateBiomes)), temperateBiomes},
+ /* bgMountains */ { static_cast<int>(ARRAYCOUNT(mountainBiomes)), mountainBiomes},
+ /* bgIce */ { static_cast<int>(ARRAYCOUNT(iceBiomes)), iceBiomes},
+ };
+
+ static const cBiomesInGroups rareBiomesInGroups[] =
+ {
+ /* bgOcean */ { static_cast<int>(ARRAYCOUNT(rareOceanBiomes)), rareOceanBiomes},
+ /* bgDesert */ { static_cast<int>(ARRAYCOUNT(rareDesertBiomes)), rareDesertBiomes},
+ /* bgTemperate */ { static_cast<int>(ARRAYCOUNT(rareTemperateBiomes)), rareTemperateBiomes},
+ /* bgMountains */ { static_cast<int>(ARRAYCOUNT(rareMountainBiomes)), rareMountainBiomes},
+ /* bgIce */ { static_cast<int>(ARRAYCOUNT(rareIceBiomes)), rareIceBiomes},
+ };
+
+ // Generate the underlying values, representing biome groups:
+ m_Underlying->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, a_Values);
+
+ // Overwrite each biome group with a random biome from that group:
+ // Take care of the bgfRare flag
+ for (int z = 0; z < a_SizeZ; z++)
+ {
+ int IdxZ = z * a_SizeX;
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ int val = a_Values[x + IdxZ];
+ const cBiomesInGroups & Biomes = (val > bgfRare) ?
+ rareBiomesInGroups[(val & (bgfRare - 1)) % ARRAYCOUNT(rareBiomesInGroups)] :
+ biomesInGroups[val % ARRAYCOUNT(biomesInGroups)];
+ int rnd = (super::m_Noise.IntNoise2DInt(x + a_MinX, z + a_MinZ) / 7);
+ a_Values[x + IdxZ] = Biomes.Biomes[rnd % Biomes.Count];
+ }
+ }
+ }
+
+protected:
+
+ struct cBiomesInGroups
+ {
+ const int Count;
+ const int * Biomes;
+ };
+
+
+ /** The underlying int generator */
+ Underlying m_Underlying;
+};
+
+
+
+
+
+/** Randomly replaces pixels of one value to another value, using the given chance. */
+class cProtIntGenReplaceRandomly :
+ public cProtIntGenWithNoise
+{
+ typedef cProtIntGenWithNoise super;
+
+public:
+ typedef std::shared_ptr<cProtIntGen> Underlying;
+
+
+ cProtIntGenReplaceRandomly(int a_Seed, int a_From, int a_To, int a_Chance, Underlying a_Underlying) :
+ super(a_Seed),
+ m_From(a_From),
+ m_To(a_To),
+ m_Chance(a_Chance),
+ m_Underlying(a_Underlying)
+ {
+ }
+
+
+ virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override
+ {
+ // Generate the underlying values:
+ m_Underlying->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, a_Values);
+
+ // Replace some of the values:
+ for (int z = 0; z < a_SizeZ; z++)
+ {
+ int idxZ = z * a_SizeX;
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ int idx = x + idxZ;
+ if (a_Values[idx] == m_From)
+ {
+ int rnd = super::m_Noise.IntNoise2DInt(x + a_MinX, z + a_MinZ) / 7;
+ if (rnd % 1000 < m_Chance)
+ {
+ a_Values[idx] = m_To;
+ }
+ }
+ }
+ } // for z
+ }
+
+
+protected:
+ /** The original value to be replaced. */
+ int m_From;
+
+ /** The destination value to which to replace. */
+ int m_To;
+
+ /** Chance, in permille, of replacing the value. */
+ int m_Chance;
+
+ Underlying m_Underlying;
+};
+
+
+
+
+
+/** Mixer that joins together finalized biomes and rivers.
+It first checks for oceans, if there is an ocean in the Biomes, it keeps the ocean.
+If there's no ocean, it checks Rivers for a river, if there is a river, it uses the Biomes to select either
+regular river or frozen river, based on the biome. */
+class cProtIntGenMixRivers:
+ public cProtIntGen
+{
+ typedef cProtIntGen super;
+
+public:
+ cProtIntGenMixRivers(Underlying a_Biomes, Underlying a_Rivers):
+ m_Biomes(a_Biomes),
+ m_Rivers(a_Rivers)
+ {
+ }
+
+
+ virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override
+ {
+ // Generate the underlying data:
+ ASSERT(a_SizeX * a_SizeZ <= m_BufferSize);
+ m_Biomes->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, a_Values);
+ int riverData[m_BufferSize];
+ m_Rivers->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, riverData);
+
+ // Mix the values:
+ for (int z = 0; z < a_SizeZ; z++)
+ {
+ int idxZ = z * a_SizeX;
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ int idx = x + idxZ;
+ if (IsBiomeOcean(a_Values[idx]))
+ {
+ // Oceans are kept without any changes
+ continue;
+ }
+ if (riverData[idx] != biRiver)
+ {
+ // There's no river, keep the current value
+ continue;
+ }
+
+ // There's a river, change the output to a river or a frozen river, based on the original biome:
+ if (IsBiomeVeryCold((EMCSBiome)a_Values[idx]))
+ {
+ a_Values[idx] = biFrozenRiver;
+ }
+ else
+ {
+ a_Values[idx] = biRiver;
+ }
+ } // for x
+ } // for z
+ }
+
+protected:
+ Underlying m_Biomes;
+ Underlying m_Rivers;
+};
+
+
+
+
+
+/** Generates a river based on the underlying data.
+This is basically an edge detector over the underlying data. The rivers are the edges where the underlying
+data changes from one pixel to its neighbor. */
+class cProtIntGenRiver:
+ public cProtIntGenWithNoise
+{
+ typedef cProtIntGenWithNoise super;
+
+public:
+ cProtIntGenRiver(int a_Seed, Underlying a_Underlying):
+ super(a_Seed),
+ m_Underlying(a_Underlying)
+ {
+ }
+
+
+ virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override
+ {
+ // Generate the underlying data:
+ int lowerSizeX = a_SizeX + 2;
+ int lowerSizeZ = a_SizeZ + 2;
+ ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize);
+ int lowerValues[m_BufferSize];
+ m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, lowerSizeX, lowerSizeZ, lowerValues);
+
+ // Detect the edges:
+ for (int z = 0; z < a_SizeZ; z++)
+ {
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ int Above = lowerValues[x + 1 + z * lowerSizeX];
+ int Below = lowerValues[x + 1 + (z + 2) * lowerSizeX];
+ int Left = lowerValues[x + (z + 1) * lowerSizeX];
+ int Right = lowerValues[x + 2 + (z + 1) * lowerSizeX];
+ int val = lowerValues[x + 1 + (z + 1) * lowerSizeX];
+
+ if ((val == Above) && (val == Below) && (val == Left) && (val == Right))
+ {
+ val = 0;
+ }
+ else
+ {
+ val = biRiver;
+ }
+ a_Values[x + z * a_SizeX] = val;
+ } // for x
+ } // for z
+ }
+
+protected:
+ Underlying m_Underlying;
+};
+
+
+
+
+
+/** Turns some of the oceans into the specified biome. Used for mushroom and deep ocean.
+The biome is only placed if at least 3 of its neighbors are ocean and only with the specified chance. */
+class cProtIntGenAddToOcean:
+ public cProtIntGenWithNoise
+{
+ typedef cProtIntGenWithNoise super;
+
+public:
+ cProtIntGenAddToOcean(int a_Seed, int a_Chance, int a_ToValue, Underlying a_Underlying):
+ super(a_Seed),
+ m_Chance(a_Chance),
+ m_ToValue(a_ToValue),
+ m_Underlying(a_Underlying)
+ {
+ }
+
+
+ virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override
+ {
+ // Generate the underlying data:
+ int lowerSizeX = a_SizeX + 2;
+ int lowerSizeZ = a_SizeZ + 2;
+ ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize);
+ int lowerValues[m_BufferSize];
+ m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, lowerSizeX, lowerSizeZ, lowerValues);
+
+ // Add the mushroom islands:
+ for (int z = 0; z < a_SizeZ; z++)
+ {
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ int val = lowerValues[x + 1 + (z + 1) * lowerSizeX];
+ if (!IsBiomeOcean(val))
+ {
+ a_Values[x + z * a_SizeX] = val;
+ continue;
+ }
+
+ // Count the ocean neighbors:
+ int Above = lowerValues[x + 1 + z * lowerSizeX];
+ int Below = lowerValues[x + 1 + (z + 2) * lowerSizeX];
+ int Left = lowerValues[x + (z + 1) * lowerSizeX];
+ int Right = lowerValues[x + 2 + (z + 1) * lowerSizeX];
+ int NumOceanNeighbors = 0;
+ if (IsBiomeOcean(Above))
+ {
+ NumOceanNeighbors += 1;
+ }
+ if (IsBiomeOcean(Below))
+ {
+ NumOceanNeighbors += 1;
+ }
+ if (IsBiomeOcean(Left))
+ {
+ NumOceanNeighbors += 1;
+ }
+ if (IsBiomeOcean(Right))
+ {
+ NumOceanNeighbors += 1;
+ }
+
+ // If at least 3 ocean neighbors and the chance is right, change:
+ if (
+ (NumOceanNeighbors >= 3) &&
+ ((super::m_Noise.IntNoise2DInt(x + a_MinX, z + a_MinZ) / 7) % 1000 < m_Chance)
+ )
+ {
+ a_Values[x + z * a_SizeX] = m_ToValue;
+ }
+ else
+ {
+ a_Values[x + z * a_SizeX] = val;
+ }
+ } // for x
+ } // for z
+ }
+
+protected:
+ /** Chance, in permille, of changing the biome. */
+ int m_Chance;
+
+ /** The value to change the ocean into. */
+ int m_ToValue;
+
+ Underlying m_Underlying;
+};
+
+
+
+
+
+/** Changes random pixels of the underlying data to the specified value. */
+class cProtIntGenSetRandomly :
+ public cProtIntGenWithNoise
+{
+ typedef cProtIntGenWithNoise super;
+
+public:
+ cProtIntGenSetRandomly(int a_Seed, int a_Chance, int a_ToValue, Underlying a_Underlying) :
+ super(a_Seed),
+ m_Chance(a_Chance),
+ m_ToValue(a_ToValue),
+ m_Underlying(a_Underlying)
+ {
+ }
+
+
+ virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override
+ {
+ // Generate the underlying data:
+ m_Underlying->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, a_Values);
+
+ // Change random pixels to bgOcean:
+ for (int z = 0; z < a_SizeZ; z++)
+ {
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ int rnd = super::m_Noise.IntNoise2DInt(x + a_MinX, z + a_MinZ) / 7;
+ if (rnd % 1000 < m_Chance)
+ {
+ a_Values[x + z * a_SizeX] = m_ToValue;
+ }
+ }
+ }
+ }
+
+protected:
+ /** Chance, in permille, of changing each pixel. */
+ int m_Chance;
+
+ /** The value to which to set the pixel. */
+ int m_ToValue;
+
+ Underlying m_Underlying;
+};
+
+
+
+
+
+
+/** Adds a "rare" flag to random biome groups, based on the given chance. */
+class cProtIntGenRareBiomeGroups:
+ public cProtIntGenWithNoise
+{
+ typedef cProtIntGenWithNoise super;
+
+public:
+ cProtIntGenRareBiomeGroups(int a_Seed, int a_Chance, Underlying a_Underlying):
+ super(a_Seed),
+ m_Chance(a_Chance),
+ m_Underlying(a_Underlying)
+ {
+ }
+
+
+ virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override
+ {
+ // Generate the underlying data:
+ m_Underlying->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, a_Values);
+
+ // Change some of the biome groups into rare biome groups:
+ for (int z = 0; z < a_SizeZ; z++)
+ {
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ int rnd = super::m_Noise.IntNoise2DInt(x + a_MinX, z + a_MinZ) / 7;
+ if (rnd % 1000 < m_Chance)
+ {
+ int idx = x + a_SizeX * z;
+ a_Values[idx] = a_Values[idx] | bgfRare;
+ }
+ }
+ }
+ }
+
+protected:
+ /** Chance, in permille, of changing each pixel into the rare biome group. */
+ int m_Chance;
+
+ /** The underlying generator. */
+ Underlying m_Underlying;
+};
+
+
+
+
+
+/** Changes biomes in the parent data into an alternate versions (usually "hill" variants), in such places
+that have their alterations set. */
+class cProtIntGenAlternateBiomes:
+ public cProtIntGenWithNoise
+{
+ typedef cProtIntGenWithNoise super;
+
+public:
+ cProtIntGenAlternateBiomes(int a_Seed, Underlying a_Alterations, Underlying a_BaseBiomes):
+ super(a_Seed),
+ m_Alterations(a_Alterations),
+ m_BaseBiomes(a_BaseBiomes)
+ {
+ }
+
+
+ virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override
+ {
+ // Generate the base biomes and the alterations:
+ m_BaseBiomes->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, a_Values);
+ int alterations[m_BufferSize];
+ m_Alterations->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, alterations);
+
+ // Change the biomes into their alternate versions:
+ int len = a_SizeX * a_SizeZ;
+ for (int idx = 0; idx < len; ++idx)
+ {
+ if (alterations[idx] == 0)
+ {
+ // No change
+ continue;
+ }
+
+ // Change to alternate biomes:
+ int val = a_Values[idx];
+ switch (val)
+ {
+ case biBirchForest: val = biBirchForestHills; break;
+ case biDesert: val = biDesertHills; break;
+ case biExtremeHills: val = biExtremeHillsPlus; break;
+ case biForest: val = biForestHills; break;
+ case biIcePlains: val = biIceMountains; break;
+ case biJungle: val = biJungleHills; break;
+ case biMegaTaiga: val = biMegaTaigaHills; break;
+ case biMesaPlateau: val = biMesa; break;
+ case biMesaPlateauF: val = biMesa; break;
+ case biMesaPlateauM: val = biMesa; break;
+ case biMesaPlateauFM: val = biMesa; break;
+ case biPlains: val = biForest; break;
+ case biRoofedForest: val = biPlains; break;
+ case biSavanna: val = biSavannaPlateau; break;
+ case biTaiga: val = biTaigaHills; break;
+ }
+ a_Values[idx] = val;
+ } // for idx - a_Values[]
+ }
+
+protected:
+ Underlying m_Alterations;
+ Underlying m_BaseBiomes;
+};
+
+
+
+
+
+/** Adds an edge between two specifically incompatible biomes, such as mesa and forest. */
+class cProtIntGenBiomeEdges:
+ public cProtIntGenWithNoise
+{
+ typedef cProtIntGenWithNoise super;
+
+public:
+ cProtIntGenBiomeEdges(int a_Seed, Underlying a_Underlying):
+ super(a_Seed),
+ m_Underlying(a_Underlying)
+ {
+ }
+
+
+ virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override
+ {
+ // Generate the underlying biomes:
+ int lowerSizeX = a_SizeX + 2;
+ int lowerSizeZ = a_SizeZ + 2;
+ ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize);
+ int lowerValues[m_BufferSize];
+ m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, lowerSizeX, lowerSizeZ, lowerValues);
+
+ // Convert incompatible edges into neutral biomes:
+ for (int z = 0; z < a_SizeZ; z++)
+ {
+ for (int x = 0; x < a_SizeX; x++)
+ {
+ int biome = lowerValues[x + 1 + (z + 1) * lowerSizeX];
+ int above = lowerValues[x + 1 + z * lowerSizeX];
+ int below = lowerValues[x + 1 + (z + 2) * lowerSizeX];
+ int left = lowerValues[x + (z + 1) * lowerSizeX];
+ int right = lowerValues[x + 2 + (z + 1) * lowerSizeX];
+
+ switch (biome)
+ {
+ case biDesert:
+ case biDesertM:
+ case biDesertHills:
+ {
+ if (
+ IsBiomeVeryCold(static_cast<EMCSBiome>(above)) ||
+ IsBiomeVeryCold(static_cast<EMCSBiome>(below)) ||
+ IsBiomeVeryCold(static_cast<EMCSBiome>(left)) ||
+ IsBiomeVeryCold(static_cast<EMCSBiome>(right))
+ )
+ {
+ biome = biPlains;
+ }
+ break;
+ } // case biDesert
+
+ case biMesaPlateau:
+ case biMesaPlateauF:
+ case biMesaPlateauFM:
+ case biMesaPlateauM:
+ {
+ if (
+ !isMesaCompatible(above) ||
+ !isMesaCompatible(below) ||
+ !isMesaCompatible(left) ||
+ !isMesaCompatible(right)
+ )
+ {
+ biome = biDesert;
+ }
+ break;
+ } // Mesa biomes
+
+ case biJungle:
+ case biJungleM:
+ {
+ if (
+ !isJungleCompatible(above) ||
+ !isJungleCompatible(below) ||
+ !isJungleCompatible(left) ||
+ !isJungleCompatible(right)
+ )
+ {
+ biome = (biome == biJungle) ? biJungleEdge : biJungleEdgeM;
+ }
+ break;
+ } // Jungle biomes
+
+ case biSwampland:
+ case biSwamplandM:
+ {
+ if (
+ IsBiomeNoDownfall(static_cast<EMCSBiome>(above)) ||
+ IsBiomeNoDownfall(static_cast<EMCSBiome>(below)) ||
+ IsBiomeNoDownfall(static_cast<EMCSBiome>(left)) ||
+ IsBiomeNoDownfall(static_cast<EMCSBiome>(right))
+ )
+ {
+ biome = biPlains;
+ }
+ break;
+ } // Swampland biomes
+ } // switch (biome)
+
+ a_Values[x + z * a_SizeX] = biome;
+ } // for x
+ } // for z
+ }
+
+
+protected:
+ Underlying m_Underlying;
+
+
+ bool isMesaCompatible(int a_Biome)
+ {
+ switch (a_Biome)
+ {
+ case biDesert:
+ case biMesa:
+ case biMesaBryce:
+ case biMesaPlateau:
+ case biMesaPlateauF:
+ case biMesaPlateauFM:
+ case biMesaPlateauM:
+ case biOcean:
+ case biDeepOcean:
+ {
+ return true;
+ }
+ default:
+ {
+ return false;
+ }
+ }
+ }
+
+
+ bool isJungleCompatible(int a_Biome)
+ {
+ switch (a_Biome)
+ {
+ case biJungle:
+ case biJungleM:
+ case biJungleEdge:
+ case biJungleEdgeM:
+ case biJungleHills:
+ {
+ return true;
+ }
+ default:
+ {
+ return false;
+ }
+ }
+ }
+};
+
+
+
+
+
+/** Changes biomes in the parent data into their alternate versions ("M" variants), in such places that
+have their alterations set. */
+class cProtIntGenMBiomes:
+ public cProtIntGenWithNoise
+{
+ typedef cProtIntGenWithNoise super;
+
+public:
+ cProtIntGenMBiomes(int a_Seed, Underlying a_Alteration, Underlying a_Underlying):
+ super(a_Seed),
+ m_Underlying(a_Underlying),
+ m_Alteration(a_Alteration)
+ {
+ }
+
+
+ virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override
+ {
+ // Generate the underlying biomes and the alterations:
+ m_Underlying->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, a_Values);
+ int alterations[m_BufferSize];
+ m_Alteration->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, alterations);
+
+ // Wherever alterations are nonzero, change into alternate biome, if available:
+ int len = a_SizeX * a_SizeZ;
+ for (int idx = 0; idx < len; ++idx)
+ {
+ if (alterations[idx] == 0)
+ {
+ continue;
+ }
+
+ // Ice spikes biome was removed from here, because it was generated way too often
+ switch (a_Values[idx])
+ {
+ case biPlains: a_Values[idx] = biSunflowerPlains; break;
+ case biDesert: a_Values[idx] = biDesertM; break;
+ case biExtremeHills: a_Values[idx] = biExtremeHillsM; break;
+ case biForest: a_Values[idx] = biFlowerForest; break;
+ case biTaiga: a_Values[idx] = biTaigaM; break;
+ case biSwampland: a_Values[idx] = biSwamplandM; break;
+ case biJungle: a_Values[idx] = biJungleM; break;
+ case biJungleEdge: a_Values[idx] = biJungleEdgeM; break;
+ case biBirchForest: a_Values[idx] = biBirchForestM; break;
+ case biBirchForestHills: a_Values[idx] = biBirchForestHillsM; break;
+ case biRoofedForest: a_Values[idx] = biRoofedForestM; break;
+ case biColdTaiga: a_Values[idx] = biColdTaigaM; break;
+ case biMegaSpruceTaiga: a_Values[idx] = biMegaSpruceTaiga; break;
+ case biMegaSpruceTaigaHills: a_Values[idx] = biMegaSpruceTaigaHills; break;
+ case biExtremeHillsPlus: a_Values[idx] = biExtremeHillsPlusM; break;
+ case biSavanna: a_Values[idx] = biSavannaM; break;
+ case biSavannaPlateau: a_Values[idx] = biSavannaPlateauM; break;
+ case biMesa: a_Values[idx] = biMesaBryce; break;
+ case biMesaPlateauF: a_Values[idx] = biMesaPlateauFM; break;
+ case biMesaPlateau: a_Values[idx] = biMesaBryce; break;
+ }
+ } // for idx - a_Values[] / alterations[]
+ }
+
+protected:
+ Underlying m_Underlying;
+ Underlying m_Alteration;
+};
+
+
+
+