1
0

Merge branch 'master' into MobSpawner

This commit is contained in:
Howaner 2014-11-29 12:05:35 +01:00
commit 648fee1a08
83 changed files with 3248 additions and 2297 deletions

View File

@ -51,6 +51,7 @@ local function MultiCommandHandler(a_Split, a_Player, a_CmdString, a_CmdInfo, a_
return a_CmdInfo.Handler(a_Split, a_Player);
end
-- Let the player know they need to give a subcommand:
assert(type(a_CmdInfo.Subcommands) == "table", "Info.lua error: There is no handler for command \"" .. a_CmdString .. "\" and there are no subcommands defined at level " .. a_Level)
ListSubcommands(a_Player, a_CmdInfo.Subcommands, a_CmdString);
return true;
end

View File

@ -247,15 +247,16 @@ void cBeaconEntity::GiveEffects(void)
}
public:
cPlayerCallback(int a_Radius, int a_PosX, int a_PosY, int a_PosZ, cEntityEffect::eType a_PrimaryEffect, cEntityEffect::eType a_SecondaryEffect, short a_EffectLevel)
: m_Radius(a_Radius)
, m_PosX(a_PosX)
, m_PosY(a_PosY)
, m_PosZ(a_PosZ)
, m_PrimaryEffect(a_PrimaryEffect)
, m_SecondaryEffect(a_SecondaryEffect)
, m_EffectLevel(a_EffectLevel)
{};
cPlayerCallback(int a_Radius, int a_PosX, int a_PosY, int a_PosZ, cEntityEffect::eType a_PrimaryEffect, cEntityEffect::eType a_SecondaryEffect, short a_EffectLevel):
m_Radius(a_Radius),
m_PosX(a_PosX),
m_PosY(a_PosY),
m_PosZ(a_PosZ),
m_PrimaryEffect(a_PrimaryEffect),
m_SecondaryEffect(a_SecondaryEffect),
m_EffectLevel(a_EffectLevel)
{
}
} PlayerCallback(Radius, m_PosX, m_PosY, m_PosZ, m_PrimaryEffect, SecondaryEffect, EffectLevel);
GetWorld()->ForEachPlayer(PlayerCallback);

View File

@ -24,7 +24,7 @@ class cBeaconEntity :
public:
// tolua_end
BLOCKENTITY_PROTODEF(cBeaconEntity);
BLOCKENTITY_PROTODEF(cBeaconEntity)
cBeaconEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);

View File

@ -29,7 +29,7 @@ class cBlockEntityWithItems :
public:
// tolua_end
BLOCKENTITY_PROTODEF(cBlockEntityWithItems);
BLOCKENTITY_PROTODEF(cBlockEntityWithItems)
cBlockEntityWithItems(
BLOCKTYPE a_BlockType, // Type of the block that the entity represents

View File

@ -28,7 +28,7 @@ public:
// tolua_end
BLOCKENTITY_PROTODEF(cChestEntity);
BLOCKENTITY_PROTODEF(cChestEntity)
/** Constructor used for normal operation */
cChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World, BLOCKTYPE a_Type);

View File

@ -28,7 +28,7 @@ public:
// tolua_end
BLOCKENTITY_PROTODEF(cCommandBlockEntity);
BLOCKENTITY_PROTODEF(cCommandBlockEntity)
/// Creates a new empty command block entity
cCommandBlockEntity(int a_X, int a_Y, int a_Z, cWorld * a_World);

View File

@ -17,7 +17,7 @@ public:
// tolua_end
BLOCKENTITY_PROTODEF(cDispenserEntity);
BLOCKENTITY_PROTODEF(cDispenserEntity)
/** Constructor used for normal operation */
cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);

View File

@ -41,7 +41,7 @@ public:
// tolua_end
BLOCKENTITY_PROTODEF(cDropSpenserEntity);
BLOCKENTITY_PROTODEF(cDropSpenserEntity)
cDropSpenserEntity(BLOCKTYPE a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
virtual ~cDropSpenserEntity();

View File

@ -25,7 +25,7 @@ public:
// tolua_end
BLOCKENTITY_PROTODEF(cDropperEntity);
BLOCKENTITY_PROTODEF(cDropperEntity)
/// Constructor used for normal operation
cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);

View File

@ -18,7 +18,7 @@ class cEnderChestEntity :
public:
// tolua_end
BLOCKENTITY_PROTODEF(cEnderChestEntity);
BLOCKENTITY_PROTODEF(cEnderChestEntity)
cEnderChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
virtual ~cEnderChestEntity();

View File

@ -26,7 +26,7 @@ public:
// tolua_end
BLOCKENTITY_PROTODEF(cFlowerPotEntity);
BLOCKENTITY_PROTODEF(cFlowerPotEntity)
/** Creates a new flowerpot entity at the specified block coords. a_World may be nullptr */
cFlowerPotEntity(int a_BlocX, int a_BlockY, int a_BlockZ, cWorld * a_World);

View File

@ -33,7 +33,7 @@ public:
// tolua_end
BLOCKENTITY_PROTODEF(cFurnaceEntity);
BLOCKENTITY_PROTODEF(cFurnaceEntity)
/** Constructor used for normal operation */
cFurnaceEntity(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cWorld * a_World);

View File

@ -31,7 +31,7 @@ public:
// tolua_end
BLOCKENTITY_PROTODEF(cHopperEntity);
BLOCKENTITY_PROTODEF(cHopperEntity)
/// Constructor used for normal operation
cHopperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);

View File

@ -17,7 +17,7 @@ public:
// tolua_end
BLOCKENTITY_PROTODEF(cJukeboxEntity);
BLOCKENTITY_PROTODEF(cJukeboxEntity)
cJukeboxEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
virtual ~cJukeboxEntity();

View File

@ -26,7 +26,7 @@ public:
// tolua_end
BLOCKENTITY_PROTODEF(cMobHeadEntity);
BLOCKENTITY_PROTODEF(cMobHeadEntity)
/** Creates a new mob head entity at the specified block coords. a_World may be nullptr */
cMobHeadEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);

View File

@ -34,7 +34,7 @@ public:
// tolua_end
BLOCKENTITY_PROTODEF(cNoteEntity);
BLOCKENTITY_PROTODEF(cNoteEntity)
/// Creates a new note entity. a_World may be nullptr
cNoteEntity(int a_X, int a_Y, int a_Z, cWorld * a_World);

View File

@ -25,7 +25,7 @@ public:
// tolua_end
BLOCKENTITY_PROTODEF(cSignEntity);
BLOCKENTITY_PROTODEF(cSignEntity)
/// Creates a new empty sign entity at the specified block coords and block type (wall or standing). a_World may be nullptr
cSignEntity(BLOCKTYPE a_BlockType, int a_X, int a_Y, int a_Z, cWorld * a_World);

View File

