1
0

Merge pull request #1603 from mc-server/ImprovedNoise

Improved noise
This commit is contained in:
Mattes D 2014-11-20 09:26:05 +01:00
commit f1bddc607c
28 changed files with 1292 additions and 554 deletions

View File

@ -9,6 +9,7 @@ include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/polarssl/include
set(FOLDERS
OSSupport HTTPServer Items Blocks Protocol Generating PolarSSL++ Bindings
WorldStorage Mobs Entities Simulator UI BlockEntities Generating/Prefabs
Noise
)
SET (SRCS
@ -50,7 +51,6 @@ SET (SRCS
MobProximityCounter.cpp
MobSpawner.cpp
MonsterConfig.cpp
Noise.cpp
ProbabDistrib.cpp
RankManager.cpp
RCONServer.cpp
@ -65,7 +65,8 @@ SET (SRCS
VoronoiMap.cpp
WebAdmin.cpp
World.cpp
main.cpp)
main.cpp
)
SET (HDRS
AllocationPool.h
@ -120,7 +121,6 @@ SET (HDRS
MobProximityCounter.h
MobSpawner.h
MonsterConfig.h
Noise.h
ProbabDistrib.h
RankManager.h
RCONServer.h
@ -137,7 +137,8 @@ SET (HDRS
VoronoiMap.h
WebAdmin.h
World.h
XMLParser.h)
XMLParser.h
)
include_directories(".")
include_directories ("${CMAKE_CURRENT_SOURCE_DIR}/../lib/sqlite")
@ -314,7 +315,7 @@ endif ()
if (NOT MSVC)
target_link_libraries(${EXECUTABLE}
OSSupport HTTPServer Bindings Items Blocks
OSSupport HTTPServer Bindings Items Blocks Noise
Protocol Generating Generating_Prefabs WorldStorage
Mobs Entities Simulator UI BlockEntities PolarSSL++
)

View File

@ -25,7 +25,7 @@
#include "BlockEntities/FlowerPotEntity.h"
#include "Entities/Pickup.h"
#include "Item.h"
#include "Noise.h"
#include "Noise/Noise.h"
#include "Root.h"
#include "MersenneTwister.h"
#include "Entities/Player.h"

View File

@ -6,7 +6,7 @@
#include "Enchantments.h"
#include "WorldStorage/FastNBT.h"
#include "FastRandom.h"
#include "Noise.h"
#include "Noise/Noise.h"

View File

@ -15,7 +15,7 @@ Interfaces to the various biome generators:
#pragma once
#include "ComposableGenerator.h"
#include "../Noise.h"
#include "../Noise/Noise.h"
#include "../VoronoiMap.h"

View File

@ -13,7 +13,6 @@
#pragma once
#include "GridStructGen.h"
#include "../Noise.h"

View File

@ -7,7 +7,7 @@
#include "ChunkDesc.h"
#include "../BlockArea.h"
#include "../Cuboid.h"
#include "../Noise.h"
#include "../Noise/Noise.h"
#include "../BlockEntities/BlockEntity.h"

View File

@ -17,7 +17,7 @@
#pragma once
#include "ComposableGenerator.h"
#include "../Noise.h"
#include "../Noise/Noise.h"

View File

@ -11,7 +11,6 @@
#include "ComposableGenerator.h"
#include "HeiGen.h"
#include "../Noise.h"

View File

@ -10,7 +10,7 @@
#pragma once
#include "ComposableGenerator.h"
#include "../Noise.h"
#include "../Noise/Noise.h"

View File

@ -10,7 +10,6 @@
#include "Globals.h"
#include "FinishGen.h"
#include "../Noise.h"
#include "../BlockID.h"
#include "../Simulator/FluidSimulator.h" // for cFluidSimulator::CanWashAway()
#include "../Simulator/FireSimulator.h"

View File

@ -16,7 +16,7 @@
#include "ComposableGenerator.h"
#include "../Noise.h"
#include "../Noise/Noise.h"
#include "../ProbabDistrib.h"

View File

@ -10,7 +10,7 @@
#pragma once
#include "ComposableGenerator.h"
#include "../Noise.h"
#include "../Noise/Noise.h"

View File

@ -15,7 +15,7 @@ Interfaces to the various height generators:
#pragma once
#include "ComposableGenerator.h"
#include "../Noise.h"
#include "../Noise/Noise.h"

View File

@ -10,7 +10,6 @@
#pragma once
#include "GridStructGen.h"
#include "../Noise.h"

View File

@ -6,6 +6,7 @@
#include "Globals.h"
#include "Noise3DGenerator.h"
#include "../OSSupport/File.h"
#include "../OSSupport/Timer.h"
#include "../IniFile.h"
#include "../LinearInterpolation.h"
#include "../LinearUpscale.h"
@ -61,30 +62,86 @@ public:
/** Linearly interpolates between two values.
Assumes that a_Ratio is in range [0, 1]. */
inline static NOISE_DATATYPE Lerp(NOISE_DATATYPE a_Val1, NOISE_DATATYPE a_Val2, NOISE_DATATYPE a_Ratio)
#if 0
// Perform speed test of the cInterpolNoise class
static class cInterpolNoiseSpeedTest
{
return a_Val1 + (a_Val2 - a_Val1) * a_Ratio;
}
/** Linearly interpolates between two values, clamping the ratio to [0, 1] first. */
inline static NOISE_DATATYPE ClampedLerp(NOISE_DATATYPE a_Val1, NOISE_DATATYPE a_Val2, NOISE_DATATYPE a_Ratio)
{
if (a_Ratio < 0)
public:
cInterpolNoiseSpeedTest(void)
{
return a_Val1;
TestSpeed2D();
TestSpeed3D();
printf("InterpolNoise speed comparison finished.\n");
}
if (a_Ratio > 1)
/** Compare the speed of the 3D InterpolNoise vs 3D CubicNoise. */
void TestSpeed3D(void)
{
return a_Val2;
printf("Evaluating 3D noise performance...\n");
static const int SIZE_X = 128;
static const int SIZE_Y = 128;
static const int SIZE_Z = 128;
static const NOISE_DATATYPE MUL = 80;
std::unique_ptr<NOISE_DATATYPE[]> arr(new NOISE_DATATYPE[SIZE_X * SIZE_Y * SIZE_Z]);
cTimer timer;
// Test the cInterpolNoise:
cInterpolNoise<Interp5Deg> interpNoise(1);
long long start = timer.GetNowTime();
for (int i = 0; i < 30; i++)
{
interpNoise.Generate3D(arr.get(), SIZE_X, SIZE_Y, SIZE_Z, MUL * i, MUL * i + MUL, 0, MUL, 0, MUL);
}
long long end = timer.GetNowTime();
printf("InterpolNoise took %.02f sec\n", static_cast<float>(end - start) / 1000);
// Test the cCubicNoise:
cCubicNoise cubicNoise(1);
start = timer.GetNowTime();
for (int i = 0; i < 30; i++)
{
cubicNoise.Generate3D(arr.get(), SIZE_X, SIZE_Y, SIZE_Z, MUL * i, MUL * i + MUL, 0, MUL, 0, MUL);
}
end = timer.GetNowTime();
printf("CubicNoise took %.02f sec\n", static_cast<float>(end - start) / 1000);
printf("3D noise performance comparison finished.\n");
}
return Lerp(a_Val1, a_Val2, a_Ratio);
}
/** Compare the speed of the 2D InterpolNoise vs 2D CubicNoise. */
void TestSpeed2D(void)
{
printf("Evaluating 2D noise performance...\n");
static const int SIZE_X = 128;
static const int SIZE_Y = 128;
static const NOISE_DATATYPE MUL = 80;
std::unique_ptr<NOISE_DATATYPE[]> arr(new NOISE_DATATYPE[SIZE_X * SIZE_Y]);
cTimer timer;
// Test the cInterpolNoise:
cInterpolNoise<Interp5Deg> interpNoise(1);
long long start = timer.GetNowTime();
for (int i = 0; i < 500; i++)
{
interpNoise.Generate2D(arr.get(), SIZE_X, SIZE_Y, MUL * i, MUL * i + MUL, 0, MUL);
}
long long end = timer.GetNowTime();
printf("InterpolNoise took %.02f sec\n", static_cast<float>(end - start) / 1000);
// Test the cCubicNoise:
cCubicNoise cubicNoise(1);
start = timer.GetNowTime();
for (int i = 0; i < 500; i++)
{
cubicNoise.Generate2D(arr.get(), SIZE_X, SIZE_Y, MUL * i, MUL * i + MUL, 0, MUL);
}
end = timer.GetNowTime();
printf("CubicNoise took %.02f sec\n", static_cast<float>(end - start) / 1000);
printf("2D noise performance comparison finished.\n");
}
} g_InterpolNoiseSpeedTest;
#endif
@ -98,9 +155,17 @@ cNoise3DGenerator::cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator) :
m_Perlin(1000),
m_Cubic(1000)
{
m_Perlin.AddOctave(1, (NOISE_DATATYPE)0.5);
m_Perlin.AddOctave((NOISE_DATATYPE)0.5, 1);
m_Perlin.AddOctave((NOISE_DATATYPE)0.5, 2);
m_Perlin.AddOctave(1, 1);
m_Perlin.AddOctave(2, 0.5);
m_Perlin.AddOctave(4, 0.25);
m_Perlin.AddOctave(8, 0.125);
m_Perlin.AddOctave(16, 0.0625);
m_Cubic.AddOctave(1, 1);
m_Cubic.AddOctave(2, 0.5);
m_Cubic.AddOctave(4, 0.25);
m_Cubic.AddOctave(8, 0.125);
m_Cubic.AddOctave(16, 0.0625);
#if 0
// DEBUG: Test the noise generation:
@ -183,8 +248,8 @@ void cNoise3DGenerator::Initialize(cIniFile & a_IniFile)
{
// Params:
m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62);
m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0);
m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 75);
m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0.1);
m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 68);
m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 8);
m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyY", 8);
m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyZ", 8);
@ -249,10 +314,10 @@ void cNoise3DGenerator::GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DAT
NOISE_DATATYPE NoiseW[DIM_X * DIM_Y * DIM_Z]; // Workspace that the noise calculation can use and trash
// Our noise array has different layout, XZY, instead of regular chunk's XYZ, that's why the coords are "renamed"
NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width)) / m_FrequencyX;
NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((a_ChunkX + 1) * cChunkDef::Width) - 1) / m_FrequencyX;
NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width)) / m_FrequencyZ;
NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((a_ChunkZ + 1) * cChunkDef::Width) - 1) / m_FrequencyZ;
NOISE_DATATYPE StartX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width)) / m_FrequencyX;
NOISE_DATATYPE EndX = ((NOISE_DATATYPE)((a_ChunkX + 1) * cChunkDef::Width)) / m_FrequencyX;
NOISE_DATATYPE StartZ = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width)) / m_FrequencyZ;
NOISE_DATATYPE EndZ = ((NOISE_DATATYPE)((a_ChunkZ + 1) * cChunkDef::Width)) / m_FrequencyZ;
NOISE_DATATYPE StartY = 0;
NOISE_DATATYPE EndY = ((NOISE_DATATYPE)256) / m_FrequencyY;
@ -262,23 +327,23 @@ void cNoise3DGenerator::GenerateNoiseArray(int a_ChunkX, int a_ChunkZ, NOISE_DAT
// Precalculate a "height" array:
NOISE_DATATYPE Height[DIM_X * DIM_Z]; // Output for the cubic noise heightmap ("source")
m_Cubic.Generate2D(Height, DIM_X, DIM_Z, StartX / 25, EndX / 25, StartZ / 25, EndZ / 25);
m_Cubic.Generate2D(Height, DIM_X, DIM_Z, StartX / 5, EndX / 5, StartZ / 5, EndZ / 5);
for (size_t i = 0; i < ARRAYCOUNT(Height); i++)
{
Height[i] = std::abs(Height[i]) * m_HeightAmplification + 1;
Height[i] = Height[i] * m_HeightAmplification;
}
// Modify the noise by height data:
for (int y = 0; y < DIM_Y; y++)
{
NOISE_DATATYPE AddHeight = (y * UPSCALE_Y - m_MidPoint) / 20;
AddHeight *= AddHeight * AddHeight;
NOISE_DATATYPE AddHeight = (y * UPSCALE_Y - m_MidPoint) / 30;
// AddHeight *= AddHeight * AddHeight;
for (int z = 0; z < DIM_Z; z++)
{
NOISE_DATATYPE * CurRow = &(NoiseO[y * DIM_X + z * DIM_X * DIM_Y]);
for (int x = 0; x < DIM_X; x++)
{
CurRow[x] += AddHeight / Height[x + DIM_X * z];
CurRow[x] += AddHeight + Height[x + DIM_X * z];
}
}
}

View File

@ -13,7 +13,8 @@
#pragma once
#include "ComposableGenerator.h"
#include "../Noise.h"
#include "../Noise/Noise.h"
#include "../Noise/InterpolNoise.h"
@ -34,17 +35,20 @@ public:
protected:
// Linear interpolation step sizes, must be divisors of cChunkDef::Width and cChunkDef::Height, respectively:
static const int UPSCALE_X = 8;
static const int UPSCALE_Y = 4;
static const int UPSCALE_Z = 8;
static const int UPSCALE_X = 4;
static const int UPSCALE_Y = 8;
static const int UPSCALE_Z = 4;
// Linear interpolation buffer dimensions, calculated from the step sizes:
static const int DIM_X = 1 + cChunkDef::Width / UPSCALE_X;
static const int DIM_Y = 1 + cChunkDef::Height / UPSCALE_Y;
static const int DIM_Z = 1 + cChunkDef::Width / UPSCALE_Z;
cPerlinNoise m_Perlin; // The base 3D noise source for the actual composition
cCubicNoise m_Cubic; // The noise used for heightmap directing
/** The base 3D noise source for the actual composition */
cOctavedNoise<cInterp5DegNoise> m_Perlin;
/** The noise used for heightmap directing. */
cOctavedNoise<cInterp5DegNoise> m_Cubic;
int m_SeaLevel;
NOISE_DATATYPE m_HeightAmplification;

View File

@ -20,7 +20,7 @@ Each uses a slightly different approach to generating:
#include "../Defines.h"
#include "../Cuboid.h"
#include "../Noise.h"
#include "../Noise/Noise.h"

View File

@ -10,7 +10,6 @@
#pragma once
#include "GridStructGen.h"
#include "../Noise.h"

View File

@ -14,7 +14,7 @@
#pragma once
#include "ComposableGenerator.h"
#include "../Noise.h"
#include "../Noise/Noise.h"

View File

@ -18,7 +18,7 @@ logs can overwrite others(leaves), but others shouldn't overwrite logs. This is
#pragma once
#include "../ChunkDef.h"
#include "../Noise.h"
#include "../Noise/Noise.h"

View File

@ -6,7 +6,7 @@
#include "Globals.h"
#include "ItemGrid.h"
#include "Items/ItemHandler.h"
#include "Noise.h"
#include "Noise/Noise.h"

21
src/Noise/CMakeLists.txt Normal file
View File

@ -0,0 +1,21 @@
cmake_minimum_required (VERSION 2.6)
project (MCServer)
include_directories ("${PROJECT_SOURCE_DIR}/../")
SET (SRCS
Noise.cpp
)
SET (HDRS
Noise.h
OctavedNoise.h
RidgedNoise.h
)
if(NOT MSVC)
add_library(Noise ${SRCS} ${HDRS})
target_link_libraries(Noise OSSupport)
endif()

524
src/Noise/InterpolNoise.h Normal file
View File

@ -0,0 +1,524 @@
// InterpolNoise.h
// Implements the cInterpolNoise class template representing a noise that interpolates the values between integer coords from a single set of neighbors
#pragma once
#include "Noise.h"
#define FAST_FLOOR(x) (((x) < 0) ? (((int)x) - 1) : ((int)x))
////////////////////////////////////////////////////////////////////////////////
// cInterpolCell2D:
template <typename T>
class cInterpolCell2D
{
public:
cInterpolCell2D(
const cNoise & a_Noise, ///< Noise to use for generating the random values
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
const NOISE_DATATYPE * a_FracX, ///< Pointer to the array that stores the X fractional values
const NOISE_DATATYPE * a_FracY ///< Pointer to the attay that stores the Y fractional values
):
m_Noise(a_Noise),
m_WorkRnds(&m_Workspace1),
m_CurFloorX(0),
m_CurFloorY(0),
m_Array(a_Array),
m_SizeX(a_SizeX),
m_SizeY(a_SizeY),
m_FracX(a_FracX),
m_FracY(a_FracY)
{
}
/** Generates part of the output noise array using the current m_WorkRnds[] values */
void Generate(
int a_FromX, int a_ToX,
int a_FromY, int a_ToY
)
{
for (int y = a_FromY; y < a_ToY; y++)
{
NOISE_DATATYPE Interp[2];
NOISE_DATATYPE FracY = T::coeff(m_FracY[y]);
Interp[0] = Lerp((*m_WorkRnds)[0][0], (*m_WorkRnds)[0][1], FracY);
Interp[1] = Lerp((*m_WorkRnds)[1][0], (*m_WorkRnds)[1][1], FracY);
int idx = y * m_SizeX + a_FromX;
for (int x = a_FromX; x < a_ToX; x++)
{
m_Array[idx++] = Lerp(Interp[0], Interp[1], T::coeff(m_FracX[x]));
} // for x
} // for y
}
/** Initializes m_WorkRnds[] with the specified values of the noise at the specified integral coords. */
void InitWorkRnds(int a_FloorX, int a_FloorY)
{
m_CurFloorX = a_FloorX;
m_CurFloorY = a_FloorY;
(*m_WorkRnds)[0][0] = m_Noise.IntNoise2D(m_CurFloorX, m_CurFloorY);
(*m_WorkRnds)[0][1] = m_Noise.IntNoise2D(m_CurFloorX, m_CurFloorY + 1);
(*m_WorkRnds)[1][0] = m_Noise.IntNoise2D(m_CurFloorX + 1, m_CurFloorY);
(*m_WorkRnds)[1][1] = m_Noise.IntNoise2D(m_CurFloorX + 1, m_CurFloorY + 1);
}
/** Updates m_WorkRnds[] for the new integral coords */
void Move(int a_NewFloorX, int a_NewFloorY)
{
// Swap the doublebuffer:
int OldFloorX = m_CurFloorX;
int OldFloorY = m_CurFloorY;
Workspace * OldWorkRnds = m_WorkRnds;
m_WorkRnds = (m_WorkRnds == &m_Workspace1) ? &m_Workspace2 : &m_Workspace1;
// Reuse as much of the old workspace as possible:
// TODO: Try out if simply calculating all 4 elements each time is faster than this monster loop
int DiffX = OldFloorX - a_NewFloorX;
int DiffY = OldFloorY - a_NewFloorY;
for (int x = 0; x < 2; x++)
{
int cx = a_NewFloorX + x;
int OldX = x - DiffX; // Where would this X be in the old grid?
for (int y = 0; y < 2; y++)
{
int cy = a_NewFloorY + y;
int OldY = y - DiffY; // Where would this Y be in the old grid?
if ((OldX >= 0) && (OldX < 2) && (OldY >= 0) && (OldY < 2))
{
(*m_WorkRnds)[x][y] = (*OldWorkRnds)[OldX][OldY];
}
else
{
(*m_WorkRnds)[x][y] = (NOISE_DATATYPE)m_Noise.IntNoise2D(cx, cy);
}
}
}
m_CurFloorX = a_NewFloorX;
m_CurFloorY = a_NewFloorY;
}
protected:
typedef NOISE_DATATYPE Workspace[2][2];
/** The noise used for generating the values at integral coords. */
const cNoise & m_Noise;
/** The current random values; points to either m_Workspace1 or m_Workspace2 (doublebuffering) */
Workspace * m_WorkRnds;
/** Buffer 1 for workspace doublebuffering, used in Move() */
Workspace m_Workspace1;
/** Buffer 2 for workspace doublebuffering, used in Move() */
Workspace m_Workspace2;
/** Coords of the currently calculated m_WorkRnds[]. */
int m_CurFloorX, m_CurFloorY;
/** The output array to generate into. */
NOISE_DATATYPE * m_Array;
/** Dimensions of the output array. */
int m_SizeX, m_SizeY;
/** Arrays holding the fractional values of the coords in each direction. */
const NOISE_DATATYPE * m_FracX;
const NOISE_DATATYPE * m_FracY;
} ;
////////////////////////////////////////////////////////////////////////////////
// cInterpolCell3D:
/** Holds a cache of the last calculated integral noise values and interpolates between them en masse.
Provides a massive optimization for cInterpolNoise.
Works by calculating multiple noise values (that have the same integral noise coords) at once. The underlying noise values
needn't be recalculated for these values, only the interpolation is done within the unit cube. */
template <typename T>
class cInterpolCell3D
{
public:
cInterpolCell3D(
const cNoise & a_Noise, ///< Noise to use for generating the random values
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
const NOISE_DATATYPE * a_FracX, ///< Pointer to the array that stores the X fractional values
const NOISE_DATATYPE * a_FracY, ///< Pointer to the attay that stores the Y fractional values
const NOISE_DATATYPE * a_FracZ ///< Pointer to the array that stores the Z fractional values
):
m_Noise(a_Noise),
m_WorkRnds(&m_Workspace1),
m_CurFloorX(0),
m_CurFloorY(0),
m_CurFloorZ(0),
m_Array(a_Array),
m_SizeX(a_SizeX),
m_SizeY(a_SizeY),
m_SizeZ(a_SizeZ),
m_FracX(a_FracX),
m_FracY(a_FracY),
m_FracZ(a_FracZ)
{
}
/** Generates part of the output array using current m_WorkRnds[]. */
void Generate(
int a_FromX, int a_ToX,
int a_FromY, int a_ToY,
int a_FromZ, int a_ToZ
)
{
for (int z = a_FromZ; z < a_ToZ; z++)
{
int idxZ = z * m_SizeX * m_SizeY;
NOISE_DATATYPE Interp2[2][2];
NOISE_DATATYPE FracZ = T::coeff(m_FracZ[z]);
for (int x = 0; x < 2; x++)
{
for (int y = 0; y < 2; y++)
{
Interp2[x][y] = Lerp((*m_WorkRnds)[x][y][0], (*m_WorkRnds)[x][y][1], FracZ);
}
}
for (int y = a_FromY; y < a_ToY; y++)
{
NOISE_DATATYPE Interp[2];
NOISE_DATATYPE FracY = T::coeff(m_FracY[y]);
Interp[0] = Lerp(Interp2[0][0], Interp2[0][1], FracY);
Interp[1] = Lerp(Interp2[1][0], Interp2[1][1], FracY);
int idx = idxZ + y * m_SizeX + a_FromX;
for (int x = a_FromX; x < a_ToX; x++)
{
m_Array[idx++] = Lerp(Interp[0], Interp[1], T::coeff(m_FracX[x]));
} // for x
} // for y
} // for z
}
/** Initializes m_WorkRnds[] with the specified Floor values. */
void InitWorkRnds(int a_FloorX, int a_FloorY, int a_FloorZ)
{
m_CurFloorX = a_FloorX;
m_CurFloorY = a_FloorY;
m_CurFloorZ = a_FloorZ;
(*m_WorkRnds)[0][0][0] = (NOISE_DATATYPE)m_Noise.IntNoise3D(m_CurFloorX, m_CurFloorY, m_CurFloorZ);
(*m_WorkRnds)[0][0][1] = (NOISE_DATATYPE)m_Noise.IntNoise3D(m_CurFloorX, m_CurFloorY, m_CurFloorZ + 1);
(*m_WorkRnds)[0][1][0] = (NOISE_DATATYPE)m_Noise.IntNoise3D(m_CurFloorX, m_CurFloorY + 1, m_CurFloorZ);
(*m_WorkRnds)[0][1][1] = (NOISE_DATATYPE)m_Noise.IntNoise3D(m_CurFloorX, m_CurFloorY + 1, m_CurFloorZ + 1);
(*m_WorkRnds)[1][0][0] = (NOISE_DATATYPE)m_Noise.IntNoise3D(m_CurFloorX + 1, m_CurFloorY, m_CurFloorZ);
(*m_WorkRnds)[1][0][1] = (NOISE_DATATYPE)m_Noise.IntNoise3D(m_CurFloorX + 1, m_CurFloorY, m_CurFloorZ + 1);
(*m_WorkRnds)[1][1][0] = (NOISE_DATATYPE)m_Noise.IntNoise3D(m_CurFloorX + 1, m_CurFloorY + 1, m_CurFloorZ);
(*m_WorkRnds)[1][1][1] = (NOISE_DATATYPE)m_Noise.IntNoise3D(m_CurFloorX + 1, m_CurFloorY + 1, m_CurFloorZ + 1);
}
/** Updates m_WorkRnds[] for the new Floor values. */
void Move(int a_NewFloorX, int a_NewFloorY, int a_NewFloorZ)
{
// Swap the doublebuffer:
int OldFloorX = m_CurFloorX;
int OldFloorY = m_CurFloorY;
int OldFloorZ = m_CurFloorZ;
Workspace * OldWorkRnds = m_WorkRnds;
m_WorkRnds = (m_WorkRnds == &m_Workspace1) ? &m_Workspace2 : &m_Workspace1;
// Reuse as much of the old workspace as possible:
// TODO: Try out if simply calculating all 8 elements each time is faster than this monster loop
int DiffX = OldFloorX - a_NewFloorX;
int DiffY = OldFloorY - a_NewFloorY;
int DiffZ = OldFloorZ - a_NewFloorZ;
for (int x = 0; x < 2; x++)
{
int cx = a_NewFloorX + x;
int OldX = x - DiffX; // Where would this X be in the old grid?
for (int y = 0; y < 2; y++)
{
int cy = a_NewFloorY + y;
int OldY = y - DiffY; // Where would this Y be in the old grid?
for (int z = 0; z < 2; z++)
{
int cz = a_NewFloorZ + z;
int OldZ = z - DiffZ;
if ((OldX >= 0) && (OldX < 2) && (OldY >= 0) && (OldY < 2) && (OldZ >= 0) && (OldZ < 2))
{
(*m_WorkRnds)[x][y][z] = (*OldWorkRnds)[OldX][OldY][OldZ];
}
else
{
(*m_WorkRnds)[x][y][z] = (NOISE_DATATYPE)m_Noise.IntNoise3D(cx, cy, cz);
}
} // for z
} // for y
} // for x
m_CurFloorX = a_NewFloorX;
m_CurFloorY = a_NewFloorY;
m_CurFloorZ = a_NewFloorZ;
}
protected:
typedef NOISE_DATATYPE Workspace[2][2][2];
/** The noise used for generating the values at integral coords. */
const cNoise & m_Noise;
/** The current random values; points to either m_Workspace1 or m_Workspace2 (doublebuffering) */
Workspace * m_WorkRnds;
/** Buffer 1 for workspace doublebuffering, used in Move() */
Workspace m_Workspace1;
/** Buffer 2 for workspace doublebuffering, used in Move() */
Workspace m_Workspace2;
/** The integral coords of the currently calculated WorkRnds[] */
int m_CurFloorX, m_CurFloorY, m_CurFloorZ;
/** The output array where the noise is calculated. */
NOISE_DATATYPE * m_Array;
/** Dimensions of the output array. */
int m_SizeX, m_SizeY, m_SizeZ;
/** Arrays holding the fractional values of the coords in each direction. */
const NOISE_DATATYPE * m_FracX;
const NOISE_DATATYPE * m_FracY;
const NOISE_DATATYPE * m_FracZ;
} ;
////////////////////////////////////////////////////////////////////////////////
// cInterpolNoise:
template <typename T>
class cInterpolNoise
{
/** Maximum size, for each direction, of the generated array. */
static const int MAX_SIZE = 256;
public:
cInterpolNoise(int a_Seed):
m_Noise(a_Seed)
{
}
/** Sets a new seed for the generators. Relays the seed to the underlying noise. */
void SetSeed(int a_Seed)
{
m_Noise.SetSeed(a_Seed);
}
/** Fills a 2D array with the values of the noise. */
void Generate2D(
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY ///< Noise-space coords of the array in the Y direction
) const
{
ASSERT(a_SizeX > 0);
ASSERT(a_SizeY > 0);
ASSERT(a_SizeX < MAX_SIZE);
ASSERT(a_SizeY < MAX_SIZE);
ASSERT(a_StartX < a_EndX);
ASSERT(a_StartY < a_EndY);
// Calculate the integral and fractional parts of each coord:
int FloorX[MAX_SIZE];
int FloorY[MAX_SIZE];
NOISE_DATATYPE FracX[MAX_SIZE];
NOISE_DATATYPE FracY[MAX_SIZE];
int SameX[MAX_SIZE];
int SameY[MAX_SIZE];
int NumSameX, NumSameY;
CalcFloorFrac(a_SizeX, a_StartX, a_EndX, FloorX, FracX, SameX, NumSameX);
CalcFloorFrac(a_SizeY, a_StartY, a_EndY, FloorY, FracY, SameY, NumSameY);
cInterpolCell2D<T> Cell(m_Noise, a_Array, a_SizeX, a_SizeY, FracX, FracY);
Cell.InitWorkRnds(FloorX[0], FloorY[0]);
// Calculate query values using Cell:
int FromY = 0;
for (int y = 0; y < NumSameY; y++)
{
int ToY = FromY + SameY[y];
int FromX = 0;
int CurFloorY = FloorY[FromY];
for (int x = 0; x < NumSameX; x++)
{
int ToX = FromX + SameX[x];
Cell.Generate(FromX, ToX, FromY, ToY);
Cell.Move(FloorX[ToX], CurFloorY);
FromX = ToX;
} // for x
Cell.Move(FloorX[0], FloorY[ToY]);
FromY = ToY;
} // for y
}
/** Fills a 3D array with the values of the noise. */
void Generate3D(
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ ///< Noise-space coords of the array in the Z direction
) const
{
// Check params:
ASSERT(a_SizeX > 1);
ASSERT(a_SizeY > 1);
ASSERT(a_SizeX < MAX_SIZE);
ASSERT(a_SizeY < MAX_SIZE);
ASSERT(a_SizeZ < MAX_SIZE);
ASSERT(a_StartX < a_EndX);
ASSERT(a_StartY < a_EndY);
ASSERT(a_StartZ < a_EndZ);
// Calculate the integral and fractional parts of each coord:
int FloorX[MAX_SIZE];
int FloorY[MAX_SIZE];
int FloorZ[MAX_SIZE];
NOISE_DATATYPE FracX[MAX_SIZE];
NOISE_DATATYPE FracY[MAX_SIZE];
NOISE_DATATYPE FracZ[MAX_SIZE];
int SameX[MAX_SIZE];
int SameY[MAX_SIZE];
int SameZ[MAX_SIZE];
int NumSameX, NumSameY, NumSameZ;
CalcFloorFrac(a_SizeX, a_StartX, a_EndX, FloorX, FracX, SameX, NumSameX);
CalcFloorFrac(a_SizeY, a_StartY, a_EndY, FloorY, FracY, SameY, NumSameY);
CalcFloorFrac(a_SizeZ, a_StartZ, a_EndZ, FloorZ, FracZ, SameZ, NumSameZ);
cInterpolCell3D<T> Cell(
m_Noise, a_Array,
a_SizeX, a_SizeY, a_SizeZ,
FracX, FracY, FracZ
);
Cell.InitWorkRnds(FloorX[0], FloorY[0], FloorZ[0]);
// Calculate query values using Cell:
int FromZ = 0;
for (int z = 0; z < NumSameZ; z++)
{
int ToZ = FromZ + SameZ[z];
int CurFloorZ = FloorZ[FromZ];
int FromY = 0;
for (int y = 0; y < NumSameY; y++)
{
int ToY = FromY + SameY[y];
int CurFloorY = FloorY[FromY];
int FromX = 0;
for (int x = 0; x < NumSameX; x++)
{
int ToX = FromX + SameX[x];
Cell.Generate(FromX, ToX, FromY, ToY, FromZ, ToZ);
Cell.Move(FloorX[ToX], CurFloorY, CurFloorZ);
FromX = ToX;
}
Cell.Move(FloorX[0], FloorY[ToY], CurFloorZ);
FromY = ToY;
} // for y
Cell.Move(FloorX[0], FloorY[0], FloorZ[ToZ]);
FromZ = ToZ;
} // for z
}
protected:
/** The noise used for the underlying value generation. */
cNoise m_Noise;
/** Calculates the integral and fractional parts along one axis.
a_Floor will receive the integral parts (array of a_Size ints).
a_Frac will receive the fractional parts (array of a_Size floats).
a_Same will receive the counts of items that have the same integral parts (array of up to a_Size ints).
a_NumSame will receive the count of a_Same elements (total count of different integral parts). */
void CalcFloorFrac(
int a_Size,
NOISE_DATATYPE a_Start, NOISE_DATATYPE a_End,
int * a_Floor, NOISE_DATATYPE * a_Frac,
int * a_Same, int & a_NumSame
) const
{
ASSERT(a_Size > 0);
// Calculate the floor and frac values:
NOISE_DATATYPE val = a_Start;
NOISE_DATATYPE dif = (a_End - a_Start) / (a_Size - 1);
for (int i = 0; i < a_Size; i++)
{
a_Floor[i] = FAST_FLOOR(val);
a_Frac[i] = val - a_Floor[i];
val += dif;
}
// Mark up the same floor values into a_Same / a_NumSame:
int CurFloor = a_Floor[0];
int LastSame = 0;
a_NumSame = 0;
for (int i = 1; i < a_Size; i++)
{
if (a_Floor[i] != CurFloor)
{
a_Same[a_NumSame] = i - LastSame;
LastSame = i;
a_NumSame += 1;
CurFloor = a_Floor[i];
}
} // for i - a_Floor[]
if (LastSame < a_Size)
{
a_Same[a_NumSame] = a_Size - LastSame;
a_NumSame += 1;
}
}
};
/** A fifth-degree curve for interpolating.
Implemented as a functor for better chance of inlining. */
struct Interp5Deg
{
static NOISE_DATATYPE coeff(NOISE_DATATYPE a_Val)
{
return a_Val * a_Val * a_Val * (a_Val * (a_Val * 6 - 15) + 10);
}
};
typedef cInterpolNoise<Interp5Deg> cInterp5DegNoise;

View File

@ -2,6 +2,7 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Noise.h"
#include "OSSupport/Timer.h"
#define FAST_FLOOR(x) (((x) < 0) ? (((int)x) - 1) : ((int)x))
@ -9,10 +10,110 @@
#if 0
/** cImprovedPerlin noise test suite:
- Generate a rather large 2D and 3D noise array and output it to a file
- Compare performance of cCubicNoise and cImprovedNoise, both in single-value and 3D-array usages */
static class cImprovedPerlinNoiseTest
{
public:
cImprovedPerlinNoiseTest(void)
{
printf("Performing Improved Perlin Noise tests...\n");
TestImage();
TestSpeed();
TestSpeedArr();
printf("Improved Perlin Noise tests complete.\n");
}
/** Tests the noise by generating 2D and 3D images and dumping them to files. */
void TestImage(void)
{
static const int SIZE_X = 256;
static const int SIZE_Y = 256;
static const int SIZE_Z = 16;
cImprovedNoise noise(1);
std::unique_ptr<NOISE_DATATYPE[]> arr(new NOISE_DATATYPE[SIZE_X * SIZE_Y * SIZE_Z]);
noise.Generate3D(arr.get(), SIZE_X, SIZE_Y, SIZE_Z, 0, 14, 0, 14, 0, 14);
Debug3DNoise(arr.get(), SIZE_X, SIZE_Y, SIZE_Z, "ImprovedPerlinNoiseTest3D", 128);
noise.Generate2D(arr.get(), SIZE_X, SIZE_Y, 0, 14, 15, 28);
Debug2DNoise(arr.get(), SIZE_X, SIZE_Y, "ImprovedPerlinNoiseTest2D", 128);
}
/** Tests the speeds of cImprovedPerlin and cCubicNoise when generating individual values. */
void TestSpeed(void)
{
cImprovedNoise improvedNoise(1);
cNoise noise(1);
cTimer timer;
// Measure the improvedNoise:
NOISE_DATATYPE sum = 0;
long long start = timer.GetNowTime();
for (int i = 0; i < 100000000; i++)
{
sum += improvedNoise.GetValueAt(i, 0, -i);
}
long long finish = timer.GetNowTime();
printf("cImprovedNoise took %.2f seconds; total is %f.\n", static_cast<float>(finish - start) / 1000.0f, sum);
// Measure the cubicNoise:
sum = 0;
start = timer.GetNowTime();
for (int i = 0; i < 100000000; i++)
{
sum += noise.IntNoise3D(i, 0, -i);
}
finish = timer.GetNowTime();
printf("cCubicNoise took %.2f seconds; total is %f.\n", static_cast<float>(finish - start) / 1000.0f, sum);
}
/** Tests the speeds of cImprovedPerlin and cCubicNoise when generating arrays. */
void TestSpeedArr(void)
{
static const int SIZE_X = 256;
static const int SIZE_Y = 256;
static const int SIZE_Z = 16;
std::unique_ptr<NOISE_DATATYPE[]> arr(new NOISE_DATATYPE[SIZE_X * SIZE_Y * SIZE_Z]);
cTimer timer;
cImprovedNoise improvedNoise(1);
cCubicNoise cubicNoise(1);
// Measure the improvedNoise:
long long start = timer.GetNowTime();
for (int i = 0; i < 40; i++)
{
improvedNoise.Generate3D(arr.get(), SIZE_X, SIZE_Y, SIZE_Z, 0, 14, 0, 14, 0, 14);
}
long long finish = timer.GetNowTime();
printf("cImprovedNoise(arr) took %.2f seconds.\n", static_cast<float>(finish - start) / 1000.0f);
// Measure the cubicNoise:
start = timer.GetNowTime();
for (int i = 0; i < 40; i++)
{
cubicNoise.Generate3D(arr.get(), SIZE_X, SIZE_Y, SIZE_Z, 0, 14, 0, 14, 0, 14);
}
finish = timer.GetNowTime();
printf("cCubicNoise(arr) took %.2f seconds.\n", static_cast<float>(finish - start) / 1000.0f);
}
} g_Test;
#endif
////////////////////////////////////////////////////////////////////////////////
// Globals:
void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase)
void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase, NOISE_DATATYPE a_Coeff)
{
const int BUF_SIZE = 512;
ASSERT(a_SizeX <= BUF_SIZE); // Just stretch it, if needed
@ -29,7 +130,7 @@ void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int
unsigned char buf[BUF_SIZE];
for (int x = 0; x < a_SizeX; x++)
{
buf[x] = (unsigned char)(std::min(255, std::max(0, (int)(128 + 32 * a_Noise[idx++]))));
buf[x] = static_cast<unsigned char>(Clamp((int)(128 + a_Coeff * a_Noise[idx++]), 0, 255));
}
f1.Write(buf, a_SizeX);
} // for y
@ -50,7 +151,7 @@ void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int
unsigned char buf[BUF_SIZE];
for (int x = 0; x < a_SizeX; x++)
{
buf[x] = (unsigned char)(std::min(255, std::max(0, (int)(128 + 32 * a_Noise[idx++]))));
buf[x] = static_cast<unsigned char>(Clamp((int)(128 + a_Coeff * a_Noise[idx++]), 0, 255));
}
f2.Write(buf, a_SizeX);
} // for z
@ -65,7 +166,7 @@ void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int
void Debug2DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, const AString & a_FileNameBase)
void Debug2DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, const AString & a_FileNameBase, NOISE_DATATYPE a_Coeff)
{
const int BUF_SIZE = 512;
ASSERT(a_SizeX <= BUF_SIZE); // Just stretch it, if needed
@ -79,7 +180,7 @@ void Debug2DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, cons
unsigned char buf[BUF_SIZE];
for (int x = 0; x < a_SizeX; x++)
{
buf[x] = (unsigned char)(std::min(255, std::max(0, (int)(128 + 32 * a_Noise[idx++]))));
buf[x] = static_cast<unsigned char>(Clamp((int)(128 + a_Coeff * a_Noise[idx++]), 0, 255));
}
f1.Write(buf, a_SizeX);
} // for y
@ -594,13 +695,6 @@ NOISE_DATATYPE cNoise::CubicNoise3D(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y, NOIS
////////////////////////////////////////////////////////////////////////////////
// cCubicNoise:
#ifdef _DEBUG
int cCubicNoise::m_NumSingleX = 0;
int cCubicNoise::m_NumSingleXY = 0;
int cCubicNoise::m_NumSingleY = 0;
int cCubicNoise::m_NumCalls = 0;
#endif // _DEBUG
cCubicNoise::cCubicNoise(int a_Seed) :
m_Noise(a_Seed)
{
@ -639,23 +733,6 @@ void cCubicNoise::Generate2D(
Cell.InitWorkRnds(FloorX[0], FloorY[0]);
#ifdef _DEBUG
// Statistics on the noise-space coords:
if (NumSameX == 1)
{
m_NumSingleX++;
if (NumSameY == 1)
{
m_NumSingleXY++;
}
}
if (NumSameY == 1)
{
m_NumSingleY++;
}
m_NumCalls++;
#endif // _DEBUG
// Calculate query values using Cell:
int FromY = 0;
for (int y = 0; y < NumSameY; y++)
@ -792,341 +869,161 @@ void cCubicNoise::CalcFloorFrac(
////////////////////////////////////////////////////////////////////////////////
// cPerlinNoise:
// cImprovedNoise:
cPerlinNoise::cPerlinNoise(void) :
m_Seed(0)
cImprovedNoise::cImprovedNoise(int a_Seed)
{
// Initialize the permutations with identity:
for (int i = 0; i < 256; i++)
{
m_Perm[i] = i;
}
// Randomize the permutation table - swap each element with a random other element:
cNoise noise(a_Seed);
for (int i = 0; i < 256; i++)
{
int rnd = (noise.IntNoise1DInt(i) / 7) % 256;
std::swap(m_Perm[i], m_Perm[rnd]);
}
// Copy the lower 256 entries into upper 256 entries:
for (int i = 0; i < 256; i++)
{
m_Perm[i + 256] = m_Perm[i];
}
}
cPerlinNoise::cPerlinNoise(int a_Seed) :
m_Seed(a_Seed)
{
}
void cPerlinNoise::SetSeed(int a_Seed)
{
m_Seed = a_Seed;
}
void cPerlinNoise::AddOctave(float a_Frequency, float a_Amplitude)
{
m_Octaves.push_back(cOctave(m_Seed * ((int)m_Octaves.size() + 4) * 4 + 1024, a_Frequency, a_Amplitude));
}
void cPerlinNoise::Generate2D(
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
NOISE_DATATYPE * a_Workspace ///< Workspace that this function can use and trash
void cImprovedNoise::Generate2D(
NOISE_DATATYPE * a_Array,
int a_SizeX, int a_SizeY,
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX,
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY
) const
{
if (m_Octaves.empty())
size_t idx = 0;
for (int y = 0; y < a_SizeY; y++)
{
// No work to be done
ASSERT(!"Perlin: No octaves to generate!");
return;
}
bool ShouldFreeWorkspace = (a_Workspace == nullptr);
int ArrayCount = a_SizeX * a_SizeY;
if (ShouldFreeWorkspace)
{
a_Workspace = new NOISE_DATATYPE[ArrayCount];
}
// Generate the first octave directly into array:
const cOctave & FirstOctave = m_Octaves.front();
FirstOctave.m_Noise.Generate2D(
a_Workspace, a_SizeX, a_SizeY,
a_StartX * FirstOctave.m_Frequency, a_EndX * FirstOctave.m_Frequency,
a_StartY * FirstOctave.m_Frequency, a_EndY * FirstOctave.m_Frequency
);
NOISE_DATATYPE Amplitude = FirstOctave.m_Amplitude;
for (int i = 0; i < ArrayCount; i++)
{
a_Array[i] = a_Workspace[i] * Amplitude;
}
// Add each octave:
for (cOctaves::const_iterator itr = m_Octaves.begin() + 1, end = m_Octaves.end(); itr != end; ++itr)
{
// Generate cubic noise for the octave:
itr->m_Noise.Generate2D(
a_Workspace, a_SizeX, a_SizeY,
a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency,
a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency
);
// Add the cubic noise into the output:
NOISE_DATATYPE Amplitude = itr->m_Amplitude;
for (int i = 0; i < ArrayCount; i++)
NOISE_DATATYPE ratioY = static_cast<NOISE_DATATYPE>(y) / (a_SizeY - 1);
NOISE_DATATYPE noiseY = Lerp(a_StartY, a_EndY, ratioY);
int noiseYInt = FAST_FLOOR(noiseY);
int yCoord = noiseYInt & 255;
NOISE_DATATYPE noiseYFrac = noiseY - noiseYInt;
NOISE_DATATYPE fadeY = Fade(noiseYFrac);
for (int x = 0; x < a_SizeX; x++)
{
a_Array[i] += a_Workspace[i] * Amplitude;
}
}
if (ShouldFreeWorkspace)
{
delete[] a_Workspace;
a_Workspace = nullptr;
}
NOISE_DATATYPE ratioX = static_cast<NOISE_DATATYPE>(x) / (a_SizeX - 1);
NOISE_DATATYPE noiseX = Lerp(a_StartX, a_EndX, ratioX);
int noiseXInt = FAST_FLOOR(noiseX);
int xCoord = noiseXInt & 255;
NOISE_DATATYPE noiseXFrac = noiseX - noiseXInt;
NOISE_DATATYPE fadeX = Fade(noiseXFrac);
// Hash the coordinates:
int A = m_Perm[xCoord] + yCoord;
int AA = m_Perm[A];
int AB = m_Perm[A + 1];
int B = m_Perm[xCoord + 1] + yCoord;
int BA = m_Perm[B];
int BB = m_Perm[B + 1];
// Lerp the gradients:
a_Array[idx++] = Lerp(
Lerp(Grad(m_Perm[AA], noiseXFrac, noiseYFrac, 0), Grad(m_Perm[BA], noiseXFrac - 1, noiseYFrac, 0), fadeX),
Lerp(Grad(m_Perm[AB], noiseXFrac, noiseYFrac - 1, 0), Grad(m_Perm[BB], noiseXFrac - 1, noiseYFrac - 1, 0), fadeX),
fadeY
);
} // for x
} // for y
}
void cPerlinNoise::Generate3D(
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction
NOISE_DATATYPE * a_Workspace ///< Workspace that this function can use and trash
void cImprovedNoise::Generate3D(
NOISE_DATATYPE * a_Array,
int a_SizeX, int a_SizeY, int a_SizeZ,
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX,
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY,
NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ
) const
{
if (m_Octaves.empty())
size_t idx = 0;
for (int z = 0; z < a_SizeZ; z++)
{
// No work to be done
ASSERT(!"Perlin: No octaves to generate!");
return;
}
bool ShouldFreeWorkspace = (a_Workspace == nullptr);
int ArrayCount = a_SizeX * a_SizeY * a_SizeZ;
if (ShouldFreeWorkspace)
{
a_Workspace = new NOISE_DATATYPE[ArrayCount];
}
// Generate the first octave directly into array:
const cOctave & FirstOctave = m_Octaves.front();
FirstOctave.m_Noise.Generate3D(
a_Workspace, a_SizeX, a_SizeY, a_SizeZ,
a_StartX * FirstOctave.m_Frequency, a_EndX * FirstOctave.m_Frequency,
a_StartY * FirstOctave.m_Frequency, a_EndY * FirstOctave.m_Frequency,
a_StartZ * FirstOctave.m_Frequency, a_EndZ * FirstOctave.m_Frequency
);
NOISE_DATATYPE Amplitude = FirstOctave.m_Amplitude;
for (int i = 0; i < ArrayCount; i++)
{
a_Array[i] = a_Workspace[i] * Amplitude;
}
// Add each octave:
for (cOctaves::const_iterator itr = m_Octaves.begin() + 1, end = m_Octaves.end(); itr != end; ++itr)
{
// Generate cubic noise for the octave:
itr->m_Noise.Generate3D(
a_Workspace, a_SizeX, a_SizeY, a_SizeZ,
a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency,
a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency,
a_StartZ * itr->m_Frequency, a_EndZ * itr->m_Frequency
);
// Add the cubic noise into the output:
NOISE_DATATYPE Amplitude = itr->m_Amplitude;
for (int i = 0; i < ArrayCount; i++)
NOISE_DATATYPE ratioZ = static_cast<NOISE_DATATYPE>(z) / (a_SizeZ - 1);
NOISE_DATATYPE noiseZ = Lerp(a_StartZ, a_EndZ, ratioZ);
int noiseZInt = FAST_FLOOR(noiseZ);
int zCoord = noiseZInt & 255;
NOISE_DATATYPE noiseZFrac = noiseZ - noiseZInt;
NOISE_DATATYPE fadeZ = Fade(noiseZFrac);
for (int y = 0; y < a_SizeY; y++)
{
a_Array[i] += a_Workspace[i] * Amplitude;
}
}
if (ShouldFreeWorkspace)
{
delete[] a_Workspace;
a_Workspace = nullptr;
}
NOISE_DATATYPE ratioY = static_cast<NOISE_DATATYPE>(y) / (a_SizeY - 1);
NOISE_DATATYPE noiseY = Lerp(a_StartY, a_EndY, ratioY);
int noiseYInt = FAST_FLOOR(noiseY);
int yCoord = noiseYInt & 255;
NOISE_DATATYPE noiseYFrac = noiseY - noiseYInt;
NOISE_DATATYPE fadeY = Fade(noiseYFrac);
for (int x = 0; x < a_SizeX; x++)
{
NOISE_DATATYPE ratioX = static_cast<NOISE_DATATYPE>(x) / (a_SizeX - 1);
NOISE_DATATYPE noiseX = Lerp(a_StartX, a_EndX, ratioX);
int noiseXInt = FAST_FLOOR(noiseX);
int xCoord = noiseXInt & 255;
NOISE_DATATYPE noiseXFrac = noiseX - noiseXInt;
NOISE_DATATYPE fadeX = Fade(noiseXFrac);
// Hash the coordinates:
int A = m_Perm[xCoord] + yCoord;
int AA = m_Perm[A] + zCoord;
int AB = m_Perm[A + 1] + zCoord;
int B = m_Perm[xCoord + 1] + yCoord;
int BA = m_Perm[B] + zCoord;
int BB = m_Perm[B + 1] + zCoord;
// Lerp the gradients:
// TODO: This may be optimized by swapping the coords and recalculating most lerps only "once every x"
a_Array[idx++] = Lerp(
Lerp(
Lerp(Grad(m_Perm[AA], noiseXFrac, noiseYFrac, noiseZFrac), Grad(m_Perm[BA], noiseXFrac - 1, noiseYFrac, noiseZFrac), fadeX),
Lerp(Grad(m_Perm[AB], noiseXFrac, noiseYFrac - 1, noiseZFrac), Grad(m_Perm[BB], noiseXFrac - 1, noiseYFrac - 1, noiseZFrac), fadeX),
fadeY
),
Lerp(
Lerp(Grad(m_Perm[AA + 1], noiseXFrac, noiseYFrac, noiseZFrac - 1), Grad(m_Perm[BA + 1], noiseXFrac - 1, noiseYFrac, noiseZFrac - 1), fadeX),
Lerp(Grad(m_Perm[AB + 1], noiseXFrac, noiseYFrac - 1, noiseZFrac - 1), Grad(m_Perm[BB + 1], noiseXFrac - 1, noiseYFrac - 1, noiseZFrac - 1), fadeX),
fadeY
),
fadeZ
);
} // for x
} // for y
} // for z
}
////////////////////////////////////////////////////////////////////////////////
// cRidgedMultiNoise:
cRidgedMultiNoise::cRidgedMultiNoise(void) :
m_Seed(0)
NOISE_DATATYPE cImprovedNoise::GetValueAt(int a_X, int a_Y, int a_Z)
{
// Hash the coordinates:
a_X = a_X & 255;
a_Y = a_Y & 255;
a_Z = a_Z & 255;
int A = m_Perm[a_X] + a_Y;
int AA = m_Perm[A] + a_Z;
return Grad(m_Perm[AA], 1, 1, 1);
}
cRidgedMultiNoise::cRidgedMultiNoise(int a_Seed) :
m_Seed(a_Seed)
{
}
void cRidgedMultiNoise::SetSeed(int a_Seed)
{
m_Seed = a_Seed;
}
void cRidgedMultiNoise::AddOctave(float a_Frequency, float a_Amplitude)
{
m_Octaves.push_back(cOctave(m_Seed * ((int)m_Octaves.size() + 4) * 4 + 1024, a_Frequency, a_Amplitude));
}
void cRidgedMultiNoise::Generate2D(
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
NOISE_DATATYPE * a_Workspace ///< Workspace that this function can use and trash
) const
{
if (m_Octaves.empty())
{
// No work to be done
ASSERT(!"RidgedMulti: No octaves to generate!");
return;
}
bool ShouldFreeWorkspace = (a_Workspace == nullptr);
int ArrayCount = a_SizeX * a_SizeY;
if (ShouldFreeWorkspace)
{
a_Workspace = new NOISE_DATATYPE[ArrayCount];
}
// Generate the first octave directly into array:
const cOctave & FirstOctave = m_Octaves.front();
FirstOctave.m_Noise.Generate2D(
a_Workspace, a_SizeX, a_SizeY,
a_StartX * FirstOctave.m_Frequency, a_EndX * FirstOctave.m_Frequency,
a_StartY * FirstOctave.m_Frequency, a_EndY * FirstOctave.m_Frequency
);
NOISE_DATATYPE Amplitude = FirstOctave.m_Amplitude;
for (int i = 0; i < ArrayCount; i++)
{
a_Array[i] = fabs(a_Workspace[i] * Amplitude);
}
// Add each octave:
for (cOctaves::const_iterator itr = m_Octaves.begin() + 1, end = m_Octaves.end(); itr != end; ++itr)
{
// Generate cubic noise for the octave:
itr->m_Noise.Generate2D(
a_Workspace, a_SizeX, a_SizeY,
a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency,
a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency
);
// Add the cubic noise into the output:
NOISE_DATATYPE Amplitude = itr->m_Amplitude;
for (int i = 0; i < ArrayCount; i++)
{
a_Array[i] += fabs(a_Workspace[i] * Amplitude);
}
}
if (ShouldFreeWorkspace)
{
delete[] a_Workspace;
a_Workspace = nullptr;
}
}
void cRidgedMultiNoise::Generate3D(
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction
NOISE_DATATYPE * a_Workspace ///< Workspace that this function can use and trash
) const
{
if (m_Octaves.empty())
{
// No work to be done
ASSERT(!"RidgedMulti: No octaves to generate!");
return;
}
bool ShouldFreeWorkspace = (a_Workspace == nullptr);
int ArrayCount = a_SizeX * a_SizeY * a_SizeZ;
if (ShouldFreeWorkspace)
{
a_Workspace = new NOISE_DATATYPE[ArrayCount];
}
// Generate the first octave directly into array:
const cOctave & FirstOctave = m_Octaves.front();
FirstOctave.m_Noise.Generate3D(
a_Workspace, a_SizeX, a_SizeY, a_SizeZ,
a_StartX * FirstOctave.m_Frequency, a_EndX * FirstOctave.m_Frequency,
a_StartY * FirstOctave.m_Frequency, a_EndY * FirstOctave.m_Frequency,
a_StartZ * FirstOctave.m_Frequency, a_EndZ * FirstOctave.m_Frequency
);
NOISE_DATATYPE Amplitude = FirstOctave.m_Amplitude;
for (int i = 0; i < ArrayCount; i++)
{
a_Array[i] = a_Workspace[i] * Amplitude;
}
// Add each octave:
for (cOctaves::const_iterator itr = m_Octaves.begin() + 1, end = m_Octaves.end(); itr != end; ++itr)
{
// Generate cubic noise for the octave:
itr->m_Noise.Generate3D(
a_Workspace, a_SizeX, a_SizeY, a_SizeZ,
a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency,
a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency,
a_StartZ * itr->m_Frequency, a_EndZ * itr->m_Frequency
);
// Add the cubic noise into the output:
NOISE_DATATYPE Amplitude = itr->m_Amplitude;
for (int i = 0; i < ArrayCount; i++)
{
a_Array[i] += a_Workspace[i] * Amplitude;
}
}
if (ShouldFreeWorkspace)
{
delete[] a_Workspace;
a_Workspace = nullptr;
}
}

View File

@ -7,22 +7,11 @@
#include <cmath>
/** The datatype used by all the noise generators. */
typedef float NOISE_DATATYPE;
// Some settings
#define NOISE_DATATYPE float
#ifdef _MSC_VER
#define INLINE __forceinline
#else
#define INLINE inline
#endif
#include "OctavedNoise.h"
#include "RidgedNoise.h"
@ -35,20 +24,20 @@ public:
cNoise(const cNoise & a_Noise);
// The following functions, if not marked INLINE, are about 20 % slower
INLINE NOISE_DATATYPE IntNoise1D(int a_X) const;
INLINE NOISE_DATATYPE IntNoise2D(int a_X, int a_Y) const;
INLINE NOISE_DATATYPE IntNoise3D(int a_X, int a_Y, int a_Z) const;
inline NOISE_DATATYPE IntNoise1D(int a_X) const;
inline NOISE_DATATYPE IntNoise2D(int a_X, int a_Y) const;
inline NOISE_DATATYPE IntNoise3D(int a_X, int a_Y, int a_Z) const;
// Return a float number in the specified range:
INLINE NOISE_DATATYPE IntNoise2DInRange(int a_X, int a_Y, float a_Min, float a_Max) const
inline NOISE_DATATYPE IntNoise2DInRange(int a_X, int a_Y, float a_Min, float a_Max) const
{
return a_Min + std::abs(IntNoise2D(a_X, a_Y)) * (a_Max - a_Min);
}
// Note: These functions have a mod8-irregular chance - each of the mod8 remainders has different chance of occurrence. Divide by 8 to rectify.
INLINE int IntNoise1DInt(int a_X) const;
INLINE int IntNoise2DInt(int a_X, int a_Y) const;
INLINE int IntNoise3DInt(int a_X, int a_Y, int a_Z) const;
inline int IntNoise1DInt(int a_X) const;
inline int IntNoise2DInt(int a_X, int a_Y) const;
inline int IntNoise3DInt(int a_X, int a_Y, int a_Z) const;
NOISE_DATATYPE LinearNoise1D(NOISE_DATATYPE a_X) const;
NOISE_DATATYPE CosineNoise1D(NOISE_DATATYPE a_X) const;
@ -61,9 +50,9 @@ public:
void SetSeed(int a_Seed) { m_Seed = a_Seed; }
INLINE static NOISE_DATATYPE CubicInterpolate (NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_C, NOISE_DATATYPE a_D, NOISE_DATATYPE a_Pct);
INLINE static NOISE_DATATYPE CosineInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct);
INLINE static NOISE_DATATYPE LinearInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct);
inline static NOISE_DATATYPE CubicInterpolate (NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_C, NOISE_DATATYPE a_D, NOISE_DATATYPE a_Pct);
inline static NOISE_DATATYPE CosineInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct);
inline static NOISE_DATATYPE LinearInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct);
private:
int m_Seed;
@ -76,19 +65,15 @@ private:
class cCubicNoise
{
public:
static const int MAX_SIZE = 512; ///< Maximum size of each dimension of the query arrays.
/** Maximum size of each dimension of the query arrays. */
static const int MAX_SIZE = 512;
/** Creates a new instance with the specified seed. */
cCubicNoise(int a_Seed);
void Generate1D(
NOISE_DATATYPE * a_Array, ///< Array to generate into
int a_SizeX, ///< Count of the array
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX ///< Noise-space coords of the array
) const;
/** Fills a 2D array with the values of the noise. */
void Generate2D(
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
@ -97,6 +82,7 @@ public:
) const;
/** Fills a 3D array with the values of the noise. */
void Generate3D(
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
@ -106,163 +92,87 @@ public:
) const;
protected:
typedef NOISE_DATATYPE Workspace1D[4];
typedef NOISE_DATATYPE Workspace2D[4][4];
cNoise m_Noise; // Used for integral rnd values
/** Noise used for integral random values. */
cNoise m_Noise;
#ifdef _DEBUG
// Statistics on the noise-space coords:
static int m_NumSingleX;
static int m_NumSingleXY;
static int m_NumSingleY;
static int m_NumCalls;
#endif // _DEBUG
/// Calculates the integral and fractional parts along one axis.
/** Calculates the integral and fractional parts along one axis.
a_Floor will receive the integral parts (array of a_Size ints).
a_Frac will receive the fractional parts (array of a_Size floats).
a_Same will receive the counts of items that have the same integral parts (array of up to a_Size ints).
a_NumSame will receive the count of a_Same elements (total count of different integral parts). */
void CalcFloorFrac(
int a_Size,
NOISE_DATATYPE a_Start, NOISE_DATATYPE a_End,
int * a_Floor, NOISE_DATATYPE * a_Frac,
int * a_Same, int & a_NumSame
) const;
void UpdateWorkRnds2DX(
Workspace2D & a_WorkRnds,
Workspace1D & a_Interps,
int a_LastFloorX, int a_NewFloorX,
int a_FloorY,
NOISE_DATATYPE a_FractionY
) const;
} ;
class cPerlinNoise
/** Improved noise, as described by Ken Perlin: http://mrl.nyu.edu/~perlin/paper445.pdf
Implementation adapted from Perlin's Java implementation: http://mrl.nyu.edu/~perlin/noise/ */
class cImprovedNoise
{
public:
cPerlinNoise(void);
cPerlinNoise(int a_Seed);
void SetSeed(int a_Seed);
void AddOctave(NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude);
void Generate1D(
NOISE_DATATYPE * a_Array, ///< Array to generate into
int a_SizeX, ///< Count of the array
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array
NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash
) const;
/** Constructs a new instance of the noise obbject.
Note that this operation is quite expensive (the permutation array being constructed). */
cImprovedNoise(int a_Seed);
/** Fills a 2D array with the values of the noise. */
void Generate2D(
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY ///< Noise-space coords of the array in the Y direction
) const;
/** Fills a 3D array with the values of the noise. */
void Generate3D(
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction
NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash
NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ ///< Noise-space coords of the array in the Z direction
) const;
/** Returns the value at the specified integral coords. Used for raw speed measurement. */
NOISE_DATATYPE GetValueAt(int a_X, int a_Y, int a_Z);
protected:
class cOctave
/** The permutation table used by the noise function. Initialized using seed. */
int m_Perm[512];
/** Calculates the fade curve, 6 * t^5 - 15 * t^4 + 10 * t^3. */
inline static NOISE_DATATYPE Fade(NOISE_DATATYPE a_T)
{
public:
cCubicNoise m_Noise;
NOISE_DATATYPE m_Frequency; // Coord multiplier
NOISE_DATATYPE m_Amplitude; // Value multiplier
cOctave(int a_Seed, NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude) :
m_Noise(a_Seed),
m_Frequency(a_Frequency),
m_Amplitude(a_Amplitude)
{
}
} ;
typedef std::vector<cOctave> cOctaves;
int m_Seed;
cOctaves m_Octaves;
} ;
return a_T * a_T * a_T * (a_T * (a_T * 6 - 15) + 10);
}
class cRidgedMultiNoise
{
public:
cRidgedMultiNoise(void);
cRidgedMultiNoise(int a_Seed);
void SetSeed(int a_Seed);
void AddOctave(NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude);
void Generate1D(
NOISE_DATATYPE * a_Array, ///< Array to generate into
int a_SizeX, ///< Count of the array
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array
NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash
) const;
void Generate2D(
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash
) const;
void Generate3D(
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction
NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash
) const;
protected:
class cOctave
/** Returns the gradient value based on the hash. */
inline static NOISE_DATATYPE Grad(int a_Hash, NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y, NOISE_DATATYPE a_Z)
{
public:
cCubicNoise m_Noise;
NOISE_DATATYPE m_Frequency; // Coord multiplier
NOISE_DATATYPE m_Amplitude; // Value multiplier
cOctave(int a_Seed, NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude) :
m_Noise(a_Seed),
m_Frequency(a_Frequency),
m_Amplitude(a_Amplitude)
{
}
} ;
typedef std::vector<cOctave> cOctaves;
int m_Seed;
cOctaves m_Octaves;
} ;
int hash = a_Hash % 16;
NOISE_DATATYPE u = (hash < 8) ? a_X : a_Y;
NOISE_DATATYPE v = (hash < 4) ? a_Y : (((hash == 12) || (hash == 14)) ? a_X : a_Z);
return (((hash & 1) == 0) ? u : -u) + (((hash & 2) == 0) ? v : -v);
}
};
typedef cOctavedNoise<cCubicNoise> cPerlinNoise;
typedef cOctavedNoise<cRidgedNoise<cCubicNoise>> cRidgedMultiNoise;
@ -376,8 +286,46 @@ NOISE_DATATYPE cNoise::LinearInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B,
////////////////////////////////////////////////////////////////////////////////
// Global functions:
extern void Debug2DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, const AString & a_FileNameBase);
extern void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase);
/** Exports the noise array into a file.
a_Coeff specifies the value that each array value is multiplied by before being converted into a byte. */
extern void Debug2DNoise(const NOISE_DATATYPE * a_Array, int a_SizeX, int a_SizeY, const AString & a_FileNameBase, NOISE_DATATYPE a_Coeff = 32);
/** Exports the noise array into a set of files, ordered by XY and XZ.
a_Coeff specifies the value that each array value is multiplied by before being converted into a byte. */
extern void Debug3DNoise(const NOISE_DATATYPE * a_Array, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase, NOISE_DATATYPE a_Coeff = 32);
/** Linearly interpolates between two values.
Assumes that a_Ratio is in range [0, 1]. */
inline NOISE_DATATYPE Lerp(NOISE_DATATYPE a_Val1, NOISE_DATATYPE a_Val2, NOISE_DATATYPE a_Ratio)
{
return a_Val1 + (a_Val2 - a_Val1) * a_Ratio;
}
/** Linearly interpolates between two values, clamping the ratio to [0, 1] first. */
inline NOISE_DATATYPE ClampedLerp(NOISE_DATATYPE a_Val1, NOISE_DATATYPE a_Val2, NOISE_DATATYPE a_Ratio)
{
if (a_Ratio < 0)
{
return a_Val1;
}
if (a_Ratio > 1)
{
return a_Val2;
}
return Lerp(a_Val1, a_Val2, a_Ratio);
}