@ -200,7 +200,7 @@ public:
BLOCKTYPE BlockStringToType(const AString & a_BlockTypeString)
int BlockStringToType(const AString & a_BlockTypeString)
{
int res = atoi(a_BlockTypeString.c_str());
if ((res != 0) || (a_BlockTypeString.compare("0") == 0))

View File

@ -1096,7 +1096,7 @@ class cIniFile;
// tolua_begin
/// Translates a blocktype string into blocktype. Takes either a number or an items.ini alias as input. Returns -1 on failure.
extern BLOCKTYPE BlockStringToType(const AString & a_BlockTypeString);
extern int BlockStringToType(const AString & a_BlockTypeString);
/// Translates an itemtype string into an item. Takes either a number, number^number, number:number or an items.ini alias as input. Returns true if successful.
extern bool StringToItem(const AString & a_ItemTypeString, cItem & a_Item);

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
@ -103,7 +104,6 @@ SET (HDRS
Inventory.h
Item.h
ItemGrid.h
LeakFinder.h
LightingThread.h
LineBlockTracer.h
LinearInterpolation.h
@ -113,14 +113,12 @@ SET (HDRS
Map.h
MapManager.h
Matrix4.h
MemoryLeak.h
MersenneTwister.h
MobCensus.h
MobFamilyCollecter.h
MobProximityCounter.h
MobSpawner.h
MonsterConfig.h
Noise.h
ProbabDistrib.h
RankManager.h
RCONServer.h
@ -128,7 +126,6 @@ SET (HDRS
Scoreboard.h
Server.h
SetChunkData.h
StackWalker.h
Statistics.h
StringCompression.h
StringUtils.h
@ -137,7 +134,8 @@ SET (HDRS
VoronoiMap.h
WebAdmin.h
World.h
XMLParser.h)
XMLParser.h
)
include_directories(".")
include_directories ("${CMAKE_CURRENT_SOURCE_DIR}/../lib/sqlite")
@ -175,6 +173,10 @@ if (NOT MSVC)
else ()
# MSVC-specific handling: Put all files into one project, separate by the folders:
# Add the MSVC-specific LeakFinder sources:
list (APPEND SRCS LeakFinder.cpp StackWalker.cpp)
list (APPEND HDRS LeakFinder.h StackWalker.h MemoryLeak.h)
source_group(Bindings FILES "Bindings/Bindings.cpp" "Bindings/Bindings.h")
# Add all subfolders as solution-folders:
@ -224,7 +226,7 @@ else ()
Bindings/Bindings.cpp PROPERTIES COMPILE_FLAGS "/Yc\"string.h\" /Fp\"$(IntDir)/Bindings.pch\""
)
SET_SOURCE_FILES_PROPERTIES(
"StackWalker.cpp LeakFinder.h" PROPERTIES COMPILE_FLAGS "/Yc\"Globals.h\""
"StackWalker.cpp LeakFinder.cpp" PROPERTIES COMPILE_FLAGS "/Yc\"Globals.h\""
)
list(APPEND SOURCE "Resources/MCServer.rc")
@ -314,7 +316,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

@ -26,7 +26,7 @@
#include "BlockEntities/SignEntity.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"
@ -1872,18 +1872,18 @@ bool cChunk::AddClient(cClientHandle * a_Client)
void cChunk::RemoveClient(cClientHandle * a_Client)
{
for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr)
for (cClientHandleList::iterator itrC = m_LoadedByClient.begin(); itrC != m_LoadedByClient.end(); ++itrC)
{
if (*itr != a_Client)
if (*itrC != a_Client)
{
continue;
}
m_LoadedByClient.erase(itr);
m_LoadedByClient.erase(itrC);
if (!a_Client->IsDestroyed())
{
for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr)
for (cEntityList::iterator itrE = m_Entities.begin(); itrE != m_Entities.end(); ++itrE)
{
/*
// DEBUG:
@ -1892,7 +1892,7 @@ void cChunk::RemoveClient(cClientHandle * a_Client)
(*itr)->GetUniqueID(), a_Client->GetUsername().c_str()
);
*/
a_Client->SendDestroyEntity(*(*itr));
a_Client->SendDestroyEntity(*(*itrE));
}
}
return;

View File

@ -1238,12 +1238,18 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
{
// TODO: Rewrite this function
LOGD("HandleRightClick: {%d, %d, %d}, face %d, HeldItem: %s",
a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, ItemToFullString(a_HeldItem).c_str()
// Distance from the block's center to the player's eye height
double dist = (Vector3d(a_BlockX, a_BlockY, a_BlockZ) + Vector3d(0.5, 0.5, 0.5) - m_Player->GetEyePosition()).Length();
LOGD("HandleRightClick: {%d, %d, %d}, face %d, HeldItem: %s; dist: %.02f",
a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, ItemToFullString(a_HeldItem).c_str(), dist
);
// Check the reach distance:
// _X 2014-11-25: I've maxed at 5.26 with a Survival client and 5.78 with a Creative client in my tests
double maxDist = m_Player->IsGameModeCreative() ? 5.78 : 5.26;
bool AreRealCoords = (dist <= maxDist);
cWorld * World = m_Player->GetWorld();
bool AreRealCoords = (Vector3d(a_BlockX, a_BlockY, a_BlockZ) - m_Player->GetPosition()).Length() <= 5;
if (
(a_BlockFace != BLOCK_FACE_NONE) && // The client is interacting with a specific block

View File

@ -289,7 +289,7 @@ void cCraftingRecipes::GetRecipe(cPlayer & a_Player, cCraftingGrid & a_CraftingG
}
// Built-in recipes:
std::auto_ptr<cRecipe> Recipe(FindRecipe(a_CraftingGrid.GetItems(), a_CraftingGrid.GetWidth(), a_CraftingGrid.GetHeight()));
std::unique_ptr<cRecipe> Recipe(FindRecipe(a_CraftingGrid.GetItems(), a_CraftingGrid.GetWidth(), a_CraftingGrid.GetHeight()));
a_Recipe.Clear();
if (Recipe.get() == nullptr)
{
@ -377,7 +377,7 @@ void cCraftingRecipes::AddRecipeLine(int a_LineNum, const AString & a_RecipeLine
return;
}
std::auto_ptr<cCraftingRecipes::cRecipe> Recipe(new cCraftingRecipes::cRecipe);
std::unique_ptr<cCraftingRecipes::cRecipe> Recipe(new cCraftingRecipes::cRecipe);
// Parse the result:
AStringVector ResultSplit = StringSplit(Sides[0], ",");
@ -758,7 +758,7 @@ cCraftingRecipes::cRecipe * cCraftingRecipes::MatchRecipe(const cItem * a_Crafti
} // for y, for x
// The recipe has matched. Create a copy of the recipe and set its coords to match the crafting grid:
std::auto_ptr<cRecipe> Recipe(new cRecipe);
std::unique_ptr<cRecipe> Recipe(new cRecipe);
Recipe->m_Result = a_Recipe->m_Result;
Recipe->m_Width = a_Recipe->m_Width;
Recipe->m_Height = a_Recipe->m_Height;

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

@ -128,7 +128,7 @@ public:
};
const cItem & GetSlot(int a_Idx) const { return m_Contents.GetSlot(a_Idx); }
void SetSlot(size_t a_Idx, const cItem & a_Item) { m_Contents.SetSlot(a_Idx, a_Item); }
void SetSlot(size_t a_Idx, const cItem & a_Item) { m_Contents.SetSlot(static_cast<int>(a_Idx), a_Item); }
protected:
cItemGrid m_Contents;

View File

@ -115,7 +115,7 @@ void cFurnaceRecipe::AddFuelFromLine(const AString & a_Line, unsigned int a_Line
Line.erase(Line.begin()); // Remove the beginning "!"
Line.erase(std::remove_if(Line.begin(), Line.end(), isspace), Line.end());
std::auto_ptr<cItem> Item(new cItem);
std::unique_ptr<cItem> Item(new cItem);
int BurnTime;
const AStringVector & Sides = StringSplit(Line, "=");
@ -157,8 +157,8 @@ void cFurnaceRecipe::AddRecipeFromLine(const AString & a_Line, unsigned int a_Li
Line.erase(std::remove_if(Line.begin(), Line.end(), isspace), Line.end());
int CookTime = 200;
std::auto_ptr<cItem> InputItem(new cItem());
std::auto_ptr<cItem> OutputItem(new cItem());
std::unique_ptr<cItem> InputItem(new cItem());
std::unique_ptr<cItem> OutputItem(new cItem());
const AStringVector & Sides = StringSplit(Line, "=");
if (Sides.size() != 2)

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

@ -10,6 +10,7 @@ SET (SRCS
ChunkDesc.cpp
ChunkGenerator.cpp
CompoGen.cpp
CompoGenBiomal.cpp
ComposableGenerator.cpp
DistortedHeightmap.cpp
DungeonRoomsFinisher.cpp
@ -30,8 +31,10 @@ SET (SRCS
StructGen.cpp
TestRailsGen.cpp
Trees.cpp
TwoHeights.cpp
UnderwaterBaseGen.cpp
VillageGen.cpp)
VillageGen.cpp
)
SET (HDRS
BioGen.h
@ -39,7 +42,9 @@ SET (HDRS
ChunkDesc.h
ChunkGenerator.h
CompoGen.h
CompoGenBiomal.h
ComposableGenerator.h
CompositedHeiGen.h
DistortedHeightmap.h
DungeonRoomsFinisher.h
EndGen.h
@ -58,11 +63,14 @@ SET (HDRS
RainbowRoadsGen.h
Ravines.h
RoughRavines.h
ShapeGen.cpp
StructGen.h
TestRailsGen.h
Trees.h
TwoHeights.h
UnderwaterBaseGen.h
VillageGen.h)
VillageGen.h
)
if(NOT MSVC)
add_library(Generating ${SRCS} ${HDRS})

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"
@ -152,6 +152,52 @@ int cChunkDesc::GetHeight(int a_RelX, int a_RelZ)
void cChunkDesc::SetHeightFromShape(const Shape & a_Shape)
{
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
for (int y = cChunkDef::Height - 1; y > 0; y--)
{
if (a_Shape[y + x * 256 + z * 16 * 256] != 0)
{
cChunkDef::SetHeight(m_HeightMap, x, z, y);
break;
}
} // for y
} // for x
} // for z
}
void cChunkDesc::GetShapeFromHeight(Shape & a_Shape) const
{
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
int height = cChunkDef::GetHeight(m_HeightMap, x, z);
for (int y = 0; y <= height; y++)
{
a_Shape[y + x * 256 + z * 16 * 256] = 1;
}
for (int y = height + 1; y < cChunkDef::Height; y++)
{
a_Shape[y + x * 256 + z * 16 * 256] = 0;
} // for y
} // for x
} // for z
}
void cChunkDesc::SetUseDefaultBiomes(bool a_bUseDefaultBiomes)
{
m_bUseDefaultBiomes = a_bUseDefaultBiomes;
@ -366,6 +412,23 @@ HEIGHTTYPE cChunkDesc::GetMaxHeight(void) const
HEIGHTTYPE cChunkDesc::GetMinHeight(void) const
{
HEIGHTTYPE MinHeight = m_HeightMap[0];
for (size_t i = 1; i < ARRAYCOUNT(m_HeightMap); i++)
{
if (m_HeightMap[i] < MinHeight)
{
MinHeight = m_HeightMap[i];
}
}
return MinHeight;
}
void cChunkDesc::FillRelCuboid(
int a_MinX, int a_MaxX,
int a_MinY, int a_MaxY,

View File

@ -30,9 +30,16 @@ class cChunkDesc
public:
// tolua_end
/** The datatype used to represent the entire chunk worth of shape.
0 = air
1 = solid
Indexed as [y + 256 * x + 256 * 16 * z]. */
typedef Byte Shape[256 * 16 * 16];
/** Uncompressed block metas, 1 meta per byte */
typedef NIBBLETYPE BlockNibbleBytes[cChunkDef::NumBlocks];
cChunkDesc(int a_ChunkX, int a_ChunkZ);
~cChunkDesc();
@ -57,10 +64,21 @@ public:
EMCSBiome GetBiome(int a_RelX, int a_RelZ);
// These operate on the heightmap, so they could get out of sync with the data
// Use UpdateHeightmap() to re-sync
// Use UpdateHeightmap() to re-calculate heightmap from the block data
void SetHeight(int a_RelX, int a_RelZ, int a_Height);
int GetHeight(int a_RelX, int a_RelZ);
// tolua_end
/** Sets the heightmap to match the given shape data.
Note that this ignores overhangs; the method is mostly used by old composition generators. */
void SetHeightFromShape(const Shape & a_Shape);
/** Sets the shape in a_Shape to match the heightmap stored currently in m_HeightMap. */
void GetShapeFromHeight(Shape & a_Shape) const;
// tolua_begin
// Default generation:
void SetUseDefaultBiomes(bool a_bUseDefaultBiomes);
bool IsUsingDefaultBiomes(void) const;
@ -77,9 +95,12 @@ public:
/** Reads an area from the chunk into a cBlockArea, blocktypes and blockmetas */
void ReadBlockArea(cBlockArea & a_Dest, int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ);
/** Returns the maximum height value in the heightmap */
/** Returns the maximum height value in the heightmap. */
HEIGHTTYPE GetMaxHeight(void) const;
/** Returns the minimum height value in the heightmap. */
HEIGHTTYPE GetMinHeight(void) const;
/** Fills the relative cuboid with specified block; allows cuboid out of range of this chunk */
void FillRelCuboid(
int a_MinX, int a_MaxX,

View File

@ -191,13 +191,13 @@ EMCSBiome cChunkGenerator::GetBiomeAt(int a_BlockX, int a_BlockZ)
BLOCKTYPE cChunkGenerator::GetIniBlock(cIniFile & a_IniFile, const AString & a_SectionName, const AString & a_ValueName, const AString & a_Default)
{
AString BlockType = a_IniFile.GetValueSet(a_SectionName, a_ValueName, a_Default);
BLOCKTYPE Block = BlockStringToType(BlockType);
int Block = BlockStringToType(BlockType);
if (Block < 0)
{
LOGWARN("[%s].%s Could not parse block value \"%s\". Using default: \"%s\".", a_SectionName.c_str(), a_ValueName.c_str(), BlockType.c_str(), a_Default.c_str());
return BlockStringToType(a_Default);
return static_cast<BLOCKTYPE>(BlockStringToType(a_Default));
}
return Block;
return static_cast<BLOCKTYPE>(Block);
}

View File

@ -21,8 +21,9 @@
////////////////////////////////////////////////////////////////////////////////
// cCompoGenSameBlock:
void cCompoGenSameBlock::ComposeTerrain(cChunkDesc & a_ChunkDesc)
void cCompoGenSameBlock::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape)
{
a_ChunkDesc.SetHeightFromShape(a_Shape);
a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
for (int z = 0; z < cChunkDef::Width; z++)
{
@ -63,7 +64,7 @@ void cCompoGenSameBlock::InitializeCompoGen(cIniFile & a_IniFile)
////////////////////////////////////////////////////////////////////////////////
// cCompoGenDebugBiomes:
void cCompoGenDebugBiomes::ComposeTerrain(cChunkDesc & a_ChunkDesc)
void cCompoGenDebugBiomes::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape)
{
static BLOCKTYPE Blocks[] =
{
@ -92,6 +93,7 @@ void cCompoGenDebugBiomes::ComposeTerrain(cChunkDesc & a_ChunkDesc)
E_BLOCK_BEDROCK,
} ;
a_ChunkDesc.SetHeightFromShape(a_Shape);
a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
for (int z = 0; z < cChunkDef::Width; z++)
@ -131,7 +133,7 @@ cCompoGenClassic::cCompoGenClassic(void) :
void cCompoGenClassic::ComposeTerrain(cChunkDesc & a_ChunkDesc)
void cCompoGenClassic::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape)
{
/* The classic composition means:
- 1 layer of grass, 3 of dirt and the rest stone, if the height > sealevel + beachheight
@ -142,6 +144,7 @@ void cCompoGenClassic::ComposeTerrain(cChunkDesc & a_ChunkDesc)
*/
a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
a_ChunkDesc.SetHeightFromShape(a_Shape);
// The patterns to use for different situations, must be same length!
const BLOCKTYPE PatternGround[] = {m_BlockTop, m_BlockMiddle, m_BlockMiddle, m_BlockMiddle} ;
@ -194,7 +197,7 @@ void cCompoGenClassic::ComposeTerrain(cChunkDesc & a_ChunkDesc)
void cCompoGenClassic::InitializeCompoGen(cIniFile & a_IniFile)
{
m_SeaLevel = a_IniFile.GetValueSetI("Generator", "ClassicSeaLevel", m_SeaLevel);
m_SeaLevel = a_IniFile.GetValueSetI("Generator", "SeaLevel", m_SeaLevel);
m_BeachHeight = a_IniFile.GetValueSetI("Generator", "ClassicBeachHeight", m_BeachHeight);
m_BeachDepth = a_IniFile.GetValueSetI("Generator", "ClassicBeachDepth", m_BeachDepth);
m_BlockTop = (BLOCKTYPE)(GetIniItemSet(a_IniFile, "Generator", "ClassicBlockTop", "grass").m_ItemType);
@ -209,323 +212,6 @@ void cCompoGenClassic::InitializeCompoGen(cIniFile & a_IniFile)
////////////////////////////////////////////////////////////////////////////////
// cCompoGenBiomal:
void cCompoGenBiomal::ComposeTerrain(cChunkDesc & a_ChunkDesc)
{
a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
int ChunkX = a_ChunkDesc.GetChunkX();
int ChunkZ = a_ChunkDesc.GetChunkZ();
/*
_X 2013_04_22:
There's no point in generating the whole cubic noise at once, because the noise values are used in
only about 20 % of the cases, so the speed gained by precalculating is lost by precalculating too much data
*/
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
int Height = a_ChunkDesc.GetHeight(x, z);
if (Height > m_SeaLevel)
{
switch (a_ChunkDesc.GetBiome(x, z))
{
case biOcean:
case biPlains:
case biExtremeHills:
case biForest:
case biTaiga:
case biSwampland:
case biRiver:
case biFrozenOcean:
case biFrozenRiver:
case biIcePlains:
case biIceMountains:
case biForestHills:
case biTaigaHills:
case biExtremeHillsEdge:
case biJungle:
case biJungleHills:
case biJungleEdge:
case biDeepOcean:
case biStoneBeach:
case biColdBeach:
case biBirchForest:
case biBirchForestHills:
case biRoofedForest:
case biColdTaiga:
case biColdTaigaHills:
case biExtremeHillsPlus:
case biSavanna:
case biSavannaPlateau:
case biSunflowerPlains:
case biExtremeHillsM:
case biFlowerForest:
case biTaigaM:
case biSwamplandM:
case biIcePlainsSpikes:
case biJungleM:
case biJungleEdgeM:
case biBirchForestM:
case biBirchForestHillsM:
case biRoofedForestM:
case biColdTaigaM:
case biExtremeHillsPlusM:
case biSavannaM:
case biSavannaPlateauM:
{
FillColumnGrass(x, z, Height, a_ChunkDesc.GetBlockTypes());
break;
}
case biMesa:
case biMesaPlateauF:
case biMesaPlateau:
case biMesaBryce:
case biMesaPlateauFM:
case biMesaPlateauM:
{
FillColumnClay(x, z, Height, a_ChunkDesc.GetBlockTypes());
break;
}
case biMegaTaiga:
case biMegaTaigaHills:
case biMegaSpruceTaiga:
case biMegaSpruceTaigaHills:
{
FillColumnDirt(x, z, Height, a_ChunkDesc.GetBlockTypes());
break;
}
case biDesertHills:
case biDesert:
case biDesertM:
case biBeach:
{
FillColumnSand(x, z, Height, a_ChunkDesc.GetBlockTypes());
break;
}
case biMushroomIsland:
case biMushroomShore:
{
FillColumnMycelium(x, z, Height, a_ChunkDesc.GetBlockTypes());
break;
}
default:
{
// TODO
ASSERT(!"CompoGenBiomal: Biome not implemented yet!");
break;
}
}
}
else
{
switch (a_ChunkDesc.GetBiome(x, z))
{
case biDesert:
case biBeach:
{
// Fill with water, sand, sandstone and stone
FillColumnWaterSand(x, z, Height, a_ChunkDesc.GetBlockTypes());
break;
}
default:
{
// Fill with water, sand/dirt/clay mix and stone
if (m_Noise.CubicNoise2D(0.3f * (cChunkDef::Width * ChunkX + x), 0.3f * (cChunkDef::Width * ChunkZ + z)) < 0)
{
FillColumnWaterSand(x, z, Height, a_ChunkDesc.GetBlockTypes());
}
else
{
FillColumnWaterDirt(x, z, Height, a_ChunkDesc.GetBlockTypes());
}
break;
}
} // switch (biome)
a_ChunkDesc.SetHeight(x, z, m_SeaLevel + 1);
} // else (under water)
a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
} // for x
} // for z
}
void cCompoGenBiomal::InitializeCompoGen(cIniFile & a_IniFile)
{
m_SeaLevel = a_IniFile.GetValueSetI("Generator", "BiomalSeaLevel", m_SeaLevel) - 1;
}
void cCompoGenBiomal::FillColumnGrass(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
{
BLOCKTYPE Pattern[] =
{
E_BLOCK_GRASS,
E_BLOCK_DIRT,
E_BLOCK_DIRT,
E_BLOCK_DIRT,
} ;
FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
{
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
}
}
void cCompoGenBiomal::FillColumnClay(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
{
BLOCKTYPE Pattern[] =
{
E_BLOCK_HARDENED_CLAY,
E_BLOCK_HARDENED_CLAY,
E_BLOCK_HARDENED_CLAY,
E_BLOCK_HARDENED_CLAY,
} ;
FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
{
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
}
}
void cCompoGenBiomal::FillColumnDirt(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
{
for (int y = 0; y < 4; y++)
{
if (a_Height - y < 0)
{
return;
}
cChunkDef::SetBlock(a_BlockTypes, a_RelX, a_Height - y, a_RelZ, E_BLOCK_DIRT);
}
for (int y = a_Height - 4; y > 0; y--)
{
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
}
}
void cCompoGenBiomal::FillColumnSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
{
BLOCKTYPE Pattern[] =
{
E_BLOCK_SAND,
E_BLOCK_SAND,
E_BLOCK_SAND,
E_BLOCK_SANDSTONE,
} ;
FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
{
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
}
}
void cCompoGenBiomal::FillColumnMycelium (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
{
BLOCKTYPE Pattern[] =
{
E_BLOCK_MYCELIUM,
E_BLOCK_DIRT,
E_BLOCK_DIRT,
E_BLOCK_DIRT,
} ;
FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
{
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
}
}
void cCompoGenBiomal::FillColumnWaterSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
{
FillColumnSand(a_RelX, a_RelZ, a_Height, a_BlockTypes);
for (int y = a_Height + 1; y <= m_SeaLevel + 1; y++)
{
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER);
}
}
void cCompoGenBiomal::FillColumnWaterDirt(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes)
{
// Dirt
BLOCKTYPE Pattern[] =
{
E_BLOCK_DIRT,
E_BLOCK_DIRT,
E_BLOCK_DIRT,
E_BLOCK_DIRT,
} ;
FillColumnPattern(a_RelX, a_RelZ, a_Height, a_BlockTypes, Pattern, ARRAYCOUNT(Pattern));
for (int y = a_Height - ARRAYCOUNT(Pattern); y > 0; y--)
{
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STONE);
}
for (int y = a_Height + 1; y <= m_SeaLevel + 1; y++)
{
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER);
}
}
void cCompoGenBiomal::FillColumnPattern(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes, const BLOCKTYPE * a_Pattern, int a_PatternSize)
{
for (int y = a_Height, idx = 0; (y >= 0) && (idx < a_PatternSize); y--, idx++)
{
cChunkDef::SetBlock(a_BlockTypes, a_RelX, y, a_RelZ, a_Pattern[idx]);
}
}
////////////////////////////////////////////////////////////////////////////////
// cCompoGenNether:
@ -540,7 +226,7 @@ cCompoGenNether::cCompoGenNether(int a_Seed) :
void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc)
void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape)
{
HEIGHTTYPE MaxHeight = a_ChunkDesc.GetMaxHeight();
@ -696,7 +382,7 @@ cCompoGenCache::~cCompoGenCache()
void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc)
void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape)
{
#ifdef _DEBUG
if (((m_NumHits + m_NumMisses) % 1024) == 10)
@ -731,6 +417,7 @@ void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc)
// Use the cached data:
memcpy(a_ChunkDesc.GetBlockTypes(), m_CacheData[Idx].m_BlockTypes, sizeof(a_ChunkDesc.GetBlockTypes()));
memcpy(a_ChunkDesc.GetBlockMetasUncompressed(), m_CacheData[Idx].m_BlockMetas, sizeof(a_ChunkDesc.GetBlockMetasUncompressed()));
memcpy(a_ChunkDesc.GetHeightMap(), m_CacheData[Idx].m_HeightMap, sizeof(a_ChunkDesc.GetHeightMap()));
m_NumHits++;
m_TotalChain += i;
@ -739,7 +426,7 @@ void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc)
// Not in the cache:
m_NumMisses++;
m_Underlying->ComposeTerrain(a_ChunkDesc);
m_Underlying->ComposeTerrain(a_ChunkDesc, a_Shape);
// Insert it as the first item in the MRU order:
int Idx = m_CacheOrder[m_CacheSize - 1];
@ -750,6 +437,7 @@ void cCompoGenCache::ComposeTerrain(cChunkDesc & a_ChunkDesc)
m_CacheOrder[0] = Idx;
memcpy(m_CacheData[Idx].m_BlockTypes, a_ChunkDesc.GetBlockTypes(), sizeof(a_ChunkDesc.GetBlockTypes()));
memcpy(m_CacheData[Idx].m_BlockMetas, a_ChunkDesc.GetBlockMetasUncompressed(), sizeof(a_ChunkDesc.GetBlockMetasUncompressed()));
memcpy(m_CacheData[Idx].m_HeightMap, a_ChunkDesc.GetHeightMap(), sizeof(a_ChunkDesc.GetHeightMap()));
m_CacheData[Idx].m_ChunkX = ChunkX;
m_CacheData[Idx].m_ChunkZ = ChunkZ;
}

View File

@ -17,7 +17,7 @@
#pragma once
#include "ComposableGenerator.h"
#include "../Noise.h"
#include "../Noise/Noise.h"
@ -38,7 +38,7 @@ protected:
bool m_IsBedrocked;
// cTerrainCompositionGen overrides:
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) override;
virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
} ;
@ -55,7 +55,7 @@ public:
protected:
// cTerrainCompositionGen overrides:
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) override;
} ;
@ -81,7 +81,7 @@ protected:
BLOCKTYPE m_BlockSea;
// cTerrainCompositionGen overrides:
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) override;
virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
} ;
@ -89,40 +89,6 @@ protected:
class cCompoGenBiomal :
public cTerrainCompositionGen
{
public:
cCompoGenBiomal(int a_Seed) :
m_Noise(a_Seed + 1000),
m_SeaLevel(62)
{
}
protected:
cNoise m_Noise;
int m_SeaLevel;
// cTerrainCompositionGen overrides:
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
void FillColumnGrass (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
void FillColumnClay (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
void FillColumnDirt (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
void FillColumnSand (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
void FillColumnMycelium (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
void FillColumnWaterSand(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
void FillColumnWaterDirt(int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes);
void FillColumnPattern (int a_RelX, int a_RelZ, int a_Height, cChunkDef::BlockTypes & a_BlockTypes, const BLOCKTYPE * a_Pattern, int a_PatternSize);
} ;
class cCompoGenNether :
public cTerrainCompositionGen
{
@ -136,7 +102,7 @@ protected:
int m_Threshold;
// cTerrainCompositionGen overrides:
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) override;
virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
} ;
@ -153,7 +119,7 @@ public:
~cCompoGenCache();
// cTerrainCompositionGen override:
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) override;
virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
protected:
@ -166,6 +132,7 @@ protected:
int m_ChunkZ;
cChunkDef::BlockTypes m_BlockTypes;
cChunkDesc::BlockNibbleBytes m_BlockMetas; // The metas are uncompressed, 1 meta per byte
cChunkDef::HeightMap m_HeightMap;
} ;
// To avoid moving large amounts of data for the MRU behavior, we MRU-ize indices to an array of the actual data

View File

@ -0,0 +1,586 @@
// CompoGenBiomal.cpp
// Implements the cCompoGenBiomal class representing the biome-aware composition generator
#include "Globals.h"
#include "ComposableGenerator.h"
#include "../IniFile.h"
#include "../Noise/Noise.h"
#include "../LinearUpscale.h"
////////////////////////////////////////////////////////////////////////////////
// cPattern:
/** This class is used to store a column pattern initialized at runtime,
so that the program doesn't need to explicitly set 256 values for each pattern
Each pattern has 256 blocks so that there's no need to check pattern bounds when assigning the
pattern - there will always be enough pattern left, even for the whole-chunk-height columns. */
class cPattern
{
public:
struct BlockInfo
{
BLOCKTYPE m_BlockType;
NIBBLETYPE m_BlockMeta;
};
cPattern(BlockInfo * a_TopBlocks, size_t a_Count)
{
// Copy the pattern into the top:
for (size_t i = 0; i < a_Count; i++)
{
m_Pattern[i] = a_TopBlocks[i];
}
// Fill the rest with stone:
static BlockInfo Stone = {E_BLOCK_STONE, 0};
for (int i = static_cast<int>(a_Count); i < cChunkDef::Height; i++)
{
m_Pattern[i] = Stone;
}
}
const BlockInfo * Get(void) const { return m_Pattern; }
protected:
BlockInfo m_Pattern[cChunkDef::Height];
} ;
////////////////////////////////////////////////////////////////////////////////
// The arrays to use for the top block pattern definitions:
static cPattern::BlockInfo tbGrass[] =
{
{E_BLOCK_GRASS, 0},
{E_BLOCK_DIRT, E_META_DIRT_NORMAL},
{E_BLOCK_DIRT, E_META_DIRT_NORMAL},
{E_BLOCK_DIRT, E_META_DIRT_NORMAL},
} ;
static cPattern::BlockInfo tbSand[] =
{
{ E_BLOCK_SAND, 0},
{ E_BLOCK_SAND, 0},
{ E_BLOCK_SAND, 0},
{ E_BLOCK_SANDSTONE, 0},
} ;
static cPattern::BlockInfo tbDirt[] =
{
{E_BLOCK_DIRT, E_META_DIRT_NORMAL},
{E_BLOCK_DIRT, E_META_DIRT_NORMAL},
{E_BLOCK_DIRT, E_META_DIRT_NORMAL},
{E_BLOCK_DIRT, E_META_DIRT_NORMAL},
} ;
static cPattern::BlockInfo tbPodzol[] =
{
{E_BLOCK_DIRT, E_META_DIRT_PODZOL},
{E_BLOCK_DIRT, E_META_DIRT_NORMAL},
{E_BLOCK_DIRT, E_META_DIRT_NORMAL},
{E_BLOCK_DIRT, E_META_DIRT_NORMAL},
} ;
static cPattern::BlockInfo tbGrassLess[] =
{
{E_BLOCK_DIRT, E_META_DIRT_GRASSLESS},
{E_BLOCK_DIRT, E_META_DIRT_NORMAL},
{E_BLOCK_DIRT, E_META_DIRT_NORMAL},
{E_BLOCK_DIRT, E_META_DIRT_NORMAL},
} ;
static cPattern::BlockInfo tbMycelium[] =
{
{E_BLOCK_MYCELIUM, 0},
{E_BLOCK_DIRT, 0},
{E_BLOCK_DIRT, 0},
{E_BLOCK_DIRT, 0},
} ;
static cPattern::BlockInfo tbGravel[] =
{
{E_BLOCK_GRAVEL, 0},
{E_BLOCK_GRAVEL, 0},
{E_BLOCK_GRAVEL, 0},
{E_BLOCK_STONE, 0},
} ;
static cPattern::BlockInfo tbStone[] =
{
{E_BLOCK_STONE, 0},
{E_BLOCK_STONE, 0},
{E_BLOCK_STONE, 0},
{E_BLOCK_STONE, 0},
} ;
////////////////////////////////////////////////////////////////////////////////
// Ocean floor pattern top-block definitions:
static cPattern::BlockInfo tbOFSand[] =
{
{E_BLOCK_SAND, 0},
{E_BLOCK_SAND, 0},
{E_BLOCK_SAND, 0},
{E_BLOCK_SANDSTONE, 0}
} ;
static cPattern::BlockInfo tbOFClay[] =
{
{ E_BLOCK_CLAY, 0},
{ E_BLOCK_CLAY, 0},
{ E_BLOCK_SAND, 0},
{ E_BLOCK_SAND, 0},
} ;
static cPattern::BlockInfo tbOFOrangeClay[] =
{
{ E_BLOCK_STAINED_CLAY, E_META_STAINED_GLASS_ORANGE},
{ E_BLOCK_STAINED_CLAY, E_META_STAINED_GLASS_ORANGE},
{ E_BLOCK_STAINED_CLAY, E_META_STAINED_GLASS_ORANGE},
} ;
////////////////////////////////////////////////////////////////////////////////
// Individual patterns to use:
static cPattern patGrass (tbGrass, ARRAYCOUNT(tbGrass));
static cPattern patSand (tbSand, ARRAYCOUNT(tbSand));
static cPattern patDirt (tbDirt, ARRAYCOUNT(tbDirt));
static cPattern patPodzol (tbPodzol, ARRAYCOUNT(tbPodzol));
static cPattern patGrassLess(tbGrassLess, ARRAYCOUNT(tbGrassLess));
static cPattern patMycelium (tbMycelium, ARRAYCOUNT(tbMycelium));
static cPattern patGravel (tbGravel, ARRAYCOUNT(tbGravel));
static cPattern patStone (tbStone, ARRAYCOUNT(tbStone));
static cPattern patOFSand (tbOFSand, ARRAYCOUNT(tbOFSand));
static cPattern patOFClay (tbOFClay, ARRAYCOUNT(tbOFClay));
static cPattern patOFOrangeClay(tbOFOrangeClay, ARRAYCOUNT(tbOFOrangeClay));
////////////////////////////////////////////////////////////////////////////////
// cCompoGenBiomal:
class cCompoGenBiomal :
public cTerrainCompositionGen
{
public:
cCompoGenBiomal(int a_Seed) :
m_SeaLevel(62),
m_OceanFloorSelect(a_Seed + 1),
m_MesaFloor(a_Seed + 2)
{
initMesaPattern(a_Seed);
}
protected:
/** The block height at which water is generated instead of air. */
int m_SeaLevel;
/** The pattern used for mesa biomes. Initialized by seed on generator creation. */
cPattern::BlockInfo m_MesaPattern[2 * cChunkDef::Height];
/** Noise used for selecting between dirt and sand on the ocean floor. */
cNoise m_OceanFloorSelect;
/** Noise used for the floor of the clay blocks in mesa biomes. */
cNoise m_MesaFloor;
// cTerrainCompositionGen overrides:
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) override
{
a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
ComposeColumn(a_ChunkDesc, x, z, &(a_Shape[x * 256 + z * 16 * 256]));
} // for x
} // for z
}
virtual void InitializeCompoGen(cIniFile & a_IniFile) override
{
m_SeaLevel = a_IniFile.GetValueSetI("Generator", "SeaLevel", m_SeaLevel);
}
/** Initializes the m_MesaPattern with a pattern based on the generator's seed. */
void initMesaPattern(int a_Seed)
{
// In a loop, choose whether to use one, two or three layers of stained clay, then choose a color and width for each layer
// Separate each group with another layer of hardened clay
cNoise patternNoise((unsigned)a_Seed);
static NIBBLETYPE allowedColors[] =
{
E_META_STAINED_CLAY_YELLOW,
E_META_STAINED_CLAY_YELLOW,
E_META_STAINED_CLAY_RED,
E_META_STAINED_CLAY_RED,
E_META_STAINED_CLAY_WHITE,
E_META_STAINED_CLAY_BROWN,
E_META_STAINED_CLAY_BROWN,
E_META_STAINED_CLAY_BROWN,
E_META_STAINED_CLAY_ORANGE,
E_META_STAINED_CLAY_ORANGE,
E_META_STAINED_CLAY_ORANGE,
E_META_STAINED_CLAY_ORANGE,
E_META_STAINED_CLAY_ORANGE,
E_META_STAINED_CLAY_ORANGE,
E_META_STAINED_CLAY_LIGHTGRAY,
} ;
static int layerSizes[] = // Adjust the chance so that thinner layers occur more commonly
{
1, 1, 1, 1, 1, 1,
2, 2, 2, 2,
3, 3,
} ;
int idx = ARRAYCOUNT(m_MesaPattern) - 1;
while (idx >= 0)
{
// A layer group of 1 - 2 color stained clay:
int rnd = patternNoise.IntNoise1DInt(idx) / 7;
int numLayers = (rnd % 2) + 1;
rnd /= 2;
for (int lay = 0; lay < numLayers; lay++)
{
int numBlocks = layerSizes[(rnd % ARRAYCOUNT(layerSizes))];
NIBBLETYPE Color = allowedColors[(rnd / 4) % ARRAYCOUNT(allowedColors)];
if (
((numBlocks == 3) && (numLayers == 2)) || // In two-layer mode disallow the 3-high layers:
(Color == E_META_STAINED_CLAY_WHITE)) // White stained clay can ever be only 1 block high
{
numBlocks = 1;
}
numBlocks = std::min(idx + 1, numBlocks); // Limit by idx so that we don't have to check inside the loop
rnd /= 32;
for (int block = 0; block < numBlocks; block++, idx--)
{
m_MesaPattern[idx].m_BlockMeta = Color;
m_MesaPattern[idx].m_BlockType = E_BLOCK_STAINED_CLAY;
} // for block
} // for lay
// A layer of hardened clay in between the layer group:
int numBlocks = (rnd % 4) + 1; // All heights the same probability
if ((numLayers == 2) && (numBlocks < 4))
{
// For two layers of stained clay, add an extra block of hardened clay:
numBlocks++;
}
numBlocks = std::min(idx + 1, numBlocks); // Limit by idx so that we don't have to check inside the loop
for (int block = 0; block < numBlocks; block++, idx--)
{
m_MesaPattern[idx].m_BlockMeta = 0;
m_MesaPattern[idx].m_BlockType = E_BLOCK_HARDENED_CLAY;
} // for block
} // while (idx >= 0)
}
/** Composes a single column in a_ChunkDesc. Chooses what to do based on the biome in that column. */
void ComposeColumn(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ, const Byte * a_ShapeColumn)
{
// Frequencies for the podzol floor selecting noise:
const NOISE_DATATYPE FrequencyX = 8;
const NOISE_DATATYPE FrequencyZ = 8;
EMCSBiome Biome = a_ChunkDesc.GetBiome(a_RelX, a_RelZ);
switch (Biome)
{
case biOcean:
case biPlains:
case biForest:
case biTaiga:
case biSwampland:
case biRiver:
case biFrozenOcean:
case biFrozenRiver:
case biIcePlains:
case biIceMountains:
case biForestHills:
case biTaigaHills:
case biExtremeHillsEdge:
case biExtremeHillsPlus:
case biExtremeHills:
case biJungle:
case biJungleHills:
case biJungleEdge:
case biDeepOcean:
case biStoneBeach:
case biColdBeach:
case biBirchForest:
case biBirchForestHills:
case biRoofedForest:
case biColdTaiga:
case biColdTaigaHills:
case biSavanna:
case biSavannaPlateau:
case biSunflowerPlains:
case biFlowerForest:
case biTaigaM:
case biSwamplandM:
case biIcePlainsSpikes:
case biJungleM:
case biJungleEdgeM:
case biBirchForestM:
case biBirchForestHillsM:
case biRoofedForestM:
case biColdTaigaM:
case biSavannaM:
case biSavannaPlateauM:
{
FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, patGrass.Get(), a_ShapeColumn);
return;
}
case biMegaTaiga:
case biMegaTaigaHills:
case biMegaSpruceTaiga:
case biMegaSpruceTaigaHills:
{
// Select the pattern to use - podzol, grass or grassless dirt:
NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + a_RelX)) / FrequencyX;
NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + a_RelZ)) / FrequencyZ;
NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY);
const cPattern::BlockInfo * Pattern = (Val < -0.9) ? patGrassLess.Get() : ((Val > 0) ? patPodzol.Get() : patGrass.Get());
FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, Pattern, a_ShapeColumn);
return;
}
case biDesertHills:
case biDesert:
case biDesertM:
case biBeach:
{
FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, patSand.Get(), a_ShapeColumn);
return;
}
case biMushroomIsland:
case biMushroomShore:
{
FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, patMycelium.Get(), a_ShapeColumn);
return;
}
case biMesa:
case biMesaPlateauF:
case biMesaPlateau:
case biMesaBryce:
case biMesaPlateauFM:
case biMesaPlateauM:
{
// Mesa biomes need special handling, because they don't follow the usual "4 blocks from top pattern",
// instead, they provide a "from bottom" pattern with varying base height,
// usually 4 blocks below the ocean level
FillColumnMesa(a_ChunkDesc, a_RelX, a_RelZ, a_ShapeColumn);
return;
}
case biExtremeHillsPlusM:
case biExtremeHillsM:
{
// Select the pattern to use - gravel, stone or grass:
NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + a_RelX)) / FrequencyX;
NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + a_RelZ)) / FrequencyZ;
NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY);
const cPattern::BlockInfo * Pattern = (Val < 0.0) ? patStone.Get() : patGrass.Get();
FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, Pattern, a_ShapeColumn);
return;
}
default:
{
ASSERT(!"Unhandled biome");
return;
}
} // switch (Biome)
}
/** Fills the specified column with the specified pattern; restarts the pattern when air is reached,
switches to ocean floor pattern if ocean is reached. Always adds bedrock at the very bottom. */
void FillColumnPattern(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ, const cPattern::BlockInfo * a_Pattern, const Byte * a_ShapeColumn)
{
bool HasHadWater = false;
int PatternIdx = 0;
int top = std::max(m_SeaLevel, a_ChunkDesc.GetHeight(a_RelX, a_RelZ));
for (int y = top; y > 0; y--)
{
if (a_ShapeColumn[y] > 0)
{
// "ground" part, use the pattern:
a_ChunkDesc.SetBlockTypeMeta(a_RelX, y, a_RelZ, a_Pattern[PatternIdx].m_BlockType, a_Pattern[PatternIdx].m_BlockMeta);
PatternIdx++;
continue;
}
// "air" or "water" part:
// Reset the pattern index to zero, so that the pattern is repeated from the top again:
PatternIdx = 0;
if (y >= m_SeaLevel)
{
// "air" part, do nothing
continue;
}
a_ChunkDesc.SetBlockType(a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER);
if (HasHadWater)
{
continue;
}
// Select the ocean-floor pattern to use:
if (a_ChunkDesc.GetBiome(a_RelX, a_RelZ) == biDeepOcean)
{
a_Pattern = patGravel.Get();
}
else
{
a_Pattern = ChooseOceanFloorPattern(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ(), a_RelX, a_RelZ);
}
HasHadWater = true;
} // for y
a_ChunkDesc.SetBlockType(a_RelX, 0, a_RelZ, E_BLOCK_BEDROCK);
}
/** Fills the specified column with mesa pattern, based on the column height */
void FillColumnMesa(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ, const Byte * a_ShapeColumn)
{
// Frequencies for the clay floor noise:
const NOISE_DATATYPE FrequencyX = 50;
const NOISE_DATATYPE FrequencyZ = 50;
int Top = a_ChunkDesc.GetHeight(a_RelX, a_RelZ);
if (Top < m_SeaLevel)
{
// The terrain is below sealevel, handle as regular ocean with red sand floor:
FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, patOFOrangeClay.Get(), a_ShapeColumn);
return;
}
NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkX() * cChunkDef::Width + a_RelX)) / FrequencyX;
NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + a_RelZ)) / FrequencyZ;
int ClayFloor = m_SeaLevel - 6 + (int)(4.f * m_MesaFloor.CubicNoise2D(NoiseX, NoiseY));
if (ClayFloor >= Top)
{
ClayFloor = Top - 1;
}
if (Top - m_SeaLevel < 5)
{
// Simple case: top is red sand, then hardened clay down to ClayFloor, then stone:
a_ChunkDesc.SetBlockTypeMeta(a_RelX, Top, a_RelZ, E_BLOCK_SAND, E_META_SAND_RED);
for (int y = Top - 1; y >= ClayFloor; y--)
{
a_ChunkDesc.SetBlockType(a_RelX, y, a_RelZ, E_BLOCK_HARDENED_CLAY);
}
for (int y = ClayFloor - 1; y > 0; y--)
{
a_ChunkDesc.SetBlockType(a_RelX, y, a_RelZ, E_BLOCK_STONE);
}
a_ChunkDesc.SetBlockType(a_RelX, 0, a_RelZ, E_BLOCK_BEDROCK);
return;
}
// Difficult case: use the mesa pattern and watch for overhangs:
int PatternIdx = cChunkDef::Height - (Top - ClayFloor); // We want the block at index ClayFloor to be pattern's 256th block (first stone)
const cPattern::BlockInfo * Pattern = m_MesaPattern;
bool HasHadWater = false;
for (int y = Top; y > 0; y--)
{
if (a_ShapeColumn[y] > 0)
{
// "ground" part, use the pattern:
a_ChunkDesc.SetBlockTypeMeta(a_RelX, y, a_RelZ, Pattern[PatternIdx].m_BlockType, Pattern[PatternIdx].m_BlockMeta);
PatternIdx++;
continue;
}
if (y >= m_SeaLevel)
{
// "air" part, do nothing
continue;
}
// "water" part, fill with water and choose new pattern for ocean floor, if not chosen already:
PatternIdx = 0;
a_ChunkDesc.SetBlockType(a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER);
if (HasHadWater)
{
continue;
}
// Select the ocean-floor pattern to use:
Pattern = ChooseOceanFloorPattern(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ(), a_RelX, a_RelZ);
HasHadWater = true;
} // for y
a_ChunkDesc.SetBlockType(a_RelX, 0, a_RelZ, E_BLOCK_BEDROCK);
}
/** Returns the pattern to use for an ocean floor in the specified column.
The returned pattern is guaranteed to be 256 blocks long. */
const cPattern::BlockInfo * ChooseOceanFloorPattern(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ)
{
// Frequencies for the ocean floor selecting noise:
const NOISE_DATATYPE FrequencyX = 3;
const NOISE_DATATYPE FrequencyZ = 3;
// Select the ocean-floor pattern to use:
NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(a_ChunkX * cChunkDef::Width + a_RelX)) / FrequencyX;
NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(a_ChunkZ * cChunkDef::Width + a_RelZ)) / FrequencyZ;
NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY);
if (Val < -0.95)
{
return patOFClay.Get();
}
else if (Val < 0)
{
return patOFSand.Get();
}
else
{
return patDirt.Get();
}
}
} ;
cTerrainCompositionGenPtr CreateCompoGenBiomal(int a_Seed)
{
return std::make_shared<cCompoGenBiomal>(a_Seed);
}

View File

@ -0,0 +1,21 @@
// CompoGenBiomal.h
#pragma once
#include "ComposableGenerator.h"
/** Returns a new instance of the Biomal composition generator. */
cTerrainCompositionGenPtr CreateCompoGenBiomal(int a_Seed);

View File

@ -17,6 +17,10 @@
#include "StructGen.h"
#include "FinishGen.h"
#include "CompoGenBiomal.h"
#include "CompositedHeiGen.h"
#include "Caves.h"
#include "DistortedHeightmap.h"
#include "DungeonRoomsFinisher.h"
@ -39,7 +43,7 @@
////////////////////////////////////////////////////////////////////////////////
// cTerrainCompositionGen:
cTerrainCompositionGenPtr cTerrainCompositionGen::CreateCompositionGen(cIniFile & a_IniFile, cBiomeGenPtr a_BiomeGen, cTerrainHeightGen & a_HeightGen, int a_Seed)
cTerrainCompositionGenPtr cTerrainCompositionGen::CreateCompositionGen(cIniFile & a_IniFile, cBiomeGenPtr a_BiomeGen, cTerrainShapeGenPtr a_ShapeGen, int a_Seed)
{
AString CompoGenName = a_IniFile.GetValueSet("Generator", "CompositionGen", "");
if (CompoGenName.empty())
@ -48,63 +52,52 @@ cTerrainCompositionGenPtr cTerrainCompositionGen::CreateCompositionGen(cIniFile
CompoGenName = "Biomal";
}
cTerrainCompositionGen * res = nullptr;
if (NoCaseCompare(CompoGenName, "sameblock") == 0)
// Compositor list is alpha-sorted
cTerrainCompositionGenPtr res;
if (NoCaseCompare(CompoGenName, "Biomal") == 0)
{
res = new cCompoGenSameBlock;
}
else if (NoCaseCompare(CompoGenName, "debugbiomes") == 0)
{
res = new cCompoGenDebugBiomes;
}
else if (NoCaseCompare(CompoGenName, "classic") == 0)
{
res = new cCompoGenClassic;
}
else if (NoCaseCompare(CompoGenName, "DistortedHeightmap") == 0)
{
res = new cDistortedHeightmap(a_Seed, a_BiomeGen);
}
else if (NoCaseCompare(CompoGenName, "end") == 0)
{
res = new cEndGen(a_Seed);
}
else if (NoCaseCompare(CompoGenName, "nether") == 0)
{
res = new cCompoGenNether(a_Seed);
res = CreateCompoGenBiomal(a_Seed);
}
else if (NoCaseCompare(CompoGenName, "BiomalNoise3D") == 0)
{
res = new cBiomalNoise3DComposable(a_Seed, a_BiomeGen);
// The composition that used to be provided with BiomalNoise3D is now provided by the Biomal compositor:
res = CreateCompoGenBiomal(a_Seed);
}
else if (NoCaseCompare(CompoGenName, "Classic") == 0)
{
res = std::make_shared<cCompoGenClassic>();
}
else if (NoCaseCompare(CompoGenName, "DebugBiomes") == 0)
{
res = std::make_shared<cCompoGenDebugBiomes>();
}
else if (NoCaseCompare(CompoGenName, "DistortedHeightmap") == 0)
{
// The composition that used to be provided with DistortedHeightmap is now provided by the Biomal compositor:
res = CreateCompoGenBiomal(a_Seed);
}
else if (NoCaseCompare(CompoGenName, "End") == 0)
{
res = std::make_shared<cEndGen>(a_Seed);
}
else if (NoCaseCompare(CompoGenName, "Nether") == 0)
{
res = std::make_shared<cCompoGenNether>(a_Seed);
}
else if (NoCaseCompare(CompoGenName, "Noise3D") == 0)
{
res = new cNoise3DComposable(a_Seed);
// The composition that used to be provided with Noise3D is now provided by the Biomal compositor:
res = CreateCompoGenBiomal(a_Seed);
}
else if (NoCaseCompare(CompoGenName, "biomal") == 0)
else if (NoCaseCompare(CompoGenName, "SameBlock") == 0)
{
res = new cCompoGenBiomal(a_Seed);
/*
// Performance-testing:
LOGINFO("Measuring performance of cCompoGenBiomal...");
clock_t BeginTick = clock();
for (int x = 0; x < 500; x++)
{
cChunkDesc Desc(200 + x * 8, 200 + x * 8);
a_BiomeGen->GenBiomes(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetBiomeMap());
a_HeightGen->GenHeightMap(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetHeightMap());
res->ComposeTerrain(Desc);
}
clock_t Duration = clock() - BeginTick;
LOGINFO("CompositionGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC);
//*/
res = std::make_shared<cCompoGenSameBlock>();
}
else
{
LOGWARN("Unknown CompositionGen \"%s\", using \"Biomal\" instead.", CompoGenName.c_str());
a_IniFile.SetValue("Generator", "CompositionGen", "Biomal");
return CreateCompositionGen(a_IniFile, a_BiomeGen, a_HeightGen, a_Seed);
return CreateCompositionGen(a_IniFile, a_BiomeGen, a_ShapeGen, a_Seed);
}
ASSERT(res != nullptr);
@ -124,7 +117,7 @@ cTerrainCompositionGenPtr cTerrainCompositionGen::CreateCompositionGen(cIniFile
cComposableGenerator::cComposableGenerator(cChunkGenerator & a_ChunkGenerator) :
super(a_ChunkGenerator),
m_BiomeGen(),
m_HeightGen(),
m_ShapeGen(),
m_CompositionGen()
{
}
@ -138,7 +131,7 @@ void cComposableGenerator::Initialize(cIniFile & a_IniFile)
super::Initialize(a_IniFile);
InitBiomeGen(a_IniFile);
InitHeightGen(a_IniFile);
InitShapeGen(a_IniFile);
InitCompositionGen(a_IniFile);
InitFinishGens(a_IniFile);
}
@ -166,16 +159,22 @@ void cComposableGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a
m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, a_ChunkDesc.GetBiomeMap());
}
cChunkDesc::Shape shape;
if (a_ChunkDesc.IsUsingDefaultHeight())
{
m_HeightGen->GenHeightMap(a_ChunkX, a_ChunkZ, a_ChunkDesc.GetHeightMap());
m_ShapeGen->GenShape(a_ChunkX, a_ChunkZ, shape);
a_ChunkDesc.SetHeightFromShape(shape);
}
else
{
// Convert the heightmap in a_ChunkDesc into shape:
a_ChunkDesc.GetShapeFromHeight(shape);
}
bool ShouldUpdateHeightmap = false;
if (a_ChunkDesc.IsUsingDefaultComposition())
{
m_CompositionGen->ComposeTerrain(a_ChunkDesc);
ShouldUpdateHeightmap = true;
m_CompositionGen->ComposeTerrain(a_ChunkDesc, shape);
}
if (a_ChunkDesc.IsUsingDefaultFinish())
@ -234,13 +233,15 @@ void cComposableGenerator::InitBiomeGen(cIniFile & a_IniFile)
void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile)
void cComposableGenerator::InitShapeGen(cIniFile & a_IniFile)
{
bool CacheOffByDefault = false;
m_HeightGen = cTerrainHeightGen::CreateHeightGen(a_IniFile, m_BiomeGen, m_ChunkGenerator.GetSeed(), CacheOffByDefault);
m_ShapeGen = cTerrainShapeGen::CreateShapeGen(a_IniFile, m_BiomeGen, m_ChunkGenerator.GetSeed(), CacheOffByDefault);
/*
// TODO
// Add a cache, if requested:
int CacheSize = a_IniFile.GetValueSetI("Generator", "HeightGenCacheSize", CacheOffByDefault ? 0 : 64);
int CacheSize = a_IniFile.GetValueSetI("Generator", "ShapeGenCacheSize", CacheOffByDefault ? 0 : 64);
if (CacheSize > 0)
{
if (CacheSize < 4)
@ -253,6 +254,7 @@ void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile)
LOGD("Using a cache for Heightgen of size %d.", CacheSize);
m_HeightGen = cTerrainHeightGenPtr(new cHeiGenCache(m_HeightGen, CacheSize));
}
*/
}
@ -261,13 +263,19 @@ void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile)
void cComposableGenerator::InitCompositionGen(cIniFile & a_IniFile)
{
m_CompositionGen = cTerrainCompositionGen::CreateCompositionGen(a_IniFile, m_BiomeGen, *m_HeightGen, m_ChunkGenerator.GetSeed());
m_CompositionGen = cTerrainCompositionGen::CreateCompositionGen(a_IniFile, m_BiomeGen, m_ShapeGen, m_ChunkGenerator.GetSeed());
// Add a cache over the composition generator:
// Even a cache of size 1 is useful due to the CompositedHeiGen cache after us doing re-composition on its misses
int CompoGenCacheSize = a_IniFile.GetValueSetI("Generator", "CompositionGenCacheSize", 64);
if (CompoGenCacheSize > 1)
if (CompoGenCacheSize > 0)
{
m_CompositionGen = cTerrainCompositionGenPtr(new cCompoGenCache(m_CompositionGen, 32));
m_CompositionGen = std::make_shared<cCompoGenCache>(m_CompositionGen, CompoGenCacheSize);
}
// Create a cache of the composited heightmaps, so that finishers may use it:
m_CompositedHeightCache = std::make_shared<cHeiGenMultiCache>(std::make_shared<cCompositedHeiGen>(m_ShapeGen, m_CompositionGen), 16, 24);
// 24 subcaches of depth 16 each = 96 KiB of RAM. Acceptable, for the amount of work this saves.
}
@ -333,7 +341,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
int MaxSize = a_IniFile.GetValueSetI("Generator", "DungeonRoomsMaxSize", 7);
int MinSize = a_IniFile.GetValueSetI("Generator", "DungeonRoomsMinSize", 5);
AString HeightDistrib = a_IniFile.GetValueSet ("Generator", "DungeonRoomsHeightDistrib", "0, 0; 10, 10; 11, 500; 40, 500; 60, 40; 90, 1");
m_FinishGens.push_back(cFinishGenPtr(new cDungeonRoomsFinisher(m_HeightGen, Seed, GridSize, MaxSize, MinSize, HeightDistrib)));
m_FinishGens.push_back(cFinishGenPtr(new cDungeonRoomsFinisher(m_ShapeGen, Seed, GridSize, MaxSize, MinSize, HeightDistrib)));
}
else if (NoCaseCompare(*itr, "Ice") == 0)
{
@ -342,7 +350,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
else if (NoCaseCompare(*itr, "LavaLakes") == 0)
{
int Probability = a_IniFile.GetValueSetI("Generator", "LavaLakesProbability", 10);
m_FinishGens.push_back(cFinishGenPtr(new cStructGenLakes(Seed * 5 + 16873, E_BLOCK_STATIONARY_LAVA, m_HeightGen, Probability)));
m_FinishGens.push_back(cFinishGenPtr(new cStructGenLakes(Seed * 5 + 16873, E_BLOCK_STATIONARY_LAVA, m_ShapeGen, Probability)));
}
else if (NoCaseCompare(*itr, "LavaSprings") == 0)
{
@ -580,7 +588,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
}
else if (NoCaseCompare(*itr, "Trees") == 0)
{
m_FinishGens.push_back(cFinishGenPtr(new cStructGenTrees(Seed, m_BiomeGen, m_HeightGen, m_CompositionGen)));
m_FinishGens.push_back(cFinishGenPtr(new cStructGenTrees(Seed, m_BiomeGen, m_ShapeGen, m_CompositionGen)));
}
else if (NoCaseCompare(*itr, "UnderwaterBases") == 0)
{
@ -588,7 +596,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
int MaxOffset = a_IniFile.GetValueSetI("Generator", "UnderwaterBaseMaxOffset", 128);
int MaxDepth = a_IniFile.GetValueSetI("Generator", "UnderwaterBaseMaxDepth", 7);
int MaxSize = a_IniFile.GetValueSetI("Generator", "UnderwaterBaseMaxSize", 128);
m_FinishGens.push_back(cFinishGenPtr(new cUnderwaterBaseGen(Seed, GridSize, MaxOffset, MaxDepth, MaxSize, m_BiomeGen)));
m_FinishGens.push_back(std::make_shared<cUnderwaterBaseGen>(Seed, GridSize, MaxOffset, MaxDepth, MaxSize, m_BiomeGen));
}
else if (NoCaseCompare(*itr, "Villages") == 0)
{
@ -598,12 +606,12 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
int MaxSize = a_IniFile.GetValueSetI("Generator", "VillageMaxSize", 128);
int MinDensity = a_IniFile.GetValueSetI("Generator", "VillageMinDensity", 50);
int MaxDensity = a_IniFile.GetValueSetI("Generator", "VillageMaxDensity", 80);
m_FinishGens.push_back(cFinishGenPtr(new cVillageGen(Seed, GridSize, MaxOffset, MaxDepth, MaxSize, MinDensity, MaxDensity, m_BiomeGen, m_HeightGen)));
m_FinishGens.push_back(std::make_shared<cVillageGen>(Seed, GridSize, MaxOffset, MaxDepth, MaxSize, MinDensity, MaxDensity, m_BiomeGen, m_CompositedHeightCache));
}
else if (NoCaseCompare(*itr, "WaterLakes") == 0)
{
int Probability = a_IniFile.GetValueSetI("Generator", "WaterLakesProbability", 25);
m_FinishGens.push_back(cFinishGenPtr(new cStructGenLakes(Seed * 3 + 652, E_BLOCK_STATIONARY_WATER, m_HeightGen, Probability)));
m_FinishGens.push_back(cFinishGenPtr(new cStructGenLakes(Seed * 3 + 652, E_BLOCK_STATIONARY_WATER, m_ShapeGen, Probability)));
}
else if (NoCaseCompare(*itr, "WaterSprings") == 0)
{

View File

@ -26,20 +26,16 @@ See http://forum.mc-server.org/showthread.php?tid=409 for details.
// Forward-declare the shared pointers to subgenerator classes:
class cBiomeGen;
class cTerrainShapeGen;
class cTerrainHeightGen;
class cTerrainCompositionGen;
class cFinishGen;
typedef SharedPtr<cBiomeGen> cBiomeGenPtr;
typedef SharedPtr<cTerrainShapeGen> cTerrainShapeGenPtr;
typedef SharedPtr<cTerrainHeightGen> cTerrainHeightGenPtr;
typedef SharedPtr<cTerrainCompositionGen> cTerrainCompositionGenPtr;
typedef SharedPtr<cFinishGen> cFinishGenPtr;
// fwd: Noise3DGenerator.h
class cNoise3DComposable;
// fwd: DistortedHeightmap.h
class cDistortedHeightmap;
@ -70,28 +66,54 @@ public:
/** The interface that a terrain height generator must implement
A terrain height generator takes chunk coords on input and outputs an array of terrain heights for that chunk.
The output array is sequenced in the same way as the BiomeGen's biome data.
/** The interface that a terrain shape generator must implement
A terrain shape generator takes chunk coords on input and outputs a 3D array of "shape" for that chunk. The shape here
represents the distinction between air and solid; there's no representation of Water since that is added by the
composition geenrator.
The output array is indexed [y + 256 * z + 16 * 256 * x], so that it's fast to later compose a single column of the terrain,
which is the dominant operation following the shape generation.
The generator may request biome information from the underlying BiomeGen, it may even request information for
other chunks than the one it's currently generating (possibly neighbors - for averaging)
other chunks than the one it's currently generating (neighbors - for averaging)
*/
class cTerrainShapeGen
{
public:
virtual ~cTerrainShapeGen() {} // Force a virtual destructor in descendants
/** Generates the shape for the given chunk */
virtual void GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape) = 0;
/** Reads parameters from the ini file, prepares generator for use. */
virtual void InitializeShapeGen(cIniFile & a_IniFile) {}
/** Creates the correct TerrainShapeGen descendant based on the ini file settings and the seed provided.
a_BiomeGen is the underlying biome generator, some shape generators may depend on it providing additional biomes data around the chunk
a_CacheOffByDefault gets set to whether the cache should be disabled by default
Implemented in ShapeGen.cpp!
*/
static cTerrainShapeGenPtr CreateShapeGen(cIniFile & a_IniFile, cBiomeGenPtr a_BiomeGen, int a_Seed, bool & a_CacheOffByDefault);
} ;
/** The interface that is used to query terrain height from the shape generator.
Usually the structure generators require only the final heightmap, and generating the whole shape only to
consume the heightmap is wasteful, so this interface is used instead; it has a cache implemented over it so
that data is retained. */
class cTerrainHeightGen
{
public:
virtual ~cTerrainHeightGen() {} // Force a virtual destructor in descendants
/** Generates heightmap for the given chunk */
/** Retrieves the heightmap for the specified chunk. */
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) = 0;
/** Reads parameters from the ini file, prepares generator for use. */
/** Initializes the generator, reading its parameters from the INI file. */
virtual void InitializeHeightGen(cIniFile & a_IniFile) {}
/** Creates the correct TerrainHeightGen descendant based on the ini file settings and the seed provided.
a_BiomeGen is the underlying biome generator, some height generators may depend on it to generate more biomes
a_CacheOffByDefault gets set to whether the cache should be disabled by default
Implemented in HeiGen.cpp!
*/
/** Creates a cTerrainHeightGen descendant based on the INI file settings. */
static cTerrainHeightGenPtr CreateHeightGen(cIniFile & a_IniFile, cBiomeGenPtr a_BiomeGen, int a_Seed, bool & a_CacheOffByDefault);
} ;
@ -109,16 +131,18 @@ class cTerrainCompositionGen
public:
virtual ~cTerrainCompositionGen() {} // Force a virtual destructor in descendants
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) = 0;
/** Generates the chunk's composition into a_ChunkDesc, using the terrain shape provided in a_Shape.
Is expected to fill a_ChunkDesc's heightmap with the data from a_Shape. */
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) = 0;
/** Reads parameters from the ini file, prepares generator for use. */
virtual void InitializeCompoGen(cIniFile & a_IniFile) {}
/** Creates the correct TerrainCompositionGen descendant based on the ini file settings and the seed provided.
a_BiomeGen is the underlying biome generator, some composition generators may depend on it to generate more biomes
a_HeightGen is the underlying height generator, some composition generators may depend on it providing additional values
a_BiomeGen is the underlying biome generator, some composition generators may depend on it providing additional biomes around the chunk
a_ShapeGen is the underlying shape generator, some composition generators may depend on it providing additional shape around the chunk
*/
static cTerrainCompositionGenPtr CreateCompositionGen(cIniFile & a_IniFile, cBiomeGenPtr a_BiomeGen, cTerrainHeightGen & a_HeightGen, int a_Seed);
static cTerrainCompositionGenPtr CreateCompositionGen(cIniFile & a_IniFile, cBiomeGenPtr a_BiomeGen, cTerrainShapeGenPtr a_ShapeGen, int a_Seed);
} ;
@ -128,7 +152,7 @@ public:
/** The interface that a finisher must implement
Finisher implements changes to the chunk after the rough terrain has been generated.
Examples of finishers are trees, snow, ore, lilypads and others.
Note that a worldgenerator may contain multiple finishers.
Note that a worldgenerator may contain multiple finishers, chained one after another.
Also note that previously we used to distinguish between a structuregen and a finisher; this distinction is
no longer relevant, all structure generators are considered finishers now (#398)
*/
@ -154,23 +178,34 @@ class cComposableGenerator :
public:
cComposableGenerator(cChunkGenerator & a_ChunkGenerator);
// cChunkGenerator::cGenerator overrides:
virtual void Initialize(cIniFile & a_IniFile) override;
virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override;
protected:
// The generation composition:
cBiomeGenPtr m_BiomeGen;
cTerrainHeightGenPtr m_HeightGen;
// The generator's composition:
/** The biome generator. */
cBiomeGenPtr m_BiomeGen;
/** The terrain shape generator. */
cTerrainShapeGenPtr m_ShapeGen;
/** The terrain composition generator. */
cTerrainCompositionGenPtr m_CompositionGen;
cFinishGenList m_FinishGens;
/** The cache for the heights of the composited terrain. */
cTerrainHeightGenPtr m_CompositedHeightCache;
/** The finisher generators, in the order in which they are applied. */
cFinishGenList m_FinishGens;
/** Reads the biome gen settings from the ini and initializes m_BiomeGen accordingly */
/** Reads the BiomeGen settings from the ini and initializes m_BiomeGen accordingly */
void InitBiomeGen(cIniFile & a_IniFile);
/** Reads the HeightGen settings from the ini and initializes m_HeightGen accordingly */
void InitHeightGen(cIniFile & a_IniFile);
/** Reads the ShapeGen settings from the ini and initializes m_ShapeGen accordingly */
void InitShapeGen(cIniFile & a_IniFile);
/** Reads the CompositionGen settings from the ini and initializes m_CompositionGen accordingly */
void InitCompositionGen(cIniFile & a_IniFile);