192
src/Noise/OctavedNoise.h Normal file
View File

@ -0,0 +1,192 @@
// OctavedNoise.h
// Implements the cOctavedNoise class template representing a noise generator that layers several octaves of another noise
#pragma once
template <typename N>
class cOctavedNoise
{
public:
cOctavedNoise(int a_Seed = 0):
m_Seed(a_Seed)
{
}
/** Sets a new seed for the generators. Relays the seed to all underlying octaves. */
void SetSeed(int a_Seed)
{
m_Seed = a_Seed;
for (auto oct: m_Octaves)
{
oct->SetSeed(a_Seed);
}
}
/** Adds a new octave to the list of octaves that compose this noise. */
void AddOctave(NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude)
{
m_Octaves.emplace_back(m_Seed, a_Frequency, a_Amplitude);
}
/** Fills a 2D array with the values of the noise. */
void Generate2D(
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash.
) const
{
// Check that state is alright:
if (m_Octaves.empty())
{
ASSERT(!"cOctavedNoise: No octaves to generate!");
return;
}
// Allocate the workspace on the heap, if it wasn't given:
std::unique_ptr<NOISE_DATATYPE[]> workspaceHeap;
if (a_Workspace == nullptr)
{
workspaceHeap.reset(new NOISE_DATATYPE[a_SizeX * a_SizeY]);
a_Workspace = workspaceHeap.get();
}
// Generate the first octave directly into array:
const cOctave & FirstOctave = m_Octaves.front();
int ArrayCount = a_SizeX * a_SizeY;
FirstOctave.m_Noise.Generate2D(
a_Workspace, a_SizeX, a_SizeY,
a_StartX * FirstOctave.m_Frequency, a_EndX * FirstOctave.m_Frequency,
a_StartY * FirstOctave.m_Frequency, a_EndY * FirstOctave.m_Frequency
);
NOISE_DATATYPE Amplitude = FirstOctave.m_Amplitude;
for (int i = 0; i < ArrayCount; i++)
{
a_Array[i] = a_Workspace[i] * Amplitude;
}
// Add each octave:
for (auto itr = m_Octaves.cbegin() + 1, end = m_Octaves.cend(); itr != end; ++itr)
{
// Generate the noise for the octave:
itr->m_Noise.Generate2D(
a_Workspace, a_SizeX, a_SizeY,
a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency,
a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency
);
// Add it into the output:
NOISE_DATATYPE Amplitude = itr->m_Amplitude;
for (int i = 0; i < ArrayCount; i++)
{
a_Array[i] += a_Workspace[i] * Amplitude;
}
} // for itr - m_Octaves[]
}
/** Fills a 3D array with the values of the noise. */
void Generate3D(
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction
NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash, same size as a_Array
) const
{
// Check that state is alright:
if (m_Octaves.empty())
{
ASSERT(!"cOctavedNoise: No octaves to generate!");
return;
}
// Allocate the workspace on the heap, if it wasn't given:
std::unique_ptr<NOISE_DATATYPE[]> workspaceHeap;
if (a_Workspace == nullptr)
{
workspaceHeap.reset(new NOISE_DATATYPE[a_SizeX * a_SizeY * a_SizeZ]);
a_Workspace = workspaceHeap.get();
}
// Generate the first octave directly into array:
const cOctave & FirstOctave = m_Octaves.front();
int ArrayCount = a_SizeX * a_SizeY * a_SizeZ;
FirstOctave.m_Noise.Generate3D(
a_Workspace, a_SizeX, a_SizeY, a_SizeZ,
a_StartX * FirstOctave.m_Frequency, a_EndX * FirstOctave.m_Frequency,
a_StartY * FirstOctave.m_Frequency, a_EndY * FirstOctave.m_Frequency,
a_StartZ * FirstOctave.m_Frequency, a_EndZ * FirstOctave.m_Frequency
);
NOISE_DATATYPE Amplitude = FirstOctave.m_Amplitude;
for (int i = 0; i < ArrayCount; i++)
{
a_Array[i] = a_Workspace[i] * Amplitude;
}
// Add each octave:
for (auto itr = m_Octaves.cbegin() + 1, end = m_Octaves.cend(); itr != end; ++itr)
{
// Generate the noise for the octave:
itr->m_Noise.Generate3D(
a_Workspace, a_SizeX, a_SizeY, a_SizeZ,
a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency,
a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency,
a_StartZ * itr->m_Frequency, a_EndZ * itr->m_Frequency
);
// Add it into the output:
NOISE_DATATYPE Amplitude = itr->m_Amplitude;
for (int i = 0; i < ArrayCount; i++)
{
a_Array[i] += a_Workspace[i] * Amplitude;
}
} // for itr - m_Octaves[]
}
protected:
/** Stores information and state for one octave of the noise. */
class cOctave
{
public:
N m_Noise;
/** Coord multiplier. */
NOISE_DATATYPE m_Frequency;
/** Value multiplier. */
NOISE_DATATYPE m_Amplitude;
cOctave(int a_Seed, NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude) :
m_Noise(a_Seed),
m_Frequency(a_Frequency),
m_Amplitude(a_Amplitude)
{
}
} ;
typedef std::vector<cOctave> cOctaves;
/** The seed used by the underlying generators. */
int m_Seed;
/** The octaves that compose this noise. */
cOctaves m_Octaves;
};