View File

@ -0,0 +1,49 @@
// CompositedHeiGen.h
// Declares the cCompositedHeiGen class representing a cTerrainHeightGen descendant that calculates heightmap of the composited terrain
// This is used to further cache heightmaps for chunks already generated for finishers that require only heightmap information
#pragma once
#include "ComposableGenerator.h"
class cCompositedHeiGen:
public cTerrainHeightGen
{
public:
cCompositedHeiGen(cTerrainShapeGenPtr a_ShapeGen, cTerrainCompositionGenPtr a_CompositionGen):
m_ShapeGen(a_ShapeGen),
m_CompositionGen(a_CompositionGen)
{
}
// cTerrainheightGen overrides:
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override
{
cChunkDesc::Shape shape;
m_ShapeGen->GenShape(a_ChunkX, a_ChunkZ, shape);
cChunkDesc desc(a_ChunkX, a_ChunkZ);
desc.SetHeightFromShape(shape);
m_CompositionGen->ComposeTerrain(desc, shape);
memcpy(a_HeightMap, desc.GetHeightMap(), sizeof(a_HeightMap));
}
protected:
cTerrainShapeGenPtr m_ShapeGen;
cTerrainCompositionGenPtr m_CompositionGen;
};

View File

@ -14,163 +14,6 @@
////////////////////////////////////////////////////////////////////////////////
// cPattern:
/// This class is used to store a column pattern initialized at runtime,
/// so that the program doesn't need to explicitly set 256 values for each pattern
/// Each pattern has 256 blocks so that there's no need to check pattern bounds when assigning the
/// pattern - there will always be enough pattern left, even for the whole chunk height
class cPattern
{
public:
cPattern(cDistortedHeightmap::sBlockInfo * a_TopBlocks, size_t a_Count)
{
// Copy the pattern into the top:
for (size_t i = 0; i < a_Count; i++)
{
m_Pattern[i] = a_TopBlocks[i];
}
// Fill the rest with stone:
static cDistortedHeightmap::sBlockInfo Stone = {E_BLOCK_STONE, 0};
for (size_t i = a_Count; i < cChunkDef::Height; i++)
{
m_Pattern[i] = Stone;
}
}
const cDistortedHeightmap::sBlockInfo * Get(void) const { return m_Pattern; }
protected:
cDistortedHeightmap::sBlockInfo m_Pattern[cChunkDef::Height];
} ;
////////////////////////////////////////////////////////////////////////////////
// The arrays to use for the top block pattern definitions:
static cDistortedHeightmap::sBlockInfo tbGrass[] =
{
{E_BLOCK_GRASS, 0},
{E_BLOCK_DIRT, E_META_DIRT_NORMAL},
{E_BLOCK_DIRT, E_META_DIRT_NORMAL},
{E_BLOCK_DIRT, E_META_DIRT_NORMAL},
} ;
static cDistortedHeightmap::sBlockInfo tbSand[] =
{
{ E_BLOCK_SAND, 0},
{ E_BLOCK_SAND, 0},
{ E_BLOCK_SAND, 0},
{ E_BLOCK_SANDSTONE, 0},
} ;
static cDistortedHeightmap::sBlockInfo tbDirt[] =
{
{E_BLOCK_DIRT, E_META_DIRT_NORMAL},
{E_BLOCK_DIRT, E_META_DIRT_NORMAL},
{E_BLOCK_DIRT, E_META_DIRT_NORMAL},
{E_BLOCK_DIRT, E_META_DIRT_NORMAL},
} ;
static cDistortedHeightmap::sBlockInfo tbPodzol[] =
{
{E_BLOCK_DIRT, E_META_DIRT_PODZOL},
{E_BLOCK_DIRT, E_META_DIRT_NORMAL},
{E_BLOCK_DIRT, E_META_DIRT_NORMAL},
{E_BLOCK_DIRT, E_META_DIRT_NORMAL},
} ;
static cDistortedHeightmap::sBlockInfo tbGrassLess[] =
{
{E_BLOCK_DIRT, E_META_DIRT_GRASSLESS},
{E_BLOCK_DIRT, E_META_DIRT_NORMAL},
{E_BLOCK_DIRT, E_META_DIRT_NORMAL},
{E_BLOCK_DIRT, E_META_DIRT_NORMAL},
} ;
static cDistortedHeightmap::sBlockInfo tbMycelium[] =
{
{E_BLOCK_MYCELIUM, 0},
{E_BLOCK_DIRT, 0},
{E_BLOCK_DIRT, 0},
{E_BLOCK_DIRT, 0},
} ;
static cDistortedHeightmap::sBlockInfo tbGravel[] =
{
{E_BLOCK_GRAVEL, 0},
{E_BLOCK_GRAVEL, 0},
{E_BLOCK_GRAVEL, 0},
{E_BLOCK_STONE, 0},
} ;
static cDistortedHeightmap::sBlockInfo tbStone[] =
{
{E_BLOCK_STONE, 0},
{E_BLOCK_STONE, 0},
{E_BLOCK_STONE, 0},
{E_BLOCK_STONE, 0},
} ;
////////////////////////////////////////////////////////////////////////////////
// Ocean floor pattern top-block definitions:
static cDistortedHeightmap::sBlockInfo tbOFSand[] =
{
{E_BLOCK_SAND, 0},
{E_BLOCK_SAND, 0},
{E_BLOCK_SAND, 0},
{E_BLOCK_SANDSTONE, 0}
} ;
static cDistortedHeightmap::sBlockInfo tbOFClay[] =
{
{ E_BLOCK_CLAY, 0},
{ E_BLOCK_CLAY, 0},
{ E_BLOCK_SAND, 0},
{ E_BLOCK_SAND, 0},
} ;
static cDistortedHeightmap::sBlockInfo tbOFRedSand[] =
{
{ E_BLOCK_SAND, E_META_SAND_RED},
{ E_BLOCK_SAND, E_META_SAND_RED},
{ E_BLOCK_SAND, E_META_SAND_RED},
{ E_BLOCK_SANDSTONE, 0},
} ;
////////////////////////////////////////////////////////////////////////////////
// Individual patterns to use:
static cPattern patGrass (tbGrass, ARRAYCOUNT(tbGrass));
static cPattern patSand (tbSand, ARRAYCOUNT(tbSand));
static cPattern patDirt (tbDirt, ARRAYCOUNT(tbDirt));
static cPattern patPodzol (tbPodzol, ARRAYCOUNT(tbPodzol));
static cPattern patGrassLess(tbGrassLess, ARRAYCOUNT(tbGrassLess));
static cPattern patMycelium (tbMycelium, ARRAYCOUNT(tbMycelium));
static cPattern patGravel (tbGravel, ARRAYCOUNT(tbGravel));
static cPattern patStone (tbStone, ARRAYCOUNT(tbStone));
static cPattern patOFSand (tbOFSand, ARRAYCOUNT(tbOFSand));
static cPattern patOFClay (tbOFClay, ARRAYCOUNT(tbOFClay));
static cPattern patOFRedSand(tbOFRedSand, ARRAYCOUNT(tbOFRedSand));
////////////////////////////////////////////////////////////////////////////////
// cDistortedHeightmap:
@ -237,7 +80,7 @@ const cDistortedHeightmap::sGenParam cDistortedHeightmap::m_GenParam[256] =
{0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 110 .. 119
{0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, {0.0f, 0.0f}, // 120 .. 128
// Release 1.7 /* biome variants:
// Release 1.7 biome variants:
/* biSunflowerPlains */ { 1.0f, 1.0f}, // 129
/* biDesertM */ { 1.0f, 1.0f}, // 130
/* biExtremeHillsM */ {16.0f, 16.0f}, // 131
@ -279,8 +122,6 @@ const cDistortedHeightmap::sGenParam cDistortedHeightmap::m_GenParam[256] =
cDistortedHeightmap::cDistortedHeightmap(int a_Seed, cBiomeGenPtr a_BiomeGen) :
m_NoiseDistortX(a_Seed + 1000),
m_NoiseDistortZ(a_Seed + 2000),
m_OceanFloorSelect(a_Seed + 3000),
m_MesaFloor(a_Seed + 4000),
m_BiomeGen(a_BiomeGen),
m_UnderlyingHeiGen(new cHeiGenBiomal(a_Seed, a_BiomeGen)),
m_HeightGen(m_UnderlyingHeiGen, 64),
@ -293,8 +134,6 @@ cDistortedHeightmap::cDistortedHeightmap(int a_Seed, cBiomeGenPtr a_BiomeGen) :
m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)1, (NOISE_DATATYPE)0.5);
m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)0.5, (NOISE_DATATYPE)1);
m_NoiseDistortZ.AddOctave((NOISE_DATATYPE)0.25, (NOISE_DATATYPE)2);
InitMesaPattern(a_Seed);
}
@ -309,7 +148,7 @@ void cDistortedHeightmap::Initialize(cIniFile & a_IniFile)
}
// Read the params from the INI file:
m_SeaLevel = a_IniFile.GetValueSetI("Generator", "DistortedHeightmapSeaLevel", 62);
m_SeaLevel = a_IniFile.GetValueSetI("Generator", "SeaLevel", 62);
m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyX", 10);
m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyY", 10);
m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "DistortedHeightmapFrequencyZ", 10);
@ -321,89 +160,6 @@ void cDistortedHeightmap::Initialize(cIniFile & a_IniFile)
void cDistortedHeightmap::InitMesaPattern(int a_Seed)
{
// Stone in the bottom half of the pattern:
for (int i = cChunkDef::Height; i < 2 * cChunkDef::Height; i++)
{
m_MesaPattern[i].BlockMeta = 0;
m_MesaPattern[i].BlockType = E_BLOCK_STONE;
}
// Stained and hardened clay in the top half of the pattern
// In a loop, choose whether to use one or two layers of stained clay, then choose a color and width for each layer
// Separate each group with another layer of hardened clay
cNoise PatternNoise((unsigned)a_Seed);
static NIBBLETYPE AllowedColors[] =
{
E_META_STAINED_CLAY_YELLOW,
E_META_STAINED_CLAY_YELLOW,
E_META_STAINED_CLAY_RED,
E_META_STAINED_CLAY_RED,
E_META_STAINED_CLAY_WHITE,
E_META_STAINED_CLAY_BROWN,
E_META_STAINED_CLAY_BROWN,
E_META_STAINED_CLAY_BROWN,
E_META_STAINED_CLAY_ORANGE,
E_META_STAINED_CLAY_ORANGE,
E_META_STAINED_CLAY_ORANGE,
E_META_STAINED_CLAY_ORANGE,
E_META_STAINED_CLAY_ORANGE,
E_META_STAINED_CLAY_ORANGE,
E_META_STAINED_CLAY_LIGHTGRAY,
} ;
static int LayerSizes[] = // Adjust the chance so that thinner layers occur more commonly
{
1, 1, 1, 1, 1, 1,
2, 2, 2, 2,
3, 3,
} ;
int Idx = cChunkDef::Height - 1;
while (Idx >= 0)
{
// A layer group of 1 - 2 color stained clay:
int Random = PatternNoise.IntNoise1DInt(Idx) / 7;
int NumLayers = (Random % 2) + 1;
Random /= 2;
for (int Lay = 0; Lay < NumLayers; Lay++)
{
int NumBlocks = LayerSizes[(Random % ARRAYCOUNT(LayerSizes))];
NIBBLETYPE Color = AllowedColors[(Random / 4) % ARRAYCOUNT(AllowedColors)];
if (
((NumBlocks == 3) && (NumLayers == 2)) || // In two-layer mode disallow the 3-high layers:
(Color == E_META_STAINED_CLAY_WHITE)) // White stained clay can ever be only 1 block high
{
NumBlocks = 1;
}
NumBlocks = std::min(Idx + 1, NumBlocks); // Limit by Idx so that we don't have to check inside the loop
Random /= 32;
for (int Block = 0; Block < NumBlocks; Block++, Idx--)
{
m_MesaPattern[Idx].BlockMeta = Color;
m_MesaPattern[Idx].BlockType = E_BLOCK_STAINED_CLAY;
} // for Block
} // for Lay
// A layer of hardened clay in between the layer group:
int NumBlocks = (Random % 4) + 1; // All heights the same probability
if ((NumLayers == 2) && (NumBlocks < 4))
{
// For two layers of stained clay, add an extra block of hardened clay:
NumBlocks++;
}
NumBlocks = std::min(Idx + 1, NumBlocks); // Limit by Idx so that we don't have to check inside the loop
for (int Block = 0; Block < NumBlocks; Block++, Idx--)
{
m_MesaPattern[Idx].BlockMeta = 0;
m_MesaPattern[Idx].BlockType = E_BLOCK_HARDENED_CLAY;
} // for Block
} // while (Idx >= 0)
}
void cDistortedHeightmap::PrepareState(int a_ChunkX, int a_ChunkZ)
{
if ((m_CurChunkX == a_ChunkX) && (m_CurChunkZ == a_ChunkZ))
@ -474,23 +230,17 @@ void cDistortedHeightmap::GenerateHeightArray(void)
void cDistortedHeightmap::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
void cDistortedHeightmap::GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape)
{
PrepareState(a_ChunkX, a_ChunkZ);
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
int NoiseArrayIdx = x + 17 * 257 * z;
cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel - 1);
for (int y = cChunkDef::Height - 1; y > m_SeaLevel - 1; y--)
int idx = x + 17 * 257 * z;
for (int y = 0; y < cChunkDef::Height; y++)
{
int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y];
if (y < HeightMapHeight)
{
cChunkDef::SetHeight(a_HeightMap, x, z, y);
break;
}
a_Shape[y + x * 256 + z * 16 * 256] = (y < m_DistortedHeightmap[idx + y * 17]) ? 1 : 0;
} // for y
} // for x
} // for z
@ -500,36 +250,7 @@ void cDistortedHeightmap::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::He
void cDistortedHeightmap::InitializeHeightGen(cIniFile & a_IniFile)
{
Initialize(a_IniFile);
}
void cDistortedHeightmap::ComposeTerrain(cChunkDesc & a_ChunkDesc)
{
// Prepare the internal state for generating this chunk:
PrepareState(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ());
// Compose:
a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
ComposeColumn(a_ChunkDesc, x, z);
} // for x
} // for z
}
void cDistortedHeightmap::InitializeCompoGen(cIniFile & a_IniFile)
void cDistortedHeightmap::InitializeShapeGen(cIniFile & a_IniFile)
{
Initialize(a_IniFile);
}
@ -654,275 +375,3 @@ void cDistortedHeightmap::GetDistortAmpsAt(BiomeNeighbors & a_Neighbors, int a_R
void cDistortedHeightmap::ComposeColumn(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ)
{
// Frequencies for the podzol floor selecting noise:
const NOISE_DATATYPE FrequencyX = 8;
const NOISE_DATATYPE FrequencyZ = 8;
EMCSBiome Biome = a_ChunkDesc.GetBiome(a_RelX, a_RelZ);
switch (Biome)
{
case biOcean:
case biPlains:
case biForest:
case biTaiga:
case biSwampland:
case biRiver:
case biFrozenOcean:
case biFrozenRiver:
case biIcePlains:
case biIceMountains:
case biForestHills:
case biTaigaHills:
case biExtremeHillsEdge:
case biExtremeHillsPlus:
case biExtremeHills:
case biJungle:
case biJungleHills:
case biJungleEdge:
case biDeepOcean:
case biStoneBeach:
case biColdBeach:
case biBirchForest:
case biBirchForestHills:
case biRoofedForest:
case biColdTaiga:
case biColdTaigaHills:
case biSavanna:
case biSavannaPlateau:
case biSunflowerPlains:
case biFlowerForest:
case biTaigaM:
case biSwamplandM:
case biIcePlainsSpikes:
case biJungleM:
case biJungleEdgeM:
case biBirchForestM:
case biBirchForestHillsM:
case biRoofedForestM:
case biColdTaigaM:
case biSavannaM:
case biSavannaPlateauM:
{
FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, patGrass.Get());
return;
}
case biMegaTaiga:
case biMegaTaigaHills:
case biMegaSpruceTaiga:
case biMegaSpruceTaigaHills:
{
// Select the pattern to use - podzol, grass or grassless dirt:
NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + a_RelX)) / FrequencyX;
NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + a_RelZ)) / FrequencyZ;
NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY);
const sBlockInfo * Pattern = (Val < -0.9) ? patGrassLess.Get() : ((Val > 0) ? patPodzol.Get() : patGrass.Get());
FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, Pattern);
return;
}
case biDesertHills:
case biDesert:
case biDesertM:
case biBeach:
{
FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, patSand.Get());
return;
}
case biMushroomIsland:
case biMushroomShore:
{
FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, patMycelium.Get());
return;
}
case biMesa:
case biMesaPlateauF:
case biMesaPlateau:
case biMesaBryce:
case biMesaPlateauFM:
case biMesaPlateauM:
{
// Mesa biomes need special handling, because they don't follow the usual "4 blocks from top pattern",
// instead, they provide a "from bottom" pattern with varying base height,
// usually 4 blocks below the ocean level
FillColumnMesa(a_ChunkDesc, a_RelX, a_RelZ);
return;
}
case biExtremeHillsPlusM:
case biExtremeHillsM:
{
// Select the pattern to use - gravel, stone or grass:
NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + a_RelX)) / FrequencyX;
NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + a_RelZ)) / FrequencyZ;
NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY);
const sBlockInfo * Pattern = (Val < 0.0) ? patStone.Get() : patGrass.Get();
FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, Pattern);
return;
}
default:
{
ASSERT(!"Unhandled biome");
return;
}
} // switch (Biome)
}
void cDistortedHeightmap::FillColumnPattern(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ, const sBlockInfo * a_Pattern)
{
int NoiseArrayIdx = a_RelX + 17 * 257 * a_RelZ;
bool HasHadWater = false;
int PatternIdx = 0;
for (int y = a_ChunkDesc.GetHeight(a_RelX, a_RelZ); y > 0; y--)
{
int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y];
if (y < HeightMapHeight)
{
// "ground" part, use the pattern:
a_ChunkDesc.SetBlockTypeMeta(a_RelX, y, a_RelZ, a_Pattern[PatternIdx].BlockType, a_Pattern[PatternIdx].BlockMeta);
PatternIdx++;
continue;
}
// "air" or "water" part:
// Reset the pattern index to zero, so that the pattern is repeated from the top again:
PatternIdx = 0;
if (y >= m_SeaLevel)
{
// "air" part, do nothing
continue;
}
a_ChunkDesc.SetBlockType(a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER);
if (HasHadWater)
{
continue;
}
// Select the ocean-floor pattern to use:
a_Pattern = a_ChunkDesc.GetBiome(a_RelX, a_RelZ) == biDeepOcean ? patGravel.Get() : ChooseOceanFloorPattern(a_RelX, a_RelZ);
HasHadWater = true;
} // for y
a_ChunkDesc.SetBlockType(a_RelX, 0, a_RelZ, E_BLOCK_BEDROCK);
}
void cDistortedHeightmap::FillColumnMesa(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ)
{
// Frequencies for the clay floor noise:
const NOISE_DATATYPE FrequencyX = 50;
const NOISE_DATATYPE FrequencyZ = 50;
int Top = a_ChunkDesc.GetHeight(a_RelX, a_RelZ);
if (Top < m_SeaLevel)
{
// The terrain is below sealevel, handle as regular ocean:
FillColumnPattern(a_ChunkDesc, a_RelX, a_RelZ, patOFRedSand.Get());
return;
}
NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + a_RelX)) / FrequencyX;
NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + a_RelZ)) / FrequencyZ;
int ClayFloor = m_SeaLevel - 6 + (int)(4.f * m_MesaFloor.CubicNoise2D(NoiseX, NoiseY));
if (ClayFloor >= Top)
{
ClayFloor = Top - 1;
}
if (Top - m_SeaLevel < 5)
{
// Simple case: top is red sand, then hardened clay down to ClayFloor, then stone:
a_ChunkDesc.SetBlockTypeMeta(a_RelX, Top, a_RelZ, E_BLOCK_SAND, E_META_SAND_RED);
for (int y = Top - 1; y >= ClayFloor; y--)
{
a_ChunkDesc.SetBlockType(a_RelX, y, a_RelZ, E_BLOCK_HARDENED_CLAY);
}
for (int y = ClayFloor - 1; y > 0; y--)
{
a_ChunkDesc.SetBlockType(a_RelX, y, a_RelZ, E_BLOCK_STONE);
}
a_ChunkDesc.SetBlockType(a_RelX, 0, a_RelZ, E_BLOCK_BEDROCK);
return;
}
// Difficult case: use the mesa pattern and watch for overhangs:
int NoiseArrayIdx = a_RelX + 17 * 257 * a_RelZ;
int PatternIdx = cChunkDef::Height - (Top - ClayFloor); // We want the block at index ClayFloor to be pattern's 256th block (first stone)
const sBlockInfo * Pattern = m_MesaPattern;
bool HasHadWater = false;
for (int y = Top; y > 0; y--)
{
int HeightMapHeight = (int)m_DistortedHeightmap[NoiseArrayIdx + 17 * y];
if (y < HeightMapHeight)
{
// "ground" part, use the pattern:
a_ChunkDesc.SetBlockTypeMeta(a_RelX, y, a_RelZ, Pattern[PatternIdx].BlockType, Pattern[PatternIdx].BlockMeta);
PatternIdx++;
continue;
}
if (y >= m_SeaLevel)
{
// "air" part, do nothing
continue;
}
// "water" part, fill with water and choose new pattern for ocean floor, if not chosen already:
PatternIdx = 0;
a_ChunkDesc.SetBlockType(a_RelX, y, a_RelZ, E_BLOCK_STATIONARY_WATER);
if (HasHadWater)
{
continue;
}
// Select the ocean-floor pattern to use:
Pattern = ChooseOceanFloorPattern(a_RelX, a_RelZ);
HasHadWater = true;
} // for y
a_ChunkDesc.SetBlockType(a_RelX, 0, a_RelZ, E_BLOCK_BEDROCK);
}
const cDistortedHeightmap::sBlockInfo * cDistortedHeightmap::ChooseOceanFloorPattern(int a_RelX, int a_RelZ)
{
// Frequencies for the ocean floor selecting noise:
const NOISE_DATATYPE FrequencyX = 3;
const NOISE_DATATYPE FrequencyZ = 3;
// Select the ocean-floor pattern to use:
NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(m_CurChunkX * cChunkDef::Width + a_RelX)) / FrequencyX;
NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(m_CurChunkZ * cChunkDef::Width + a_RelZ)) / FrequencyZ;
NOISE_DATATYPE Val = m_OceanFloorSelect.CubicNoise2D(NoiseX, NoiseY);
if (Val < -0.95)
{
return patOFClay.Get();
}
else if (Val < 0)
{
return patOFSand.Get();
}
else
{
return patDirt.Get();
}
}

View File

@ -11,7 +11,6 @@
#include "ComposableGenerator.h"
#include "HeiGen.h"
#include "../Noise.h"
@ -24,17 +23,9 @@
class cDistortedHeightmap :
public cTerrainHeightGen,
public cTerrainCompositionGen
public cTerrainShapeGen
{
public:
/// Structure used for storing block patterns for columns
struct sBlockInfo
{
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
} ;
cDistortedHeightmap(int a_Seed, cBiomeGenPtr a_BiomeGen);
protected:
@ -52,8 +43,6 @@ protected:
cPerlinNoise m_NoiseDistortX;
cPerlinNoise m_NoiseDistortZ;
cNoise m_OceanFloorSelect; ///< Used for selecting between dirt and sand on the ocean floor
cNoise m_MesaFloor; ///< Used for the floor of the clay blocks in mesa biomes
int m_SeaLevel;
NOISE_DATATYPE m_FrequencyX;
@ -71,9 +60,9 @@ protected:
cTerrainHeightGenPtr m_UnderlyingHeiGen;
/** Cache for m_UnderlyingHeiGen. */
cHeiGenCache m_HeightGen;
cHeiGenCache m_HeightGen;
/// Heightmap for the current chunk, before distortion (from m_HeightGen). Used for optimization.
/** Heightmap for the current chunk, before distortion (from m_HeightGen). Used for optimization. */
cChunkDef::HeightMap m_CurChunkHeights;
// Per-biome terrain generator parameters:
@ -88,54 +77,30 @@ protected:
NOISE_DATATYPE m_DistortAmpX[DIM_X * DIM_Z];
NOISE_DATATYPE m_DistortAmpZ[DIM_X * DIM_Z];
/// True if Initialize() has been called. Used to initialize-once even with multiple init entrypoints (HeiGen / CompoGen)
/** True if Initialize() has been called. Used to initialize-once even with multiple init entrypoints (HeiGen / CompoGen). */
bool m_IsInitialized;
/// The vertical pattern to be used for mesa biomes. Seed-dependant.
/// One Height of pattern and one Height of stone to avoid checking pattern dimensions
sBlockInfo m_MesaPattern[2 * cChunkDef::Height];
/// Initializes m_MesaPattern with a reasonable pattern of stained clay / hardened clay, based on the seed
void InitMesaPattern(int a_Seed);
/// Unless the LastChunk coords are equal to coords given, prepares the internal state (noise arrays, heightmap)
/** Unless the LastChunk coords are equal to coords given, prepares the internal state (noise arrays, heightmap). */
void PrepareState(int a_ChunkX, int a_ChunkZ);
/// Generates the m_DistortedHeightmap array for the current chunk
/** Generates the m_DistortedHeightmap array for the current chunk. */
void GenerateHeightArray(void);
/// Calculates the heightmap value (before distortion) at the specified (floating-point) coords
/** Calculates the heightmap value (before distortion) at the specified (floating-point) coords. */
int GetHeightmapAt(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Z);
/// Updates m_DistortAmpX/Z[] based on m_CurChunkX and m_CurChunkZ
/** Updates m_DistortAmpX/Z[] based on m_CurChunkX and m_CurChunkZ. */
void UpdateDistortAmps(void);
/// Calculates the X and Z distortion amplitudes based on the neighbors' biomes
/** Calculates the X and Z distortion amplitudes based on the neighbors' biomes. */
void GetDistortAmpsAt(BiomeNeighbors & a_Neighbors, int a_RelX, int a_RelZ, NOISE_DATATYPE & a_DistortAmpX, NOISE_DATATYPE & a_DistortAmpZ);
/// Reads the settings from the ini file. Skips reading if already initialized
/** Reads the settings from the ini file. Skips reading if already initialized. */
void Initialize(cIniFile & a_IniFile);
/// Composes a single column in a_ChunkDesc. Chooses what to do based on the biome in that column
void ComposeColumn(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ);
/// Fills the specified column with the specified pattern; restarts the pattern when air is reached,
/// switches to ocean floor pattern if ocean is reached. Always adds bedrock at the very bottom.
void FillColumnPattern(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ, const sBlockInfo * a_Pattern);
/// Fills the specified column with mesa pattern, based on the column height
void FillColumnMesa(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelZ);
/// Returns the pattern to use for an ocean floor in the specified column
const sBlockInfo * ChooseOceanFloorPattern(int a_RelX, int a_RelZ);
// cTerrainHeightGen overrides:
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
// cTerrainCompositionGen overrides:
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
// cTerrainShapeGen overrides:
virtual void GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape) override;
virtual void InitializeShapeGen(cIniFile & a_IniFile) override;
} ;

View File

@ -78,7 +78,8 @@ protected:
/** Decodes the position index along the room walls into a proper 2D position for a chest. */
/** Decodes the position index along the room walls into a proper 2D position for a chest.
The Y coord of the returned vector specifies the chest's meta value*/
Vector3i DecodeChestCoords(int a_PosIdx, int a_SizeX, int a_SizeZ)
{
if (a_PosIdx < a_SizeX)
@ -258,9 +259,9 @@ protected:
////////////////////////////////////////////////////////////////////////////////
// cDungeonRoomsFinisher:
cDungeonRoomsFinisher::cDungeonRoomsFinisher(cTerrainHeightGenPtr a_HeightGen, int a_Seed, int a_GridSize, int a_MaxSize, int a_MinSize, const AString & a_HeightDistrib) :
cDungeonRoomsFinisher::cDungeonRoomsFinisher(cTerrainShapeGenPtr a_ShapeGen, int a_Seed, int a_GridSize, int a_MaxSize, int a_MinSize, const AString & a_HeightDistrib) :
super(a_Seed + 100, a_GridSize, a_GridSize, a_GridSize, a_GridSize, a_MaxSize, a_MaxSize, 1024),
m_HeightGen(a_HeightGen),
m_ShapeGen(a_ShapeGen),
m_MaxHalfSize((a_MaxSize + 1) / 2),
m_MinHalfSize((a_MinSize + 1) / 2),
m_HeightProbability(cChunkDef::Height)
@ -293,13 +294,21 @@ cDungeonRoomsFinisher::cStructurePtr cDungeonRoomsFinisher::CreateStructure(int
int ChunkX, ChunkZ;
int RelX = a_OriginX, RelY = 0, RelZ = a_OriginZ;
cChunkDef::AbsoluteToRelative(RelX, RelY, RelZ, ChunkX, ChunkZ);
cChunkDef::HeightMap HeightMap;
m_HeightGen->GenHeightMap(ChunkX, ChunkZ, HeightMap);
int Height = cChunkDef::GetHeight(HeightMap, RelX, RelZ); // Max room height at {a_OriginX, a_OriginZ}
Height = Clamp(m_HeightProbability.MapValue(rnd % m_HeightProbability.GetSum()), 10, Height - 5);
cChunkDesc::Shape shape;
m_ShapeGen->GenShape(ChunkX, ChunkZ, shape);
int height = 0;
int idx = RelX * 256 + RelZ * 16 * 256;
for (int y = 6; y < cChunkDef::Height; y++)
{
if (shape[idx + y] != 0)
{
continue;
}
height = Clamp(m_HeightProbability.MapValue(rnd % m_HeightProbability.GetSum()), 10, y - 5);
}
// Create the dungeon room descriptor:
return cStructurePtr(new cDungeonRoom(a_GridX, a_GridZ, a_OriginX, a_OriginZ, HalfSizeX, HalfSizeZ, Height, m_Noise));
return cStructurePtr(new cDungeonRoom(a_GridX, a_GridZ, a_OriginX, a_OriginZ, HalfSizeX, HalfSizeZ, height, m_Noise));
}

View File

@ -23,15 +23,15 @@ class cDungeonRoomsFinisher :
public:
/** Creates a new dungeon room finisher.
a_HeightGen is the underlying height generator, so that the rooms can always be placed under the terrain.
a_ShapeGen is the underlying terrain shape generator, so that the rooms can always be placed under the terrain.
a_MaxSize and a_MinSize are the maximum and minimum sizes of the room's internal (air) area, in blocks across.
a_HeightDistrib is the string defining the height distribution for the rooms (cProbabDistrib format). */
cDungeonRoomsFinisher(cTerrainHeightGenPtr a_HeightGen, int a_Seed, int a_GridSize, int a_MaxSize, int a_MinSize, const AString & a_HeightDistrib);
cDungeonRoomsFinisher(cTerrainShapeGenPtr a_ShapeGen, int a_Seed, int a_GridSize, int a_MaxSize, int a_MinSize, const AString & a_HeightDistrib);
protected:
/** The height gen that is used for limiting the rooms' Y coords */
cTerrainHeightGenPtr m_HeightGen;
/** The shape gen that is used for limiting the rooms' Y coords */
cTerrainShapeGenPtr m_ShapeGen;
/** Maximum half-size (from center to wall) of the dungeon room's inner (air) area. Default is 3 (vanilla). */
int m_MaxHalfSize;

View File

@ -147,13 +147,14 @@ bool cEndGen::IsChunkOutsideRange(int a_ChunkX, int a_ChunkZ)
void cEndGen::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
void cEndGen::GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape)
{
// If the chunk is outside out range, fill the shape with zeroes:
if (IsChunkOutsideRange(a_ChunkX, a_ChunkZ))
{
for (size_t i = 0; i < ARRAYCOUNT(a_HeightMap); i++)
for (size_t i = 0; i < ARRAYCOUNT(a_Shape); i++)
{
a_HeightMap[i] = 0;
a_Shape[i] = 0;
}
return;
}
@ -165,15 +166,14 @@ void cEndGen::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_
{
for (int x = 0; x < cChunkDef::Width; x++)
{
cChunkDef::SetHeight(a_HeightMap, x, z, MaxY);
for (int y = MaxY; y > 0; y--)
for (int y = 0; y < MaxY; y++)
{
if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= 0)
{
cChunkDef::SetHeight(a_HeightMap, x, z, y);
break;
}
} // for y
a_Shape[(x + 16 * z) * 256 + y] = (m_NoiseArray[y * 17 * 17 + z * 17 + z] > 0) ? 1 : 0;
}
for (int y = MaxY; y < cChunkDef::Height; y++)
{
a_Shape[(x + 16 * z) * 256 + y] = 0;
}
} // for x
} // for z
}
@ -182,30 +182,18 @@ void cEndGen::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_
void cEndGen::ComposeTerrain(cChunkDesc & a_ChunkDesc)
void cEndGen::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape)
{
if (IsChunkOutsideRange(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()))
{
a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
return;
}
PrepareState(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ());
int MaxY = std::min((int)(1.75 * m_IslandSizeY + 1), cChunkDef::Height - 1);
a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
for (int y = MaxY; y > 0; y--)
for (int y = 0; y < cChunkDef::Height; y++)
{
if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= 0)
if (a_Shape[(x + 16 * z) * 256 + y] != 0)
{
a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_END_STONE, 0);
}
else
{
a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_AIR, 0);
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_END_STONE);
}
} // for y
} // for x

View File