91
src/Noise/RidgedNoise.h Normal file
View File

@ -0,0 +1,91 @@
// RidgedNoise.h
// Implements the cRidgedNoise template class that generates ridged noise based on another noise provider.
#pragma once
template <typename N>
class cRidgedNoise
{
public:
/** Creates a new instance with the seed set to 0. */
cRidgedNoise(void):
m_Noise(0)
{
}
/** Creates a new instance with the specified seed. */
cRidgedNoise(int a_Seed):
m_Noise(a_Seed)
{
}
/** Sets the seed for the underlying noise. */
void SetSeed(int a_Seed)
{
m_Noise.SetSeed(a_Seed);
}
/** Fills a 2D array with the values of the noise. */
void Generate2D(
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY ///< Noise-space coords of the array in the Y direction
) const
{
int ArrayCount = a_SizeX * a_SizeY;
m_Noise.Generate2D(
a_Array, a_SizeX, a_SizeY,
a_StartX, a_EndX,
a_StartY, a_EndY
);
for (int i = 0; i < ArrayCount; i++)
{
a_Array[i] = fabs(a_Array[i]);
}
}
/** Fills a 3D array with the values of the noise. */
void Generate3D(
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ ///< Noise-space coords of the array in the Z direction
) const
{
int ArrayCount = a_SizeX * a_SizeY * a_SizeZ;
m_Noise.Generate2D(
a_Array, a_SizeX, a_SizeY, a_SizeZ,
a_StartX, a_EndX,
a_StartY, a_EndY,
a_StartZ, a_EndZ
);
for (int i = 0; i < ArrayCount; i++)
{
a_Array[i] = fabs(a_Array[i]);
}
}
protected:
N m_Noise;
} ;

View File

@ -9,7 +9,7 @@
#pragma once
#include "Noise.h"
#include "Noise/Noise.h"