@ -10,14 +10,14 @@
#pragma once
#include "ComposableGenerator.h"
#include "../Noise.h"
#include "../Noise/Noise.h"
class cEndGen :
public cTerrainHeightGen,
public cTerrainShapeGen,
public cTerrainCompositionGen
{
public:
@ -59,10 +59,10 @@ protected:
/// Returns true if the chunk is outside of the island's dimensions
bool IsChunkOutsideRange(int a_ChunkX, int a_ChunkZ);
// cTerrainHeightGen overrides:
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
// cTerrainShapeGen overrides:
virtual void GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape) override;
// cTerrainCompositionGen overrides:
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc::Shape & a_Shape) override;
virtual void InitializeCompoGen(cIniFile & a_IniFile) override;
} ;

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"
@ -412,7 +411,7 @@ void cFinishGenSnow::GenFinish(cChunkDesc & a_ChunkDesc)
case biFrozenOcean:
{
int Height = a_ChunkDesc.GetHeight(x, z);
if (cBlockInfo::IsSnowable(a_ChunkDesc.GetBlockType(x, Height, z)))
if (cBlockInfo::IsSnowable(a_ChunkDesc.GetBlockType(x, Height, z)) && (Height < cChunkDef::Height - 1))
{
a_ChunkDesc.SetBlockType(x, Height + 1, z, E_BLOCK_SNOW);
a_ChunkDesc.SetHeight(x, z, Height + 1);

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,6 @@
////////////////////////////////////////////////////////////////////////////////
// cHeiGenFlat:
@ -133,15 +132,6 @@ void cHeiGenCache::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap
void cHeiGenCache::InitializeHeightGen(cIniFile & a_IniFile)
{
m_HeiGenToCache->InitializeHeightGen(a_IniFile);
}
bool cHeiGenCache::GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height)
{
for (int i = 0; i < m_CacheSize; i++)
@ -159,6 +149,51 @@ bool cHeiGenCache::GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_Rel
////////////////////////////////////////////////////////////////////////////////
// cHeiGenMultiCache:
cHeiGenMultiCache::cHeiGenMultiCache(cTerrainHeightGenPtr a_HeiGenToCache, size_t a_SubCacheSize, size_t a_NumSubCaches):
m_NumSubCaches(a_NumSubCaches)
{
// Create the individual sub-caches:
m_SubCaches.reserve(a_NumSubCaches);
for (size_t i = 0; i < a_NumSubCaches; i++)
{
m_SubCaches.push_back(std::make_shared<cHeiGenCache>(a_HeiGenToCache, a_SubCacheSize));
}
}
void cHeiGenMultiCache::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
{
// Get the subcache responsible for this chunk:
const size_t cacheIdx = ((size_t)a_ChunkX + m_CoeffZ * (size_t)a_ChunkZ) % m_NumSubCaches;
// Ask the subcache:
m_SubCaches[cacheIdx]->GenHeightMap(a_ChunkX, a_ChunkZ, a_HeightMap);
}
bool cHeiGenMultiCache::GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height)
{
// Get the subcache responsible for this chunk:
const size_t cacheIdx = ((size_t)a_ChunkX + m_CoeffZ * (size_t)a_ChunkZ) % m_NumSubCaches;
// Ask the subcache:
return m_SubCaches[cacheIdx]->GetHeightAt(a_ChunkX, a_ChunkZ, a_RelX, a_RelZ, a_Height);
}
////////////////////////////////////////////////////////////////////////////////
// cHeiGenClassic:
@ -750,43 +785,51 @@ cTerrainHeightGenPtr cTerrainHeightGen::CreateHeightGen(cIniFile & a_IniFile, cB
}
a_CacheOffByDefault = false;
cTerrainHeightGen * res = nullptr;
if (NoCaseCompare(HeightGenName, "flat") == 0)
cTerrainHeightGenPtr res;
if (NoCaseCompare(HeightGenName, "Flat") == 0)
{
res = new cHeiGenFlat;
res = std::make_shared<cHeiGenFlat>();
a_CacheOffByDefault = true; // We're generating faster than a cache would retrieve data
}
else if (NoCaseCompare(HeightGenName, "classic") == 0)
{
res = new cHeiGenClassic(a_Seed);
res = std::make_shared<cHeiGenClassic>(a_Seed);
}
else if (NoCaseCompare(HeightGenName, "DistortedHeightmap") == 0)
{
res = new cDistortedHeightmap(a_Seed, a_BiomeGen);
// Not a heightmap-based generator, but it used to be accessible via HeightGen, so we need to skip making the default out of it
// Return an empty pointer, the caller will create the proper generator:
return cTerrainHeightGenPtr();
}
else if (NoCaseCompare(HeightGenName, "End") == 0)
{
res = new cEndGen(a_Seed);
// Not a heightmap-based generator, but it used to be accessible via HeightGen, so we need to skip making the default out of it
// Return an empty pointer, the caller will create the proper generator:
return cTerrainHeightGenPtr();
}
else if (NoCaseCompare(HeightGenName, "MinMax") == 0)
{
res = new cHeiGenMinMax(a_Seed, a_BiomeGen);
res = std::make_shared<cHeiGenMinMax>(a_Seed, a_BiomeGen);
}
else if (NoCaseCompare(HeightGenName, "Mountains") == 0)
{
res = new cHeiGenMountains(a_Seed);
res = std::make_shared<cHeiGenMountains>(a_Seed);
}
else if (NoCaseCompare(HeightGenName, "BiomalNoise3D") == 0)
{
res = new cBiomalNoise3DComposable(a_Seed, a_BiomeGen);
// Not a heightmap-based generator, but it used to be accessible via HeightGen, so we need to skip making the default out of it
// Return an empty pointer, the caller will create the proper generator:
return cTerrainHeightGenPtr();
}
else if (NoCaseCompare(HeightGenName, "Noise3D") == 0)
{
res = new cNoise3DComposable(a_Seed);
// Not a heightmap-based generator, but it used to be accessible via HeightGen, so we need to skip making the default out of it
// Return an empty pointer, the caller will create the proper generator:
return cTerrainHeightGenPtr();
}
else if (NoCaseCompare(HeightGenName, "biomal") == 0)
else if (NoCaseCompare(HeightGenName, "Biomal") == 0)
{
res = new cHeiGenBiomal(a_Seed, a_BiomeGen);
res = std::make_shared<cHeiGenBiomal>(a_Seed, a_BiomeGen);
/*
// Performance-testing:
@ -805,7 +848,6 @@ cTerrainHeightGenPtr cTerrainHeightGen::CreateHeightGen(cIniFile & a_IniFile, cB
{
// No match found, force-set the default and retry
LOGWARN("Unknown HeightGen \"%s\", using \"Biomal\" instead.", HeightGenName.c_str());
a_IniFile.DeleteValue("Generator", "HeightGen");
a_IniFile.SetValue("Generator", "HeightGen", "Biomal");
return CreateHeightGen(a_IniFile, a_BiomeGen, a_Seed, a_CacheOffByDefault);
}
@ -813,7 +855,7 @@ cTerrainHeightGenPtr cTerrainHeightGen::CreateHeightGen(cIniFile & a_IniFile, cB
// Read the settings:
res->InitializeHeightGen(a_IniFile);
return cTerrainHeightGenPtr(res);
return res;
}

View File

@ -2,10 +2,12 @@
// HeiGen.h
/*
Interfaces to the various height generators:
Interfaces to the various height-based terrain shape generators:
- cHeiGenFlat
- cHeiGenClassic
- cHeiGenBiomal
Also implements the heightmap cache
*/
@ -15,7 +17,79 @@ Interfaces to the various height generators:
#pragma once
#include "ComposableGenerator.h"
#include "../Noise.h"
#include "../Noise/Noise.h"
/** A simple cache that stores N most recently generated chunks' heightmaps; N being settable upon creation */
class cHeiGenCache :
public cTerrainHeightGen
{
public:
cHeiGenCache(cTerrainHeightGenPtr a_HeiGenToCache, int a_CacheSize);
~cHeiGenCache();
// cTerrainHeightGen overrides:
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
/** Retrieves height at the specified point in the cache, returns true if found, false if not found */
bool GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height);
protected:
struct sCacheData
{
int m_ChunkX;
int m_ChunkZ;
cChunkDef::HeightMap m_HeightMap;
} ;
/** The terrain height generator that is being cached. */
cTerrainHeightGenPtr m_HeiGenToCache;
// To avoid moving large amounts of data for the MRU behavior, we MRU-ize indices to an array of the actual data
int m_CacheSize;
int * m_CacheOrder; // MRU-ized order, indices into m_CacheData array
sCacheData * m_CacheData; // m_CacheData[m_CacheOrder[0]] is the most recently used
// Cache statistics
int m_NumHits;
int m_NumMisses;
int m_TotalChain; // Number of cache items walked to get to a hit (only added for hits)
} ;
/** Caches heightmaps in multiple underlying caches to improve the distribution and lower the chain length. */
class cHeiGenMultiCache:
public cTerrainHeightGen
{
public:
cHeiGenMultiCache(cTerrainHeightGenPtr a_HeightGenToCache, size_t a_SubCacheSize, size_t a_NumSubCaches);
// cTerrainHeightGen overrides:
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
/** Retrieves height at the specified point in the cache, returns true if found, false if not found */
bool GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height);
protected:
typedef SharedPtr<cHeiGenCache> cHeiGenCachePtr;
typedef std::vector<cHeiGenCachePtr> cHeiGenCachePtrs;
/** The coefficient used to turn Z coords into index (x + Coeff * z). */
static const size_t m_CoeffZ = 5;
/** Number of sub-caches, pulled out of m_SubCaches.size() for performance reasons. */
size_t m_NumSubCaches;
/** The individual sub-caches. */
cHeiGenCachePtrs m_SubCaches;
};
@ -40,47 +114,6 @@ protected:
/// A simple cache that stores N most recently generated chunks' heightmaps; N being settable upon creation
class cHeiGenCache :
public cTerrainHeightGen
{
public:
cHeiGenCache(cTerrainHeightGenPtr a_HeiGenToCache, int a_CacheSize);
~cHeiGenCache();
// cTerrainHeightGen overrides:
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
/// Retrieves height at the specified point in the cache, returns true if found, false if not found
bool GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelZ, HEIGHTTYPE & a_Height);
protected:
cTerrainHeightGenPtr m_HeiGenToCache;
struct sCacheData
{
int m_ChunkX;
int m_ChunkZ;
cChunkDef::HeightMap m_HeightMap;
} ;
// To avoid moving large amounts of data for the MRU behavior, we MRU-ize indices to an array of the actual data
int m_CacheSize;
int * m_CacheOrder; // MRU-ized order, indices into m_CacheData array
sCacheData * m_CacheData; // m_CacheData[m_CacheOrder[0]] is the most recently used
// Cache statistics
int m_NumHits;
int m_NumMisses;
int m_TotalChain; // Number of cache items walked to get to a hit (only added for hits)
} ;
class cHeiGenClassic :
public cTerrainHeightGen
{
@ -138,6 +171,10 @@ public:
{
}
// cTerrainHeightGen overrides:
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
protected:
typedef cChunkDef::BiomeMap BiomeNeighbors[3][3];
@ -155,9 +192,6 @@ protected:
} ;
static const sGenParam m_GenParam[256];
// cTerrainHeightGen overrides:
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
virtual void InitializeHeightGen(cIniFile & a_IniFile) override;
NOISE_DATATYPE GetHeightAt(int a_RelX, int a_RelZ, int a_ChunkX, int a_ChunkZ, const BiomeNeighbors & a_BiomeNeighbors);
} ;

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,72 +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);
#if 0
// DEBUG: Test the noise generation:
// NOTE: In order to be able to run MCS with this code, you need to increase the default thread stack size
// In MSVC, it is done in Project Settings -> Configuration Properties -> Linker -> System, set Stack reserve size to at least 64M
m_SeaLevel = 62;
m_HeightAmplification = 0;
m_MidPoint = 75;
m_FrequencyX = 4;
m_FrequencyY = 4;
m_FrequencyZ = 4;
m_AirThreshold = 0.5;
const int NumChunks = 4;
NOISE_DATATYPE Noise[NumChunks][cChunkDef::Width * cChunkDef::Width * cChunkDef::Height];
for (int x = 0; x < NumChunks; x++)
{
GenerateNoiseArray(x, 5, Noise[x]);
}
// Save in XY cuts:
cFile f1;
if (f1.Open("Test_XY.grab", cFile::fmWrite))
{
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int y = 0; y < cChunkDef::Height; y++)
{
for (int i = 0; i < NumChunks; i++)
{
int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height;
unsigned char buf[cChunkDef::Width];
for (int x = 0; x < cChunkDef::Width; x++)
{
buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++]))));
}
f1.Write(buf, cChunkDef::Width);
}
} // for y
} // for z
} // if (XY file open)
cFile f2;
if (f2.Open("Test_XZ.grab", cFile::fmWrite))
{
for (int y = 0; y < cChunkDef::Height; y++)
{
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int i = 0; i < NumChunks; i++)
{
int idx = y * cChunkDef::Width + z * cChunkDef::Width * cChunkDef::Height;
unsigned char buf[cChunkDef::Width];
for (int x = 0; x < cChunkDef::Width; x++)
{
buf[x] = (unsigned char)(std::min(256, std::max(0, (int)(128 + 32 * Noise[i][idx++]))));
}
f2.Write(buf, cChunkDef::Width);
}
} // for z
} // for y
} // if (XZ file open)
#endif // 0
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);
}
@ -182,9 +184,9 @@ cNoise3DGenerator::~cNoise3DGenerator()
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_SeaLevel = a_IniFile.GetValueSetI("Generator", "SeaLevel", 62);
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 +251,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 +264,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];
}
}
}
@ -390,7 +392,6 @@ void cNoise3DComposable::Initialize(cIniFile & a_IniFile)
{
// Params:
// The defaults generate extreme hills terrain
m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62);
m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0.045);
m_MidPoint = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DMidPoint", 75);
m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DFrequencyX", 40);
@ -458,110 +459,60 @@ void cNoise3DComposable::GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ)
NOISE_DATATYPE BaseNoise[5 * 5];
NOISE_DATATYPE BlockX = static_cast<NOISE_DATATYPE>(a_ChunkX * cChunkDef::Width);
NOISE_DATATYPE BlockZ = static_cast<NOISE_DATATYPE>(a_ChunkZ * cChunkDef::Width);
// Note that we have to swap the coords, because noise generator uses [x + SizeX * y + SizeX * SizeY * z] ordering and we want "BlockY" to be "z":
m_ChoiceNoise.Generate3D (ChoiceNoise, 5, 5, 33, BlockX / m_ChoiceFrequencyX, (BlockX + 17) / m_ChoiceFrequencyX, BlockZ / m_ChoiceFrequencyZ, (BlockZ + 17) / m_ChoiceFrequencyZ, 0, 257 / m_ChoiceFrequencyY, Workspace);
m_DensityNoiseA.Generate3D(DensityNoiseA, 5, 5, 33, BlockX / m_FrequencyX, (BlockX + 17) / m_FrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, 0, 257 / m_FrequencyY, Workspace);
m_DensityNoiseB.Generate3D(DensityNoiseB, 5, 5, 33, BlockX / m_FrequencyX, (BlockX + 17) / m_FrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, 0, 257 / m_FrequencyY, Workspace);
// Note that we have to swap the X and Y coords, because noise generator uses [x + SizeX * y + SizeX * SizeY * z] ordering and we want "BlockY" to be "x":
m_ChoiceNoise.Generate3D (ChoiceNoise, 33, 5, 5, 0, 257 / m_ChoiceFrequencyY, BlockX / m_ChoiceFrequencyX, (BlockX + 17) / m_ChoiceFrequencyX, BlockZ / m_ChoiceFrequencyZ, (BlockZ + 17) / m_ChoiceFrequencyZ, Workspace);
m_DensityNoiseA.Generate3D(DensityNoiseA, 33, 5, 5, 0, 257 / m_FrequencyY, BlockX / m_FrequencyX, (BlockX + 17) / m_FrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, Workspace);
m_DensityNoiseB.Generate3D(DensityNoiseB, 33, 5, 5, 0, 257 / m_FrequencyY, BlockX / m_FrequencyX, (BlockX + 17) / m_FrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, Workspace);
m_BaseNoise.Generate2D (BaseNoise, 5, 5, BlockX / m_BaseFrequencyX, (BlockX + 17) / m_BaseFrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, Workspace);
// Calculate the final noise based on the partial noises:
for (int y = 0; y < 33; y++)
for (int z = 0; z < 5; z++)
{
NOISE_DATATYPE AddHeight = (static_cast<NOISE_DATATYPE>(y * 8) - m_MidPoint) * m_HeightAmplification;
// If "underground", make the terrain smoother by forcing the vertical linear gradient into steeper slope:
if (AddHeight < 0)
for (int x = 0; x < 5; x++)
{
AddHeight *= 4;
}
for (int z = 0; z < 5; z++)
{
for (int x = 0; x < 5; x++)
NOISE_DATATYPE curBaseNoise = BaseNoise[x + 5 * z];
for (int y = 0; y < 33; y++)
{
int idx = x + 5 * z + 5 * 5 * y;
Workspace[idx] = ClampedLerp(DensityNoiseA[idx], DensityNoiseB[idx], 8 * (ChoiceNoise[idx] + 0.5f)) + AddHeight + BaseNoise[x + 5 * z];
NOISE_DATATYPE AddHeight = (static_cast<NOISE_DATATYPE>(y * 8) - m_MidPoint) * m_HeightAmplification;
// If "underground", make the terrain smoother by forcing the vertical linear gradient into steeper slope:
if (AddHeight < 0)
{
AddHeight *= 4;
}
// If too high, cut off any terrain:
if (y > 28)
{
AddHeight = AddHeight + static_cast<NOISE_DATATYPE>(y - 28) / 4;
}
// Decide between the two density noises:
int idx = 33 * x + 33 * 5 * z + y;
Workspace[idx] = ClampedLerp(DensityNoiseA[idx], DensityNoiseB[idx], 8 * (ChoiceNoise[idx] + 0.5f)) + AddHeight + curBaseNoise;
}
}
}
LinearUpscale3DArray<NOISE_DATATYPE>(Workspace, 5, 5, 33, m_NoiseArray, 4, 4, 8);
LinearUpscale3DArray<NOISE_DATATYPE>(Workspace, 33, 5, 5, m_NoiseArray, 8, 4, 4);
}
void cNoise3DComposable::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
void cNoise3DComposable::GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape)
{
GenerateNoiseArrayIfNeeded(a_ChunkX, a_ChunkZ);
// Translate the noise array into Shape:
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel);
for (int y = cChunkDef::Height - 1; y > m_SeaLevel; y--)
for (int y = 0; y < cChunkDef::Height; y++)
{
if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= m_AirThreshold)
{
cChunkDef::SetHeight(a_HeightMap, x, z, y);
break;
}
} // for y
} // for x
} // for z
}
void cNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc)
{
GenerateNoiseArrayIfNeeded(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ());
a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
// Make basic terrain composition:
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
int LastAir = a_ChunkDesc.GetHeight(x, z) + 1;
bool HasHadWater = false;
for (int y = LastAir; y < m_SeaLevel; y++)
{
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER);
a_Shape[y + x * 256 + z * 256 * 16] = (m_NoiseArray[y + 257 * x + 257 * 17 * z] > m_AirThreshold) ? 0 : 1;
}
for (int y = LastAir - 1; y > 0; y--)
{
if (m_NoiseArray[x + 17 * z + 17 * 17 * y] > m_AirThreshold)
{
// "air" part
LastAir = y;
if (y < m_SeaLevel)
{
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER);
HasHadWater = true;
}
continue;
}
// "ground" part:
if (LastAir - y > 4)
{
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STONE);
continue;
}
if (HasHadWater)
{
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND);
}
else
{
a_ChunkDesc.SetBlockType(x, y, z, (LastAir == y + 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT);
}
} // for y
a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
} // for x
} // for z
}
@ -586,7 +537,7 @@ cBiomalNoise3DComposable::cBiomalNoise3DComposable(int a_Seed, cBiomeGenPtr a_Bi
{
for (int x = 0; x <= AVERAGING_SIZE * 2; x++)
{
m_Weight[z][x] = static_cast<NOISE_DATATYPE>((5 - std::abs(5 - x)) + (5 - std::abs(5 - z)));
m_Weight[z][x] = static_cast<NOISE_DATATYPE>((AVERAGING_SIZE - std::abs(AVERAGING_SIZE - x)) + (AVERAGING_SIZE - std::abs(AVERAGING_SIZE - z)));
m_WeightSum += m_Weight[z][x];
}
}
@ -600,7 +551,7 @@ void cBiomalNoise3DComposable::Initialize(cIniFile & a_IniFile)
{
// Params:
// The defaults generate extreme hills terrain
m_SeaLevel = a_IniFile.GetValueSetI("Generator", "BiomalNoise3DSeaLevel", 62);
m_SeaLevel = a_IniFile.GetValueSetI("Generator", "SeaLevel", 62);
m_FrequencyX = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DFrequencyX", 40);
m_FrequencyY = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DFrequencyY", 40);
m_FrequencyZ = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "BiomalNoise3DFrequencyZ", 40);
@ -671,34 +622,42 @@ void cBiomalNoise3DComposable::GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_Ch
NOISE_DATATYPE BaseNoise[5 * 5];
NOISE_DATATYPE BlockX = static_cast<NOISE_DATATYPE>(a_ChunkX * cChunkDef::Width);
NOISE_DATATYPE BlockZ = static_cast<NOISE_DATATYPE>(a_ChunkZ * cChunkDef::Width);
// Note that we have to swap the coords, because noise generator uses [x + SizeX * y + SizeX * SizeY * z] ordering and we want "BlockY" to be "z":
m_ChoiceNoise.Generate3D (ChoiceNoise, 5, 5, 33, BlockX / m_ChoiceFrequencyX, (BlockX + 17) / m_ChoiceFrequencyX, BlockZ / m_ChoiceFrequencyZ, (BlockZ + 17) / m_ChoiceFrequencyZ, 0, 257 / m_ChoiceFrequencyY, Workspace);
m_DensityNoiseA.Generate3D(DensityNoiseA, 5, 5, 33, BlockX / m_FrequencyX, (BlockX + 17) / m_FrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, 0, 257 / m_FrequencyY, Workspace);
m_DensityNoiseB.Generate3D(DensityNoiseB, 5, 5, 33, BlockX / m_FrequencyX, (BlockX + 17) / m_FrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, 0, 257 / m_FrequencyY, Workspace);
// Note that we have to swap the X and Y coords, because noise generator uses [x + SizeX * y + SizeX * SizeY * z] ordering and we want "BlockY" to be "x":
m_ChoiceNoise.Generate3D (ChoiceNoise, 33, 5, 5, 0, 257 / m_ChoiceFrequencyY, BlockX / m_ChoiceFrequencyX, (BlockX + 17) / m_ChoiceFrequencyX, BlockZ / m_ChoiceFrequencyZ, (BlockZ + 17) / m_ChoiceFrequencyZ, Workspace);
m_DensityNoiseA.Generate3D(DensityNoiseA, 33, 5, 5, 0, 257 / m_FrequencyY, BlockX / m_FrequencyX, (BlockX + 17) / m_FrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, Workspace);
m_DensityNoiseB.Generate3D(DensityNoiseB, 33, 5, 5, 0, 257 / m_FrequencyY, BlockX / m_FrequencyX, (BlockX + 17) / m_FrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, Workspace);
m_BaseNoise.Generate2D (BaseNoise, 5, 5, BlockX / m_BaseFrequencyX, (BlockX + 17) / m_BaseFrequencyX, BlockZ / m_FrequencyZ, (BlockZ + 17) / m_FrequencyZ, Workspace);
// Calculate the final noise based on the partial noises:
for (int y = 0; y < 33; y++)
for (int z = 0; z < 5; z++)
{
NOISE_DATATYPE BlockHeight = static_cast<NOISE_DATATYPE>(y * 8);
for (int z = 0; z < 5; z++)
for (int x = 0; x < 5; x++)
{
for (int x = 0; x < 5; x++)
NOISE_DATATYPE curMidPoint = MidPoint[x + 5 * z];
NOISE_DATATYPE curHeightAmp = HeightAmp[x + 5 * z];
NOISE_DATATYPE curBaseNoise = BaseNoise[x + 5 * z];
for (int y = 0; y < 33; y++)
{
NOISE_DATATYPE AddHeight = (BlockHeight - MidPoint[x + 5 * z]) * HeightAmp[x + 5 * z];
NOISE_DATATYPE AddHeight = (static_cast<NOISE_DATATYPE>(y * 8) - curMidPoint) * curHeightAmp;
// If "underground", make the terrain smoother by forcing the vertical linear gradient into steeper slope:
if (AddHeight < 0)
{
AddHeight *= 4;
}
// If too high, cut off any terrain:
if (y > 28)
{
AddHeight = AddHeight + static_cast<NOISE_DATATYPE>(y - 28) / 4;
}
int idx = x + 5 * z + 5 * 5 * y;
Workspace[idx] = ClampedLerp(DensityNoiseA[idx], DensityNoiseB[idx], 8 * (ChoiceNoise[idx] + 0.5f)) + AddHeight + BaseNoise[x + 5 * z];
// Decide between the two density noises:
int idx = 33 * x + y + 33 * 5 * z;
Workspace[idx] = ClampedLerp(DensityNoiseA[idx], DensityNoiseB[idx], 8 * (ChoiceNoise[idx] + 0.5f)) + AddHeight + curBaseNoise;
}
}
}
LinearUpscale3DArray<NOISE_DATATYPE>(Workspace, 5, 5, 33, m_NoiseArray, 4, 4, 8);
LinearUpscale3DArray<NOISE_DATATYPE>(Workspace, 33, 5, 5, m_NoiseArray, 8, 4, 4);
}
@ -756,72 +715,67 @@ void cBiomalNoise3DComposable::GetBiomeParams(EMCSBiome a_Biome, NOISE_DATATYPE
{
switch (a_Biome)
{
case biBeach: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
case biBirchForest: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
case biBirchForestHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
case biBirchForestHillsM: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
case biBirchForestM: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
case biColdBeach: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
case biDesertHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
case biDeepOcean: a_HeightAmp = 0.17f; a_MidPoint = 35; break;
case biDesert: a_HeightAmp = 0.29f; a_MidPoint = 62; break;
case biEnd: a_HeightAmp = 0.15f; a_MidPoint = 64; break;
case biExtremeHills: a_HeightAmp = 0.045f; a_MidPoint = 75; break;
case biExtremeHillsPlus: a_HeightAmp = 0.04f; a_MidPoint = 80; break;
case biFlowerForest: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
case biForest: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
case biForestHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
case biFrozenRiver: a_HeightAmp = 0.4f; a_MidPoint = 53; break;
case biFrozenOcean: a_HeightAmp = 0.17f; a_MidPoint = 47; break;
case biIceMountains: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
case biIcePlains: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
case biIcePlainsSpikes: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
case biJungle: a_HeightAmp = 0.1f; a_MidPoint = 63; break;
case biJungleHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
case biJungleM: a_HeightAmp = 0.1f; a_MidPoint = 63; break;
case biMegaSpruceTaigaHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
case biMegaTaigaHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
case biMushroomShore: a_HeightAmp = 0.15f; a_MidPoint = 15; break;
case biOcean: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
case biPlains: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
case biRiver: a_HeightAmp = 0.4f; a_MidPoint = 53; break;
case biSwampland: a_HeightAmp = 0.25f; a_MidPoint = 59; break;
case biSwamplandM: a_HeightAmp = 0.11f; a_MidPoint = 59; break;
case biTaigaHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
/*
// Still missing:
case biColdTaiga: a_HeightAmp = 0.15f; a_MidPoint = 30; break;
case biColdTaigaHills: a_HeightAmp = 0.15f; a_MidPoint = 31; break;
case biColdTaigaM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
case biDesertM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
case biExtremeHillsEdge: a_HeightAmp = 0.15f; a_MidPoint = 20; break;
case biExtremeHillsM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
case biExtremeHillsPlusM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
case biJungleEdge: a_HeightAmp = 0.15f; a_MidPoint = 23; break;
case biJungleEdgeM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
case biMegaSpruceTaiga: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
case biMegaTaiga: a_HeightAmp = 0.15f; a_MidPoint = 32; break;
case biMesa: a_HeightAmp = 0.15f; a_MidPoint = 37; break;
case biMesaBryce: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
case biMesaPlateau: a_HeightAmp = 0.15f; a_MidPoint = 39; break;
case biMesaPlateauF: a_HeightAmp = 0.15f; a_MidPoint = 38; break;
case biMesaPlateauFM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
case biMesaPlateauM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
case biMushroomIsland: a_HeightAmp = 0.15f; a_MidPoint = 14; break;
case biNether: a_HeightAmp = 0.15f; a_MidPoint = 68; break;
case biRoofedForest: a_HeightAmp = 0.15f; a_MidPoint = 29; break;
case biRoofedForestM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
case biSavanna: a_HeightAmp = 0.15f; a_MidPoint = 35; break;
case biSavannaM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
case biSavannaPlateau: a_HeightAmp = 0.15f; a_MidPoint = 36; break;
case biSavannaPlateauM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
case biStoneBeach: a_HeightAmp = 0.15f; a_MidPoint = 25; break;
case biSunflowerPlains: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
case biTaiga: a_HeightAmp = 0.15f; a_MidPoint = 65; break;
case biTaigaM: a_HeightAmp = 0.15f; a_MidPoint = 70; break;
*/
case biBeach: a_HeightAmp = 0.2f; a_MidPoint = 60; break;
case biBirchForest: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
case biBirchForestHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
case biBirchForestHillsM: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
case biBirchForestM: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
case biColdBeach: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
case biColdTaiga: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
case biColdTaigaM: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
case biColdTaigaHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
case biDesertHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
case biDeepOcean: a_HeightAmp = 0.17f; a_MidPoint = 35; break;
case biDesert: a_HeightAmp = 0.29f; a_MidPoint = 62; break;
case biDesertM: a_HeightAmp = 0.29f; a_MidPoint = 62; break;
case biEnd: a_HeightAmp = 0.15f; a_MidPoint = 64; break;
case biExtremeHills: a_HeightAmp = 0.045f; a_MidPoint = 75; break;
case biExtremeHillsEdge: a_HeightAmp = 0.1f; a_MidPoint = 70; break;
case biExtremeHillsM: a_HeightAmp = 0.045f; a_MidPoint = 75; break;
case biExtremeHillsPlus: a_HeightAmp = 0.04f; a_MidPoint = 80; break;
case biExtremeHillsPlusM: a_HeightAmp = 0.04f; a_MidPoint = 80; break;
case biFlowerForest: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
case biForest: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
case biForestHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
case biFrozenRiver: a_HeightAmp = 0.4f; a_MidPoint = 57; break;
case biFrozenOcean: a_HeightAmp = 0.12f; a_MidPoint = 45; break;
case biIceMountains: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
case biIcePlains: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
case biIcePlainsSpikes: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
case biJungle: a_HeightAmp = 0.1f; a_MidPoint = 63; break;
case biJungleEdge: a_HeightAmp = 0.15f; a_MidPoint = 62; break;
case biJungleEdgeM: a_HeightAmp = 0.15f; a_MidPoint = 62; break;
case biJungleHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
case biJungleM: a_HeightAmp = 0.1f; a_MidPoint = 63; break;
case biMegaSpruceTaiga: a_HeightAmp = 0.09f; a_MidPoint = 64; break;
case biMegaSpruceTaigaHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
case biMegaTaiga: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
case biMegaTaigaHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
case biMesa: a_HeightAmp = 0.09f; a_MidPoint = 61; break;
case biMesaBryce: a_HeightAmp = 0.15f; a_MidPoint = 61; break;
case biMesaPlateau: a_HeightAmp = 0.25f; a_MidPoint = 86; break;
case biMesaPlateauF: a_HeightAmp = 0.25f; a_MidPoint = 96; break;
case biMesaPlateauFM: a_HeightAmp = 0.25f; a_MidPoint = 96; break;
case biMesaPlateauM: a_HeightAmp = 0.25f; a_MidPoint = 86; break;
case biMushroomShore: a_HeightAmp = 0.075f; a_MidPoint = 60; break;
case biMushroomIsland: a_HeightAmp = 0.06f; a_MidPoint = 80; break;
case biNether: a_HeightAmp = 0.01f; a_MidPoint = 64; break;
case biOcean: a_HeightAmp = 0.12f; a_MidPoint = 45; break;
case biPlains: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
case biRiver: a_HeightAmp = 0.4f; a_MidPoint = 57; break;
case biRoofedForest: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
case biRoofedForestM: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
case biSavanna: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
case biSavannaM: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
case biSavannaPlateau: a_HeightAmp = 0.3f; a_MidPoint = 85; break;
case biSavannaPlateauM: a_HeightAmp = 0.012f; a_MidPoint = 105; break;
case biStoneBeach: a_HeightAmp = 0.075f; a_MidPoint = 60; break;
case biSunflowerPlains: a_HeightAmp = 0.3f; a_MidPoint = 62; break;
case biSwampland: a_HeightAmp = 0.25f; a_MidPoint = 59; break;
case biSwamplandM: a_HeightAmp = 0.11f; a_MidPoint = 59; break;
case biTaiga: a_HeightAmp = 0.1f; a_MidPoint = 64; break;
case biTaigaM: a_HeightAmp = 0.1f; a_MidPoint = 70; break;
case biTaigaHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break;
default:
{
// Make a crazy terrain so that it stands out
@ -835,78 +789,19 @@ void cBiomalNoise3DComposable::GetBiomeParams(EMCSBiome a_Biome, NOISE_DATATYPE
void cBiomalNoise3DComposable::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
void cBiomalNoise3DComposable::GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape)
{
GenerateNoiseArrayIfNeeded(a_ChunkX, a_ChunkZ);
// Translate the noise array into Shape:
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
cChunkDef::SetHeight(a_HeightMap, x, z, m_SeaLevel);
for (int y = cChunkDef::Height - 1; y > m_SeaLevel; y--)
for (int y = 0; y < cChunkDef::Height; y++)
{
if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= m_AirThreshold)
{
cChunkDef::SetHeight(a_HeightMap, x, z, y);
break;
}
} // for y
} // for x
} // for z
}
void cBiomalNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc)
{
GenerateNoiseArrayIfNeeded(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ());
a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0);
// Make basic terrain composition:
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
int LastAir = a_ChunkDesc.GetHeight(x, z) + 1;
bool HasHadWater = false;
for (int y = LastAir; y < m_SeaLevel; y++)
{
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER);
a_Shape[y + x * 256 + z * 256 * 16] = (m_NoiseArray[y + 257 * x + 257 * 17 * z] > m_AirThreshold) ? 0 : 1;
}
for (int y = LastAir - 1; y > 0; y--)
{
if (m_NoiseArray[x + 17 * z + 17 * 17 * y] > m_AirThreshold)
{
// "air" part
LastAir = y;
if (y < m_SeaLevel)
{
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STATIONARY_WATER);
HasHadWater = true;
}
continue;
}
// "ground" part:
if (LastAir - y > 4)
{
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_STONE);
continue;
}
if (HasHadWater)
{
a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SAND);
}
else
{
a_ChunkDesc.SetBlockType(x, y, z, (LastAir == y + 1) ? E_BLOCK_GRASS : E_BLOCK_DIRT);
}
} // for y
a_ChunkDesc.SetBlockType(x, 0, z, E_BLOCK_BEDROCK);
} // for x
} // for z
}
@ -914,3 +809,4 @@ void cBiomalNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc)

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;
@ -69,8 +73,7 @@ protected:
class cNoise3DComposable :
public cTerrainHeightGen,
public cTerrainCompositionGen
public cTerrainShapeGen
{
public:
cNoise3DComposable(int a_Seed);
@ -78,20 +81,17 @@ public:
void Initialize(cIniFile & a_IniFile);
protected:
/** The noise that is used to choose between density noise A and B. */
cPerlinNoise m_ChoiceNoise;
/** The 3D noise that is used to choose between density noise A and B. */
cOctavedNoise<cInterpolNoise<Interp5Deg>> m_ChoiceNoise;
/** Density 3D noise, variant A. */
cPerlinNoise m_DensityNoiseA;
cOctavedNoise<cInterpolNoise<Interp5Deg>> m_DensityNoiseA;
/** Density 3D noise, variant B. */
cPerlinNoise m_DensityNoiseB;
cOctavedNoise<cInterpolNoise<Interp5Deg>> m_DensityNoiseB;
/** Heightmap-like noise used to provide variance for low-amplitude biomes. */
cPerlinNoise m_BaseNoise;
/** Block height of the sealevel, used for composing the terrain. */
int m_SeaLevel;
cOctavedNoise<cInterpolNoise<Interp5Deg>> m_BaseNoise;
/** The main parameter of the generator, specifies the slope of the vertical linear gradient.
A higher value means a steeper slope and a smaller total amplitude of the generated terrain. */
@ -127,12 +127,8 @@ protected:
void GenerateNoiseArrayIfNeeded(int a_ChunkX, int a_ChunkZ);
// cTerrainHeightGen overrides:
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
virtual void InitializeHeightGen(cIniFile & a_IniFile) override { Initialize(a_IniFile); }
// cTerrainCompositionGen overrides:
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
virtual void InitializeCompoGen(cIniFile & a_IniFile) override { Initialize(a_IniFile); }
virtual void GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape) override;
virtual void InitializeShapeGen(cIniFile & a_IniFile) override { Initialize(a_IniFile); }
} ;
@ -140,8 +136,7 @@ protected:
class cBiomalNoise3DComposable :
public cTerrainHeightGen,
public cTerrainCompositionGen
public cTerrainShapeGen
{
public:
cBiomalNoise3DComposable(int a_Seed, cBiomeGenPtr a_BiomeGen);
@ -149,24 +144,24 @@ public:
void Initialize(cIniFile & a_IniFile);
protected:
/** Number of columns around the pixel to query for biomes for averaging. */
static const int AVERAGING_SIZE = 5;
/** Number of columns around the pixel to query for biomes for averaging. Must be less than or equal to 16. */
static const int AVERAGING_SIZE = 9;
/** Type used for a single parameter across the entire (downscaled) chunk. */
typedef NOISE_DATATYPE ChunkParam[5 * 5];
/** The noise that is used to choose between density noise A and B. */
cPerlinNoise m_ChoiceNoise;
cOctavedNoise<cInterpolNoise<Interp5Deg>> m_ChoiceNoise;
/** Density 3D noise, variant A. */
cPerlinNoise m_DensityNoiseA;
cOctavedNoise<cInterpolNoise<Interp5Deg>> m_DensityNoiseA;
/** Density 3D noise, variant B. */
cPerlinNoise m_DensityNoiseB;
cOctavedNoise<cInterpolNoise<Interp5Deg>> m_DensityNoiseB;
/** Heightmap-like noise used to provide variance for low-amplitude biomes. */
cPerlinNoise m_BaseNoise;
cOctavedNoise<cInterpolNoise<Interp5Deg>> m_BaseNoise;
/** The underlying biome generator. */
cBiomeGenPtr m_BiomeGen;
@ -194,7 +189,7 @@ protected:
// Cache for the last calculated chunk (reused between heightmap and composition queries):
int m_LastChunkX;
int m_LastChunkZ;
NOISE_DATATYPE m_NoiseArray[17 * 17 * 257]; // x + 17 * z + 17 * 17 * y
NOISE_DATATYPE m_NoiseArray[17 * 17 * 257]; // 257 * x + y + 257 * 17 * z
/** Weights for summing up neighboring biomes. */
NOISE_DATATYPE m_Weight[AVERAGING_SIZE * 2 + 1][AVERAGING_SIZE * 2 + 1];
@ -212,13 +207,9 @@ protected:
/** Returns the parameters for the specified biome. */
void GetBiomeParams(EMCSBiome a_Biome, NOISE_DATATYPE & a_HeightAmp, NOISE_DATATYPE & a_MidPoint);
// cTerrainHeightGen overrides:
virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
virtual void InitializeHeightGen(cIniFile & a_IniFile) override { Initialize(a_IniFile); }
// cTerrainCompositionGen overrides:
virtual void ComposeTerrain(cChunkDesc & a_ChunkDesc) override;
virtual void InitializeCompoGen(cIniFile & a_IniFile) override { Initialize(a_IniFile); }
// cTerrainShapeGen overrides:
virtual void GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape) override;
virtual void InitializeShapeGen(cIniFile & a_IniFile) override { Initialize(a_IniFile); }
} ;

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"

145
src/Generating/ShapeGen.cpp Normal file
View File

@ -0,0 +1,145 @@
// ShapeGen.cpp
// Implements the function to create a cTerrainShapeGen descendant based on INI file settings
#include "Globals.h"
#include "HeiGen.h"
#include "../IniFile.h"
#include "DistortedHeightmap.h"
#include "EndGen.h"
#include "Noise3DGenerator.h"
#include "TwoHeights.h"
////////////////////////////////////////////////////////////////////////////////
// cTerrainHeightToShapeGen:
/** Converts old-style height-generators into new-style shape-generators. */
class cTerrainHeightToShapeGen:
public cTerrainShapeGen
{
public:
cTerrainHeightToShapeGen(cTerrainHeightGenPtr a_HeightGen):
m_HeightGen(a_HeightGen)
{
}
// cTerrainShapeGen overrides:
virtual void GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape) override
{
// Generate the heightmap:
cChunkDef::HeightMap heightMap;
m_HeightGen->GenHeightMap(a_ChunkX, a_ChunkZ, heightMap);
// Convert from heightmap to shape:
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
HEIGHTTYPE height = cChunkDef::GetHeight(heightMap, x, z) + 1;
Byte * shapeColumn = &(a_Shape[(x + 16 * z) * 256]);
for (int y = 0; y < height; y++)
{
shapeColumn[y] = 1;
}
for (int y = height; y < cChunkDef::Height; y++)
{
shapeColumn[y] = 0;
}
} // for x
} // for z
}
virtual void InitializeShapeGen(cIniFile & a_IniFile) override
{
m_HeightGen->InitializeHeightGen(a_IniFile);
}
protected:
/** The height generator being converted. */
cTerrainHeightGenPtr m_HeightGen;
};
typedef SharedPtr<cTerrainHeightToShapeGen> cTerrainHeightToShapeGenPtr;
////////////////////////////////////////////////////////////////////////////////
// cTerrainShapeGen:
cTerrainShapeGenPtr cTerrainShapeGen::CreateShapeGen(cIniFile & a_IniFile, cBiomeGenPtr a_BiomeGen, int a_Seed, bool & a_CacheOffByDefault)
{
AString shapeGenName = a_IniFile.GetValueSet("Generator", "ShapeGen", "");
if (shapeGenName.empty())
{
LOGWARN("[Generator] ShapeGen value not set in world.ini, using \"BiomalNoise3D\".");
shapeGenName = "BiomalNoise3D";
}
// If the shapegen is HeightMap, redirect to older HeightMap-based generators:
if (NoCaseCompare(shapeGenName, "HeightMap") == 0)
{
cTerrainHeightGenPtr heightGen = cTerrainHeightGen::CreateHeightGen(a_IniFile, a_BiomeGen, a_Seed, a_CacheOffByDefault);
if (heightGen != nullptr)
{
return std::make_shared<cTerrainHeightToShapeGen>(heightGen);
}
// The height gen was not recognized; several heightgens were promoted to shape gens, so let's try them instead:
shapeGenName = a_IniFile.GetValue("Generator", "HeightGen", "");
if (shapeGenName.empty())
{
LOGWARNING("[Generator] ShapeGen set to HeightMap, but HeightGen not set. Reverting to \"BiomalNoise3D\".");
shapeGenName = "BiomalNoise3D";
a_IniFile.SetValue("Generator", "ShapeGen", shapeGenName);
}
}
// Choose the shape generator based on the name:
a_CacheOffByDefault = false;
cTerrainShapeGenPtr res;
if (NoCaseCompare(shapeGenName, "DistortedHeightmap") == 0)
{
res = std::make_shared<cDistortedHeightmap>(a_Seed, a_BiomeGen);
}
else if (NoCaseCompare(shapeGenName, "End") == 0)
{
res = std::make_shared<cEndGen>(a_Seed);
}
else if (NoCaseCompare(shapeGenName, "BiomalNoise3D") == 0)
{
res = std::make_shared<cBiomalNoise3DComposable>(a_Seed, a_BiomeGen);
}
else if (NoCaseCompare(shapeGenName, "Noise3D") == 0)
{
res = std::make_shared<cNoise3DComposable>(a_Seed);
}
else if (NoCaseCompare(shapeGenName, "TwoHeights") == 0)
{
res = CreateShapeGenTwoHeights(a_Seed, a_BiomeGen);
}
else
{
// No match found, force-set the default and retry
LOGWARN("Unknown ShapeGen \"%s\", using \"BiomalNoise3D\" instead.", shapeGenName.c_str());
a_IniFile.SetValue("Generator", "ShapeGen", "BiomalNoise3D");
return CreateShapeGen(a_IniFile, a_BiomeGen, a_Seed, a_CacheOffByDefault);
}
// Read the settings:
res->InitializeShapeGen(a_IniFile);
return res;
}

View File

@ -37,10 +37,13 @@ void cStructGenTrees::GenFinish(cChunkDesc & a_ChunkDesc)
Dest = &WorkerDesc;
WorkerDesc.SetChunkCoords(BaseX, BaseZ);
// TODO: This may cause a lot of wasted calculations, instead of pulling data out of a single (cChunkDesc) cache
cChunkDesc::Shape workerShape;
m_BiomeGen->GenBiomes (BaseX, BaseZ, WorkerDesc.GetBiomeMap());
m_HeightGen->GenHeightMap (BaseX, BaseZ, WorkerDesc.GetHeightMap());
m_CompositionGen->ComposeTerrain(WorkerDesc);
// TODO: Free the entity lists
m_ShapeGen->GenShape (BaseX, BaseZ, workerShape);
WorkerDesc.SetHeightFromShape (workerShape);
m_CompositionGen->ComposeTerrain(WorkerDesc, workerShape);
}
else
{
@ -97,7 +100,7 @@ void cStructGenTrees::GenerateSingleTree(
int Height = a_ChunkDesc.GetHeight(x, z);
if ((Height <= 0) || (Height > 240))
if ((Height <= 0) || (Height >= 230))
{
return;
}
@ -125,6 +128,11 @@ void cStructGenTrees::GenerateSingleTree(
// Outside the chunk
continue;
}
if (itr->y >= cChunkDef::Height)
{
// Above the chunk, cut off (this shouldn't happen too often, we're limiting trees to y < 230)
continue;
}
BLOCKTYPE Block = a_ChunkDesc.GetBlockType(itr->x, itr->y, itr->z);
switch (Block)
@ -159,7 +167,7 @@ void cStructGenTrees::ApplyTreeImage(
// Put the generated image into a_BlockTypes, push things outside this chunk into a_Blocks
for (sSetBlockVector::const_iterator itr = a_Image.begin(), end = a_Image.end(); itr != end; ++itr)
{
if ((itr->ChunkX == a_ChunkX) && (itr->ChunkZ == a_ChunkZ))
if ((itr->ChunkX == a_ChunkX) && (itr->ChunkZ == a_ChunkZ) && (itr->y < cChunkDef::Height))
{
// Inside this chunk, integrate into a_ChunkDesc:
switch (a_ChunkDesc.GetBlockType(itr->x, itr->y, itr->z))
@ -390,7 +398,7 @@ void cStructGenLakes::GenFinish(cChunkDesc & a_ChunkDesc)
}
cBlockArea Lake;
CreateLakeImage(ChunkX + x, ChunkZ + z, Lake);
CreateLakeImage(ChunkX + x, ChunkZ + z, a_ChunkDesc.GetMinHeight(), Lake);
int OfsX = Lake.GetOriginX() + x * cChunkDef::Width;
int OfsZ = Lake.GetOriginZ() + z * cChunkDef::Width;
@ -404,25 +412,13 @@ void cStructGenLakes::GenFinish(cChunkDesc & a_ChunkDesc)
void cStructGenLakes::CreateLakeImage(int a_ChunkX, int a_ChunkZ, cBlockArea & a_Lake)
void cStructGenLakes::CreateLakeImage(int a_ChunkX, int a_ChunkZ, int a_MaxLakeHeight, cBlockArea & a_Lake)
{
a_Lake.Create(16, 8, 16);
a_Lake.Fill(cBlockArea::baTypes, E_BLOCK_SPONGE); // Sponge is the NOP blocktype for lake merging strategy
// Find the minimum height in this chunk:
cChunkDef::HeightMap HeightMap;
m_HeiGen->GenHeightMap(a_ChunkX, a_ChunkZ, HeightMap);
HEIGHTTYPE MinHeight = HeightMap[0];
for (size_t i = 1; i < ARRAYCOUNT(HeightMap); i++)
{
if (HeightMap[i] < MinHeight)
{
MinHeight = HeightMap[i];
}
}
// Make a random position in the chunk by using a random 16 block XZ offset and random height up to chunk's max height minus 6
MinHeight = std::max(MinHeight - 6, 2);
int MinHeight = std::max(a_MaxLakeHeight - 6, 2);
int Rnd = m_Noise.IntNoise3DInt(a_ChunkX, 128, a_ChunkZ) / 11;
// Random offset [-8 .. 8], with higher probability around 0; add up four three-bit-wide randoms [0 .. 28], divide and subtract to get range
int OffsetX = 4 * ((Rnd & 0x07) + ((Rnd & 0x38) >> 3) + ((Rnd & 0x1c0) >> 6) + ((Rnd & 0xe00) >> 9)) / 7 - 8;

View File

@ -14,7 +14,7 @@
#pragma once
#include "ComposableGenerator.h"
#include "../Noise.h"
#include "../Noise/Noise.h"
@ -24,11 +24,11 @@ class cStructGenTrees :
public cFinishGen
{
public:
cStructGenTrees(int a_Seed, cBiomeGenPtr a_BiomeGen, cTerrainHeightGenPtr a_HeightGen, cTerrainCompositionGenPtr a_CompositionGen) :
cStructGenTrees(int a_Seed, cBiomeGenPtr a_BiomeGen, cTerrainShapeGenPtr a_ShapeGen, cTerrainCompositionGenPtr a_CompositionGen) :
m_Seed(a_Seed),
m_Noise(a_Seed),
m_BiomeGen(a_BiomeGen),
m_HeightGen(a_HeightGen),
m_ShapeGen(a_ShapeGen),
m_CompositionGen(a_CompositionGen)
{}
@ -37,12 +37,12 @@ protected:
int m_Seed;
cNoise m_Noise;
cBiomeGenPtr m_BiomeGen;
cTerrainHeightGenPtr m_HeightGen;
cTerrainShapeGenPtr m_ShapeGen;
cTerrainCompositionGenPtr m_CompositionGen;
/** Generates and applies an image of a single tree.
Parts of the tree inside the chunk are applied to a_BlockX.
Parts of the tree outside the chunk are stored in a_OutsideX
Parts of the tree inside the chunk are applied to a_ChunkDesc.
Parts of the tree outside the chunk are stored in a_OutsideXYZ
*/
void GenerateSingleTree(
int a_ChunkX, int a_ChunkZ, int a_Seq,
@ -51,7 +51,7 @@ protected:
sSetBlockVector & a_OutsideOther
) ;
/// Applies an image into chunk blockdata; all blocks outside the chunk will be appended to a_Overflow
/** Applies an image into chunk blockdata; all blocks outside the chunk will be appended to a_Overflow. */
void ApplyTreeImage(
int a_ChunkX, int a_ChunkZ,
cChunkDesc & a_ChunkDesc,
@ -124,27 +124,30 @@ class cStructGenLakes :
public cFinishGen
{
public:
cStructGenLakes(int a_Seed, BLOCKTYPE a_Fluid, cTerrainHeightGenPtr a_HeiGen, int a_Probability) :
cStructGenLakes(int a_Seed, BLOCKTYPE a_Fluid, cTerrainShapeGenPtr a_ShapeGen, int a_Probability) :
m_Noise(a_Seed),
m_Seed(a_Seed),
m_Fluid(a_Fluid),
m_HeiGen(a_HeiGen),
m_ShapeGen(a_ShapeGen),
m_Probability(a_Probability)
{
}
protected:
cNoise m_Noise;
int m_Seed;
BLOCKTYPE m_Fluid;
cTerrainHeightGenPtr m_HeiGen;
int m_Probability; ///< Chance, 0 .. 100, of a chunk having the lake
cNoise m_Noise;
int m_Seed;
BLOCKTYPE m_Fluid;
cTerrainShapeGenPtr m_ShapeGen;
/** Chance, [0 .. 100], of a chunk having the lake. */
int m_Probability;
// cFinishGen override:
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
/// Creates a lake image for the specified chunk into a_Lake
void CreateLakeImage(int a_ChunkX, int a_ChunkZ, cBlockArea & a_Lake);
/** Creates a lake image for the specified chunk into a_Lake. */
void CreateLakeImage(int a_ChunkX, int a_ChunkZ, int a_MaxLakeHeight, cBlockArea & a_Lake);
} ;

View File

@ -61,7 +61,7 @@ static const sCoords BigO3[] =
static const sCoords BigO4[] = // Part of Big Jungle tree
{
/* -4 */ {-2, -4}, {-1, -4}, {0, -4}, {1, -4}, {2, -4},
/* -4 */ {-2, -4}, {-1, -4}, {0, -4}, {1, -4}, {2, -4},
/* -3 */ {-3, -3}, {-2, -3}, {-1, -3}, {0, -3}, {1, -3}, {2, -3}, {3, -3},
/* -2 */ {-4, -2}, {-3, -2}, {-2, -2}, {-1, -2}, {0, -2}, {1, -2}, {2, -2}, {3, -2}, {4, -2},
/* -1 */ {-4, -1}, {-3, -1}, {-2, -1}, {-1, -1}, {0, -1}, {1, -1}, {2, -1}, {3, -1}, {4, -1},
@ -361,7 +361,109 @@ void GetSmallAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a
void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
{
// TODO
int Height = 7 + a_Noise.IntNoise3DInt(a_BlockX, a_BlockY, a_BlockZ) % 4;
// Array with possible directions for a branch to go to.
const Vector3d AvailableDirections[] =
{
{ -1, 0, 0 }, { 0, 0, -1 },
{ -1, 0, 1 }, { -1, 0, -1 },
{ 1, 0, 1 }, { 1, 0, -1 },
{ 1, 0, 0 }, { 0, 0, 1 },
{ -0.5, 0, 0 }, { 0, 0, -0.5 },
{ -0.5, 0, 0.5 }, { -0.5, 0, -0.5 },
{ 0.5, 0, 0.5 }, { 0.5, 0, -0.5 },
{ 0.5, 0, 0 }, { 0, 0, 0.5 },
{ -1, 0.5, 0 }, { 0, 0.5, -1 },
{ -1, 0.5, 1 }, { -1, 0.5, -1 },
{ 1, 0.5, 1 }, { 1, 0.5, -1 },
{ 1, 0.5, 0 }, { 0, 0.5, 1 },
{ -0.5, 0.5, 0 }, { 0, 0.5, -0.5 },
{ -0.5, 0.5, 0.5 }, { -0.5, 0.5, -0.5 },
{ 0.5, 0.5, 0.5 }, { 0.5, 0.5, -0.5 },
{ 0.5, 0.5, 0 }, { 0, 0.5, 0.5 },
};
// Create branches
for (int i = 4; i < Height; i++)
{
// Get a direction for the trunk to go to.
Vector3d BranchStartDirection = AvailableDirections[a_Noise.IntNoise3DInt(a_BlockX, a_BlockY + i, a_BlockZ) % ARRAYCOUNT(AvailableDirections)];
Vector3d BranchDirection = AvailableDirections[a_Noise.IntNoise3DInt(a_BlockX, a_BlockY / i, a_BlockZ) % ARRAYCOUNT(AvailableDirections)] / 3;
int BranchLength = 2 + a_Noise.IntNoise3DInt(a_BlockX * a_Seq, a_BlockY * a_Seq, a_BlockZ * a_Seq) % 3;
GetLargeAppleTreeBranch(a_BlockX, a_BlockY + i, a_BlockZ, BranchLength, BranchStartDirection, BranchDirection, a_BlockY + Height, a_Noise, a_LogBlocks);
}
// Place leaves around each log block
for (auto itr : a_LogBlocks)
{
// Get the log's X and Z coordinates
int X = itr.ChunkX * 16 + itr.x;
int Z = itr.ChunkZ * 16 + itr.z;
a_OtherBlocks.push_back(sSetBlock(X, itr.y - 2, Z, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
PushCoordBlocks(X, itr.y - 2, Z, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
for (int y = -1; y <= 1; y++)
{
PushCoordBlocks (X, itr.y + y, Z, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
}
PushCoordBlocks(X, itr.y + 2, Z, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
a_OtherBlocks.push_back(sSetBlock(X, itr.y + 2, Z, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
}
// Trunk:
for (int i = 0; i < Height; i++)
{
a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE));
}
}
void GetLargeAppleTreeBranch(int a_BlockX, int a_BlockY, int a_BlockZ, int a_BranchLength, Vector3d a_StartDirection, Vector3d a_Direction, int a_TreeHeight, cNoise & a_Noise, sSetBlockVector & a_LogBlocks)
{
Vector3d CurrentPos = Vector3d(a_BlockX, a_BlockY, a_BlockZ);
Vector3d Direction = a_StartDirection;
for (int i = 0; i < a_BranchLength; i++)
{
CurrentPos += Direction;
if (CurrentPos.y >= a_TreeHeight)
{
return;
}
Direction -= a_Direction;
Direction.clamp(-1.0, 1.0);
a_LogBlocks.push_back(sSetBlock(FloorC(CurrentPos.x), FloorC(CurrentPos.y), FloorC(CurrentPos.z), E_BLOCK_LOG, GetLogMetaFromDirection(E_META_LOG_APPLE, Direction)));
}
}
NIBBLETYPE GetLogMetaFromDirection(NIBBLETYPE a_BlockMeta, Vector3d a_Direction)
{
a_Direction.abs();
if ((a_Direction.y > a_Direction.x) && (a_Direction.y > a_Direction.z))
{
return a_BlockMeta;
}
else if (a_Direction.x > a_Direction.z)
{
return a_BlockMeta + 4;
}
else
{
return a_BlockMeta + 8;
}
}

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"
@ -62,6 +62,12 @@ void GetSmallAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a
/// Generates an image of a large (branching) apple tree
void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
/// Generates a branch for a large apple tree
void GetLargeAppleTreeBranch(int a_BlockX, int a_BlockY, int a_BlockZ, int a_BranchLength, Vector3d a_StartDirection, Vector3d a_Direction, int a_TreeHeight, cNoise & a_Noise, sSetBlockVector & a_LogBlocks);
/// Returns the meta for a log from the given direction
NIBBLETYPE GetLogMetaFromDirection(NIBBLETYPE a_BlockMeta, Vector3d a_Direction);
/// Generates an image of a random birch tree
void GetBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);

View File

@ -0,0 +1,121 @@
// TwoHeights.cpp
// Implements the cTwoHeights class representing the terrain shape generator using two switched heightmaps
#include "Globals.h"
#include "TwoHeights.h"
#include "../Noise/InterpolNoise.h"
#include "HeiGen.h"
#include "../LinearUpscale.h"
#include "../IniFile.h"
class cTwoHeights:
public cTerrainShapeGen
{
typedef cTerrainShapeGen super;
public:
cTwoHeights(int a_Seed, cBiomeGenPtr a_BiomeGen):
m_Seed(a_Seed),
m_Choice(a_Seed),
m_HeightA(a_Seed + 1, a_BiomeGen),
m_HeightB(a_Seed + 2, a_BiomeGen)
{
}
// cTerrainShapeGen override:
virtual void GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape) override
{
// Generate the two heightmaps:
cChunkDef::HeightMap heightsA;
cChunkDef::HeightMap heightsB;
m_HeightA.GenHeightMap(a_ChunkX, a_ChunkZ, heightsA);
m_HeightB.GenHeightMap(a_ChunkX, a_ChunkZ, heightsB);
// Generate the choice noise:
NOISE_DATATYPE smallChoice[33 * 5 * 5];
NOISE_DATATYPE workspace[33 * 5 * 5];
NOISE_DATATYPE startX = 0;
NOISE_DATATYPE endX = 256 * m_FrequencyY;
NOISE_DATATYPE startY = a_ChunkX * cChunkDef::Width * m_FrequencyX;
NOISE_DATATYPE endY = (a_ChunkX * cChunkDef::Width + cChunkDef::Width + 1) * m_FrequencyX;
NOISE_DATATYPE startZ = a_ChunkZ * cChunkDef::Width * m_FrequencyZ;
NOISE_DATATYPE endZ = (a_ChunkZ * cChunkDef::Width + cChunkDef::Width + 1) * m_FrequencyZ;
m_Choice.Generate3D(smallChoice, 33, 5, 5, startX, endX, startY, endY, startZ, endZ, workspace);
NOISE_DATATYPE choice[257 * 17 * 17];
LinearUpscale3DArray(smallChoice, 33, 5, 5, choice, 8, 4, 4);
// Generate the shape:
int idxShape = 0;
for (int z = 0; z < cChunkDef::Width; z++)
{
for (int x = 0; x < cChunkDef::Width; x++)
{
int idxChoice = 257 * 17 * z + 257 * x;
NOISE_DATATYPE heightA = static_cast<NOISE_DATATYPE>(cChunkDef::GetHeight(heightsA, x, z));
NOISE_DATATYPE heightB = static_cast<NOISE_DATATYPE>(cChunkDef::GetHeight(heightsB, x, z));
for (int y = 0; y < cChunkDef::Height; y++)
{
int height = static_cast<int>(ClampedLerp(heightA, heightB, choice[idxChoice++]));
a_Shape[idxShape++] = (y < height) ? 1 : 0;
}
} // for x
} // for z
}
virtual void InitializeShapeGen(cIniFile & a_IniFile)
{
m_FrequencyX = static_cast<NOISE_DATATYPE>(a_IniFile.GetValueSetF("Generator", "TwoHeightsFrequencyX", 40));
m_FrequencyY = static_cast<NOISE_DATATYPE>(a_IniFile.GetValueSetF("Generator", "TwoHeightsFrequencyY", 40));
m_FrequencyZ = static_cast<NOISE_DATATYPE>(a_IniFile.GetValueSetF("Generator", "TwoHeightsFrequencyZ", 40));
// Initialize the two underlying height generators from an empty INI file:
cIniFile empty;
m_HeightA.InitializeHeightGen(empty);
m_HeightB.InitializeHeightGen(empty);
// Add the choice octaves:
NOISE_DATATYPE freq = 0.001f;
NOISE_DATATYPE ampl = 1;
for (int i = 0; i < 4; i++)
{
m_Choice.AddOctave(freq, ampl);
freq = freq * 2;
ampl = ampl / 2;
}
}
protected:
int m_Seed;
/** The noise used to decide between the two heightmaps. */
cOctavedNoise<cInterpolNoise<Interp5Deg>> m_Choice;
/** The first height generator. */
cHeiGenBiomal m_HeightA;
/** The second height generator. */
cHeiGenBiomal m_HeightB;
/** The base frequencies for m_Choice in each of the world axis directions. */
NOISE_DATATYPE m_FrequencyX, m_FrequencyY, m_FrequencyZ;
};
cTerrainShapeGenPtr CreateShapeGenTwoHeights(int a_Seed, cBiomeGenPtr a_BiomeGen)
{
return std::make_shared<cTwoHeights>(a_Seed, a_BiomeGen);
}

View File

@ -0,0 +1,23 @@
// TwoHeights.h
// Declares the function to create a new instance of the cTwoHeights terrain shape generator
#pragma once
#include "ComposableGenerator.h"
/** Creates and returns a new instance of the cTwoHeights terrain shape generator.
The instance must be Initialize()-d before it is used. */
extern cTerrainShapeGenPtr CreateShapeGenTwoHeights(int a_Seed, cBiomeGenPtr a_BiomeGen);

View File

@ -18,8 +18,8 @@
/*
How village generating works:
By descending from a cGridStructGen, a semi-random grid is generated. A village may be generated for each of
the grid's cells. Each cell checks the biomes in an entire chunk around it, only generating a village if all
By descending from a cGridStructGen, a semi-random (jitter) grid is generated. A village may be generated for each
of the grid's cells. Each cell checks the biomes in an entire chunk around it, only generating a village if all
biomes are village-friendly. If yes, the entire village structure is built for that cell. If not, the cell
is left village-less.
@ -125,7 +125,7 @@ public:
m_Noise(a_Seed),
m_MaxSize(a_MaxSize),
m_Density(a_Density),
m_Borders(a_OriginX - a_MaxSize, 0, a_OriginZ - a_MaxSize, a_OriginX + a_MaxSize, 255, a_OriginZ + a_MaxSize),
m_Borders(a_OriginX - a_MaxSize, 0, a_OriginZ - a_MaxSize, a_OriginX + a_MaxSize, cChunkDef::Height - 1, a_OriginZ + a_MaxSize),
m_Prefabs(a_Prefabs),
m_HeightGen(a_HeightGen),
m_RoadBlock(a_RoadBlock),

View File

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

View File

@ -160,7 +160,7 @@ void cMonster::TickPathFinding()
BLOCKTYPE BlockAtYP = m_World->GetBlock(gCrossCoords[i].x + PosX, PosY + 1, gCrossCoords[i].z + PosZ);
BLOCKTYPE BlockAtYPP = m_World->GetBlock(gCrossCoords[i].x + PosX, PosY + 2, gCrossCoords[i].z + PosZ);
int LowestY = FindFirstNonAirBlockPosition(gCrossCoords[i].x + PosX, gCrossCoords[i].z + PosZ);
BLOCKTYPE BlockAtLowestY = m_World->GetBlock(gCrossCoords[i].x + PosX, LowestY, gCrossCoords[i].z + PosZ);
BLOCKTYPE BlockAtLowestY = (LowestY >= cChunkDef::Height) ? E_BLOCK_AIR : m_World->GetBlock(gCrossCoords[i].x + PosX, LowestY, gCrossCoords[i].z + PosZ);
if (
(!cBlockInfo::IsSolid(BlockAtY)) &&
@ -453,7 +453,7 @@ int cMonster::FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ)
}
else
{
while (cBlockInfo::IsSolid(m_World->GetBlock((int)floor(a_PosX), PosY, (int)floor(a_PosZ))) && (PosY < cChunkDef::Height))
while ((PosY < cChunkDef::Height) && cBlockInfo::IsSolid(m_World->GetBlock((int)floor(a_PosX), PosY, (int)floor(a_PosZ))))
{
PosY++;
}

View File

@ -169,10 +169,12 @@ protected:
/** Stores if mobile is currently moving towards the ultimate, final destination */
bool m_bMovingToDestination;
/** Finds the first non-air block position (not the highest, as cWorld::GetHeight does)
If current Y is nonsolid, goes down to try to find a solid block, then returns that + 1
If current Y is solid, goes up to find first nonsolid block, and returns that */
/** Finds the lowest non-air block position (not the highest, as cWorld::GetHeight does)
If current Y is nonsolid, goes down to try to find a solid block, then returns that + 1
If current Y is solid, goes up to find first nonsolid block, and returns that.
If no suitable position is found, returns cChunkDef::Height. */
int FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ);
/** Returns if a monster can actually reach a given height by jumping or walking */
inline bool IsNextYPosReachable(int a_PosY)
{

View File

@ -98,3 +98,23 @@ void cPig::Tick(float a_Dt, cChunk & a_Chunk)
bool cPig::DoTakeDamage(TakeDamageInfo & a_TDI)
{
if (!super::DoTakeDamage(a_TDI))
{
return false;
}
if (a_TDI.DamageType == dtLightning)
{
Destroy();
m_World->SpawnMob(GetPosX(), GetPosY(), GetPosZ(), mtZombiePigman);
return true;
}
return true;
}

View File

@ -17,6 +17,9 @@ public:
CLASS_PROTODEF(cPig)
// cEntity overrides
virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
virtual void OnRightClicked(cPlayer & a_Player) override;
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;

View File

@ -37,6 +37,13 @@ bool cVillager::DoTakeDamage(TakeDamageInfo & a_TDI)
m_World->BroadcastEntityStatus(*this, esVillagerAngry);
}
}
if (a_TDI.DamageType == dtLightning)
{
Destroy();
m_World->SpawnMob(GetPosX(), GetPosY(), GetPosZ(), mtWitch);
return true;
}
return true;
}

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;
}
}
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);
if (ShouldFreeWorkspace)
{
delete[] a_Workspace;
a_Workspace = nullptr;
}
// 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;
}
}
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);
if (ShouldFreeWorkspace)
{
delete[] a_Workspace;
a_Workspace = nullptr;
}
// 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;
return a_T * a_T * a_T * (a_T * (a_T * 6 - 15) + 10);
}
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;
} ;
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;
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);
}
};
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;
} ;
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);
}

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

@ -0,0 +1,196 @@
// 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:
int ArrayCount = a_SizeX * a_SizeY;
{
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 (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:
int ArrayCount = a_SizeX * a_SizeY * a_SizeZ;
{
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 (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] = std::abs(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] = std::abs(a_Array[i]);
}
}
protected:
N m_Noise;
} ;

View File

@ -154,7 +154,7 @@ void cRoot::Start(void)
m_WebAdmin->Init();
LOGD("Loading settings...");
m_RankManager = new cRankManager();
m_RankManager.reset(new cRankManager());
m_RankManager->Initialize(m_MojangAPI);
m_CraftingRecipes = new cCraftingRecipes;
m_FurnaceRecipe = new cFurnaceRecipe();

View File

@ -86,7 +86,7 @@ public:
cPluginManager * GetPluginManager (void) { return m_PluginManager; } // tolua_export
cAuthenticator & GetAuthenticator (void) { return m_Authenticator; }
cMojangAPI & GetMojangAPI (void) { return m_MojangAPI; }
cRankManager * GetRankManager (void) { return m_RankManager; }
cRankManager * GetRankManager (void) { return m_RankManager.get(); }
/** Queues a console command for execution through the cServer class.
The command will be executed in the tick thread
@ -188,7 +188,9 @@ private:
cPluginManager * m_PluginManager;
cAuthenticator m_Authenticator;
cMojangAPI m_MojangAPI;
cRankManager * m_RankManager;
std::unique_ptr<cRankManager> m_RankManager;
cHTTPServer m_HTTPServer;
bool m_bStop;

View File

@ -93,6 +93,21 @@ public:
return x * a_Rhs.x + y * a_Rhs.y + z * a_Rhs.z;
}
inline void abs()
{
x = (x < 0) ? -x : x;
y = (y < 0) ? -y : y;
z = (z < 0) ? -z : z;
}
// We can't use a capital letter, because we wouldn't be able to call the normal Clamp function.
inline void clamp(T a_Min, T a_Max)
{
x = Clamp(x, a_Min, a_Max);
y = Clamp(y, a_Min, a_Max);
z = Clamp(z, a_Min, a_Max);
}
inline Vector3<T> Cross(const Vector3<T> & a_Rhs) const
{
return Vector3<T>(

View File

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

View File

@ -74,102 +74,137 @@ const int TIME_SPAWN_DIVISOR = 148;
////////////////////////////////////////////////////////////////////////////////
// cWorldLoadProgress:
// cSpawnPrepare:
/// A simple thread that displays the progress of world loading / saving in cWorld::InitializeSpawn()
class cWorldLoadProgress :
public cIsThread
/** Generates and lights the spawn area of the world. Runs as a separate thread. */
class cSpawnPrepare:
public cIsThread,
public cChunkCoordCallback
{
typedef cIsThread super;
public:
cWorldLoadProgress(cWorld * a_World) :
cIsThread("cWorldLoadProgress"),
m_World(a_World)
cSpawnPrepare(cWorld & a_World, int a_SpawnChunkX, int a_SpawnChunkZ, int a_PrepareDistance):
super("SpawnPrepare"),
m_World(a_World),
m_SpawnChunkX(a_SpawnChunkX),
m_SpawnChunkZ(a_SpawnChunkZ),
m_PrepareDistance(a_PrepareDistance),
m_MaxIdx(a_PrepareDistance * a_PrepareDistance),
m_NumPrepared(0),
m_LastReportTime(0),
m_LastReportChunkCount(0)
{
// Start the thread:
Start();
// Wait for start confirmation, so that the thread can be waited-upon after the constructor returns:
m_EvtStarted.Wait();
}
void Stop(void)
// cIsThread override:
virtual void Execute(void) override
{
m_ShouldTerminate = true;
Wait();
// Confirm thread start:
m_EvtStarted.Set();
// Queue the initial chunks:
m_MaxIdx = m_PrepareDistance * m_PrepareDistance;
int maxQueue = std::min(m_MaxIdx - 1, 100); // Number of chunks to queue at once
m_NextIdx = maxQueue;
m_LastReportTime = m_Timer.GetNowTime();
for (int i = 0; i < maxQueue; i++)
{
int chunkX, chunkZ;
DecodeChunkCoords(i, chunkX, chunkZ);
m_World.GetLightingThread().QueueChunk(chunkX, chunkZ, this);
} // for i
// Wait for the lighting thread to prepare everything. Event is set in the Call() callback:
m_EvtFinished.Wait();
}
protected:
cWorld & m_World;
int m_SpawnChunkX;
int m_SpawnChunkZ;
int m_PrepareDistance;
cWorld * m_World;
/** The index of the next chunk to be queued in the lighting thread. */
int m_NextIdx;
virtual void Execute(void) override
/** The maximum index of the prepared chunks. Queueing stops when m_NextIdx reaches this number. */
int m_MaxIdx;
/** Total number of chunks already finished preparing. Preparation finishes when this number reaches m_MaxIdx. */
int m_NumPrepared;
/** Event used to signal that the thread has started. */
cEvent m_EvtStarted;
/** Event used to signal that the preparation is finished. */
cEvent m_EvtFinished;
/** The timer used to report progress every second. */
cTimer m_Timer;
/** The timestamp of the last progress report emitted. */
long long m_LastReportTime;
/** Number of chunks prepared when the last progress report was emitted. */
int m_LastReportChunkCount;
// cChunkCoordCallback override:
virtual void Call(int a_ChunkX, int a_ChunkZ)
{
for (;;)
// Check if this was the last chunk:
m_NumPrepared += 1;
if (m_NumPrepared >= m_MaxIdx)
{
LOG("" SIZE_T_FMT " chunks to load, %d chunks to generate",
m_World->GetStorage().GetLoadQueueLength(),
m_World->GetGenerator().GetQueueLength()
);
m_EvtFinished.Set();
}
// Wait for 2 sec, but be "reasonably wakeable" when the thread is to finish
for (int i = 0; i < 20; i++)
{
cSleep::MilliSleep(100);
if (m_ShouldTerminate)
{
return;
}
}
} // for (-ever)
}
} ;
////////////////////////////////////////////////////////////////////////////////
// cWorldLightingProgress:
/// A simple thread that displays the progress of world lighting in cWorld::InitializeSpawn()
class cWorldLightingProgress :
public cIsThread
{
public:
cWorldLightingProgress(cLightingThread * a_Lighting) :
cIsThread("cWorldLightingProgress"),
m_Lighting(a_Lighting)
{
Start();
}
void Stop(void)
{
m_ShouldTerminate = true;
Wait();
}
protected:
cLightingThread * m_Lighting;
virtual void Execute(void) override
{
for (;;)
// Queue another chunk, if appropriate:
if (m_NextIdx < m_MaxIdx)
{
LOG("" SIZE_T_FMT " chunks remaining to light", m_Lighting->GetQueueLength()
);
int chunkX, chunkZ;
DecodeChunkCoords(m_NextIdx, chunkX, chunkZ);
m_World.GetLightingThread().QueueChunk(chunkX, chunkZ, this);
m_NextIdx += 1;
}
// Wait for 2 sec, but be "reasonably wakeable" when the thread is to finish
for (int i = 0; i < 20; i++)
{
cSleep::MilliSleep(100);
if (m_ShouldTerminate)
{
return;
}
}
} // for (-ever)
// Report progress every 1 second:
long long now = m_Timer.GetNowTime();
if (now - m_LastReportTime > 1000)
{
float percentDone = static_cast<float>(m_NumPrepared * 100) / m_MaxIdx;
float chunkSpeed = static_cast<float>((m_NumPrepared - m_LastReportChunkCount) * 1000) / (now - m_LastReportTime);
LOG("Preparing spawn (%s): %.02f%% done (%d chunks out of %d; %.02f chunks / sec)",
m_World.GetName().c_str(), percentDone, m_NumPrepared, m_MaxIdx, chunkSpeed
);
m_LastReportTime = now;
m_LastReportChunkCount = m_NumPrepared;
}
}
} ;
/** Decodes the index into chunk coords. Provides the specific chunk ordering. */
void DecodeChunkCoords(int a_Idx, int & a_ChunkX, int & a_ChunkZ)
{
// A zigzag pattern from the top to bottom, each row alternating between forward-x and backward-x:
int z = a_Idx / m_PrepareDistance;
int x = a_Idx % m_PrepareDistance;
if ((z & 1) == 0)
{
// Reverse every second row:
x = m_PrepareDistance - 1 - x;
}
a_ChunkZ = m_SpawnChunkZ + z - m_PrepareDistance / 2;
a_ChunkX = m_SpawnChunkX + x - m_PrepareDistance / 2;
}
};
@ -433,52 +468,8 @@ void cWorld::InitializeSpawn(void)
int ViewDist = IniFile.GetValueSetI("SpawnPosition", "PregenerateDistance", DefaultViewDist);
IniFile.WriteFile(m_IniFileName);
LOG("Preparing spawn area in world \"%s\", %d x %d chunks, total %d chunks...", m_WorldName.c_str(), ViewDist, ViewDist, ViewDist * ViewDist);
for (int x = 0; x < ViewDist; x++)
{
for (int z = 0; z < ViewDist; z++)
{
m_ChunkMap->TouchChunk(x + ChunkX-(ViewDist - 1) / 2, z + ChunkZ-(ViewDist - 1) / 2); // Queue the chunk in the generator / loader
}
}
{
// Display progress during this process:
cWorldLoadProgress Progress(this);
// Wait for the loader to finish loading
m_Storage.WaitForLoadQueueEmpty();
// Wait for the generator to finish generating
m_Generator.WaitForQueueEmpty();
// Wait for the loader to finish saving
m_Storage.WaitForSaveQueueEmpty();
Progress.Stop();
}
// Light all chunks that have been newly generated:
LOG("Lighting spawn area in world \"%s\"...", m_WorldName.c_str());
for (int x = 0; x < ViewDist; x++)
{
int ChX = x + ChunkX-(ViewDist - 1) / 2;
for (int z = 0; z < ViewDist; z++)
{
int ChZ = z + ChunkZ-(ViewDist - 1) / 2;
if (!m_ChunkMap->IsChunkLighted(ChX, ChZ))
{
m_Lighting.QueueChunk(ChX, ChZ); // Queue the chunk in the lighting thread
}
} // for z
} // for x
{
cWorldLightingProgress Progress(&m_Lighting);
m_Lighting.WaitForQueueEmpty();
Progress.Stop();
}
cSpawnPrepare prep(*this, ChunkX, ChunkZ, ViewDist);
prep.Wait();
#ifdef TEST_LINEBLOCKTRACER
// DEBUG: Test out the cLineBlockTracer class by tracing a few lines:
@ -741,28 +732,32 @@ void cWorld::InitialiseGeneratorDefaults(cIniFile & a_IniFile)
{
case dimEnd:
{
a_IniFile.GetValueSet("Generator", "BiomeGen", "Constant");
a_IniFile.GetValueSet("Generator", "ConstantBiome", "End");
a_IniFile.GetValueSet("Generator", "HeightGen", "Biomal");
a_IniFile.GetValueSet("Generator", "Generator", "Composable");
a_IniFile.GetValueSet("Generator", "BiomeGen", "Constant");
a_IniFile.GetValueSet("Generator", "ConstantBiome", "End");
a_IniFile.GetValueSet("Generator", "ShapeGen", "End");
a_IniFile.GetValueSet("Generator", "CompositionGen", "End");
break;
}
case dimOverworld:
{
a_IniFile.GetValueSet("Generator", "BiomeGen", "MultiStepMap");
a_IniFile.GetValueSet("Generator", "HeightGen", "DistortedHeightmap");
a_IniFile.GetValueSet("Generator", "CompositionGen", "DistortedHeightmap");
a_IniFile.GetValueSet("Generator", "Finishers", "Ravines, WormNestCaves, WaterLakes, WaterSprings, LavaLakes, LavaSprings, OreNests, Mineshafts, Trees, SprinkleFoliage, Ice, Snow, Lilypads, BottomLava, DeadBushes, PreSimulator");
a_IniFile.GetValueSet("Generator", "Generator", "Composable");
a_IniFile.GetValueSet("Generator", "BiomeGen", "Grown");
a_IniFile.GetValueSet("Generator", "ShapeGen", "BiomalNoise3D");
a_IniFile.GetValueSet("Generator", "CompositionGen", "Biomal");
a_IniFile.GetValueSet("Generator", "Finishers", "Ravines, WormNestCaves, WaterLakes, WaterSprings, LavaLakes, LavaSprings, OreNests, Mineshafts, Trees, Villages, SprinkleFoliage, Ice, Snow, Lilypads, BottomLava, DeadBushes, PreSimulator");
break;
}
case dimNether:
{
a_IniFile.GetValueSet("Generator", "BiomeGen", "Constant");
a_IniFile.GetValueSet("Generator", "ConstantBiome", "Nether");
a_IniFile.GetValueSet("Generator", "HeightGen", "Flat");
a_IniFile.GetValueSet("Generator", "FlatHeight", "128");
a_IniFile.GetValueSet("Generator", "CompositionGen", "Nether");
a_IniFile.GetValueSet("Generator", "Finishers", "WormNestCaves, BottomLava, LavaSprings, NetherClumpFoliage, NetherForts, PreSimulator");
a_IniFile.GetValueSet("Generator", "Generator", "Composable");
a_IniFile.GetValueSet("Generator", "BiomeGen", "Constant");
a_IniFile.GetValueSet("Generator", "ConstantBiome", "Nether");
a_IniFile.GetValueSet("Generator", "ShapeGen", "HeightMap");
a_IniFile.GetValueSet("Generator", "HeightGen", "Flat");
a_IniFile.GetValueSet("Generator", "FlatHeight", "128");
a_IniFile.GetValueSet("Generator", "CompositionGen", "Nether");
a_IniFile.GetValueSet("Generator", "Finishers", "WormNestCaves, BottomLava, LavaSprings, NetherClumpFoliage, NetherForts, PreSimulator");
a_IniFile.GetValueSet("Generator", "BottomLavaHeight", "30");
break;
}

View File

@ -696,6 +696,8 @@ public:
inline size_t GetStorageLoadQueueLength(void) { return m_Storage.GetLoadQueueLength(); } // tolua_export
inline size_t GetStorageSaveQueueLength(void) { return m_Storage.GetSaveQueueLength(); } // tolua_export
cLightingThread & GetLightingThread(void) { return m_Lighting; }
void InitializeSpawn(void);
/** Starts threads that belong to this world */

View File

@ -1857,9 +1857,10 @@ void cWSSAnvil::LoadArrowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
int InBlockZIdx = a_NBT.FindChildByName(a_TagIdx, "zTile");
if ((InBlockXIdx > 0) && (InBlockYIdx > 0) && (InBlockZIdx > 0))
{
if (a_NBT.GetType(InBlockXIdx) == a_NBT.GetType(InBlockYIdx) == a_NBT.GetType(InBlockZIdx))
eTagType typeX = a_NBT.GetType(InBlockXIdx);
if ((typeX == a_NBT.GetType(InBlockYIdx)) && (typeX == a_NBT.GetType(InBlockZIdx)))
{
switch (a_NBT.GetType(InBlockXIdx))
switch (typeX)
{
case TAG_Int:
{
@ -1873,6 +1874,11 @@ void cWSSAnvil::LoadArrowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
Arrow->SetBlockHit(Vector3i((int)a_NBT.GetShort(InBlockXIdx), (int)a_NBT.GetShort(InBlockYIdx), (int)a_NBT.GetShort(InBlockZIdx)));
break;
}
default:
{
// No hit block, the arrow is still flying?
break;
}
}
}
}