Merge branch 'master' into MobSpawner
This commit is contained in:
commit
648fee1a08
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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))
|
||||
|
@ -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);
|
||||
|
@ -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++
|
||||
)
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include "Enchantments.h"
|
||||
#include "WorldStorage/FastNBT.h"
|
||||
#include "FastRandom.h"
|
||||
#include "Noise.h"
|
||||
#include "Noise/Noise.h"
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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"
|
||||
|
||||
|
||||
|
@ -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})
|
||||
|
@ -13,7 +13,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "GridStructGen.h"
|
||||
#include "../Noise.h"
|
||||
|
||||
|
||||
|
||||
|
@ -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,
|
||||
|
@ -29,10 +29,17 @@ 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,8 +95,11 @@ 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(
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
586
src/Generating/CompoGenBiomal.cpp
Normal file
586
src/Generating/CompoGenBiomal.cpp
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
|
21
src/Generating/CompoGenBiomal.h
Normal file
21
src/Generating/CompoGenBiomal.h
Normal 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);
|
||||
|
||||
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
49
src/Generating/CompositedHeiGen.h
Normal file
49
src/Generating/CompositedHeiGen.h
Normal 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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
} ;
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
} ;
|
||||
|
@ -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);
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
|
||||
#include "ComposableGenerator.h"
|
||||
#include "../Noise.h"
|
||||
#include "../Noise/Noise.h"
|
||||
#include "../ProbabDistrib.h"
|
||||
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "ComposableGenerator.h"
|
||||
#include "../Noise.h"
|
||||
#include "../Noise/Noise.h"
|
||||
|
||||
|
||||
|
||||
|
@ -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,15 +848,14 @@ 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);
|
||||
}
|
||||
|
||||
// Read the settings:
|
||||
res->InitializeHeightGen(a_IniFile);
|
||||
|
||||
return cTerrainHeightGenPtr(res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
@ -137,7 +170,11 @@ public:
|
||||
m_BiomeGen(a_BiomeGen)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// 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];
|
||||
@ -154,11 +191,8 @@ protected:
|
||||
float m_BaseHeight;
|
||||
} ;
|
||||
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);
|
||||
} ;
|
||||
|
||||
|
@ -10,7 +10,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "GridStructGen.h"
|
||||
#include "../Noise.h"
|
||||
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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,21 +81,18 @@ 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;
|
||||
cOctavedNoise<cInterpolNoise<Interp5Deg>> m_BaseNoise;
|
||||
|
||||
/** Block height of the sealevel, used for composing the terrain. */
|
||||
int m_SeaLevel;
|
||||
|
||||
/** 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. */
|
||||
NOISE_DATATYPE m_HeightAmplification;
|
||||
@ -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); }
|
||||
} ;
|
||||
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
||||
|
||||
|
@ -10,7 +10,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "GridStructGen.h"
|
||||
#include "../Noise.h"
|
||||
|
||||
|
||||
|
||||
|
145
src/Generating/ShapeGen.cpp
Normal file
145
src/Generating/ShapeGen.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
} ;
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
121
src/Generating/TwoHeights.cpp
Normal file
121
src/Generating/TwoHeights.cpp
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
23
src/Generating/TwoHeights.h
Normal file
23
src/Generating/TwoHeights.h
Normal 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);
|
||||
|
||||
|
||||
|
||||
|
@ -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),
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include "Globals.h"
|
||||
#include "ItemGrid.h"
|
||||
#include "Items/ItemHandler.h"
|
||||
#include "Noise.h"
|
||||
#include "Noise/Noise.h"
|
||||
|
||||
|
||||
|
||||
|
@ -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++;
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
21
src/Noise/CMakeLists.txt
Normal 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
524
src/Noise/InterpolNoise.h
Normal 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;
|
||||
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "Noise.h"
|
||||
#include "OSSupport/Timer.h"
|
||||
|
||||
#define FAST_FLOOR(x) (((x) < 0) ? (((int)x) - 1) : ((int)x))
|
||||
|
||||
@ -9,10 +10,110 @@
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
/** cImprovedPerlin noise test suite:
|
||||
- Generate a rather large 2D and 3D noise array and output it to a file
|
||||
- Compare performance of cCubicNoise and cImprovedNoise, both in single-value and 3D-array usages */
|
||||
static class cImprovedPerlinNoiseTest
|
||||
{
|
||||
public:
|
||||
cImprovedPerlinNoiseTest(void)
|
||||
{
|
||||
printf("Performing Improved Perlin Noise tests...\n");
|
||||
TestImage();
|
||||
TestSpeed();
|
||||
TestSpeedArr();
|
||||
printf("Improved Perlin Noise tests complete.\n");
|
||||
}
|
||||
|
||||
|
||||
/** Tests the noise by generating 2D and 3D images and dumping them to files. */
|
||||
void TestImage(void)
|
||||
{
|
||||
static const int SIZE_X = 256;
|
||||
static const int SIZE_Y = 256;
|
||||
static const int SIZE_Z = 16;
|
||||
|
||||
cImprovedNoise noise(1);
|
||||
std::unique_ptr<NOISE_DATATYPE[]> arr(new NOISE_DATATYPE[SIZE_X * SIZE_Y * SIZE_Z]);
|
||||
noise.Generate3D(arr.get(), SIZE_X, SIZE_Y, SIZE_Z, 0, 14, 0, 14, 0, 14);
|
||||
Debug3DNoise(arr.get(), SIZE_X, SIZE_Y, SIZE_Z, "ImprovedPerlinNoiseTest3D", 128);
|
||||
noise.Generate2D(arr.get(), SIZE_X, SIZE_Y, 0, 14, 15, 28);
|
||||
Debug2DNoise(arr.get(), SIZE_X, SIZE_Y, "ImprovedPerlinNoiseTest2D", 128);
|
||||
}
|
||||
|
||||
|
||||
/** Tests the speeds of cImprovedPerlin and cCubicNoise when generating individual values. */
|
||||
void TestSpeed(void)
|
||||
{
|
||||
cImprovedNoise improvedNoise(1);
|
||||
cNoise noise(1);
|
||||
cTimer timer;
|
||||
|
||||
// Measure the improvedNoise:
|
||||
NOISE_DATATYPE sum = 0;
|
||||
long long start = timer.GetNowTime();
|
||||
for (int i = 0; i < 100000000; i++)
|
||||
{
|
||||
sum += improvedNoise.GetValueAt(i, 0, -i);
|
||||
}
|
||||
long long finish = timer.GetNowTime();
|
||||
printf("cImprovedNoise took %.2f seconds; total is %f.\n", static_cast<float>(finish - start) / 1000.0f, sum);
|
||||
|
||||
// Measure the cubicNoise:
|
||||
sum = 0;
|
||||
start = timer.GetNowTime();
|
||||
for (int i = 0; i < 100000000; i++)
|
||||
{
|
||||
sum += noise.IntNoise3D(i, 0, -i);
|
||||
}
|
||||
finish = timer.GetNowTime();
|
||||
printf("cCubicNoise took %.2f seconds; total is %f.\n", static_cast<float>(finish - start) / 1000.0f, sum);
|
||||
}
|
||||
|
||||
|
||||
/** Tests the speeds of cImprovedPerlin and cCubicNoise when generating arrays. */
|
||||
void TestSpeedArr(void)
|
||||
{
|
||||
static const int SIZE_X = 256;
|
||||
static const int SIZE_Y = 256;
|
||||
static const int SIZE_Z = 16;
|
||||
|
||||
std::unique_ptr<NOISE_DATATYPE[]> arr(new NOISE_DATATYPE[SIZE_X * SIZE_Y * SIZE_Z]);
|
||||
cTimer timer;
|
||||
cImprovedNoise improvedNoise(1);
|
||||
cCubicNoise cubicNoise(1);
|
||||
|
||||
// Measure the improvedNoise:
|
||||
long long start = timer.GetNowTime();
|
||||
for (int i = 0; i < 40; i++)
|
||||
{
|
||||
improvedNoise.Generate3D(arr.get(), SIZE_X, SIZE_Y, SIZE_Z, 0, 14, 0, 14, 0, 14);
|
||||
}
|
||||
long long finish = timer.GetNowTime();
|
||||
printf("cImprovedNoise(arr) took %.2f seconds.\n", static_cast<float>(finish - start) / 1000.0f);
|
||||
|
||||
// Measure the cubicNoise:
|
||||
start = timer.GetNowTime();
|
||||
for (int i = 0; i < 40; i++)
|
||||
{
|
||||
cubicNoise.Generate3D(arr.get(), SIZE_X, SIZE_Y, SIZE_Z, 0, 14, 0, 14, 0, 14);
|
||||
}
|
||||
finish = timer.GetNowTime();
|
||||
printf("cCubicNoise(arr) took %.2f seconds.\n", static_cast<float>(finish - start) / 1000.0f);
|
||||
}
|
||||
} g_Test;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Globals:
|
||||
|
||||
void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase)
|
||||
void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase, NOISE_DATATYPE a_Coeff)
|
||||
{
|
||||
const int BUF_SIZE = 512;
|
||||
ASSERT(a_SizeX <= BUF_SIZE); // Just stretch it, if needed
|
||||
@ -29,7 +130,7 @@ void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int
|
||||
unsigned char buf[BUF_SIZE];
|
||||
for (int x = 0; x < a_SizeX; x++)
|
||||
{
|
||||
buf[x] = (unsigned char)(std::min(255, std::max(0, (int)(128 + 32 * a_Noise[idx++]))));
|
||||
buf[x] = static_cast<unsigned char>(Clamp((int)(128 + a_Coeff * a_Noise[idx++]), 0, 255));
|
||||
}
|
||||
f1.Write(buf, a_SizeX);
|
||||
} // for y
|
||||
@ -50,7 +151,7 @@ void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int
|
||||
unsigned char buf[BUF_SIZE];
|
||||
for (int x = 0; x < a_SizeX; x++)
|
||||
{
|
||||
buf[x] = (unsigned char)(std::min(255, std::max(0, (int)(128 + 32 * a_Noise[idx++]))));
|
||||
buf[x] = static_cast<unsigned char>(Clamp((int)(128 + a_Coeff * a_Noise[idx++]), 0, 255));
|
||||
}
|
||||
f2.Write(buf, a_SizeX);
|
||||
} // for z
|
||||
@ -65,7 +166,7 @@ void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int
|
||||
|
||||
|
||||
|
||||
void Debug2DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, const AString & a_FileNameBase)
|
||||
void Debug2DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, const AString & a_FileNameBase, NOISE_DATATYPE a_Coeff)
|
||||
{
|
||||
const int BUF_SIZE = 512;
|
||||
ASSERT(a_SizeX <= BUF_SIZE); // Just stretch it, if needed
|
||||
@ -79,7 +180,7 @@ void Debug2DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, cons
|
||||
unsigned char buf[BUF_SIZE];
|
||||
for (int x = 0; x < a_SizeX; x++)
|
||||
{
|
||||
buf[x] = (unsigned char)(std::min(255, std::max(0, (int)(128 + 32 * a_Noise[idx++]))));
|
||||
buf[x] = static_cast<unsigned char>(Clamp((int)(128 + a_Coeff * a_Noise[idx++]), 0, 255));
|
||||
}
|
||||
f1.Write(buf, a_SizeX);
|
||||
} // for y
|
||||
@ -594,13 +695,6 @@ NOISE_DATATYPE cNoise::CubicNoise3D(NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y, NOIS
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cCubicNoise:
|
||||
|
||||
#ifdef _DEBUG
|
||||
int cCubicNoise::m_NumSingleX = 0;
|
||||
int cCubicNoise::m_NumSingleXY = 0;
|
||||
int cCubicNoise::m_NumSingleY = 0;
|
||||
int cCubicNoise::m_NumCalls = 0;
|
||||
#endif // _DEBUG
|
||||
|
||||
cCubicNoise::cCubicNoise(int a_Seed) :
|
||||
m_Noise(a_Seed)
|
||||
{
|
||||
@ -639,23 +733,6 @@ void cCubicNoise::Generate2D(
|
||||
|
||||
Cell.InitWorkRnds(FloorX[0], FloorY[0]);
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Statistics on the noise-space coords:
|
||||
if (NumSameX == 1)
|
||||
{
|
||||
m_NumSingleX++;
|
||||
if (NumSameY == 1)
|
||||
{
|
||||
m_NumSingleXY++;
|
||||
}
|
||||
}
|
||||
if (NumSameY == 1)
|
||||
{
|
||||
m_NumSingleY++;
|
||||
}
|
||||
m_NumCalls++;
|
||||
#endif // _DEBUG
|
||||
|
||||
// Calculate query values using Cell:
|
||||
int FromY = 0;
|
||||
for (int y = 0; y < NumSameY; y++)
|
||||
@ -792,341 +869,161 @@ void cCubicNoise::CalcFloorFrac(
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cPerlinNoise:
|
||||
// cImprovedNoise:
|
||||
|
||||
cPerlinNoise::cPerlinNoise(void) :
|
||||
m_Seed(0)
|
||||
cImprovedNoise::cImprovedNoise(int a_Seed)
|
||||
{
|
||||
// Initialize the permutations with identity:
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
m_Perm[i] = i;
|
||||
}
|
||||
|
||||
// Randomize the permutation table - swap each element with a random other element:
|
||||
cNoise noise(a_Seed);
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
int rnd = (noise.IntNoise1DInt(i) / 7) % 256;
|
||||
std::swap(m_Perm[i], m_Perm[rnd]);
|
||||
}
|
||||
|
||||
// Copy the lower 256 entries into upper 256 entries:
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
m_Perm[i + 256] = m_Perm[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cPerlinNoise::cPerlinNoise(int a_Seed) :
|
||||
m_Seed(a_Seed)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPerlinNoise::SetSeed(int a_Seed)
|
||||
{
|
||||
m_Seed = a_Seed;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPerlinNoise::AddOctave(float a_Frequency, float a_Amplitude)
|
||||
{
|
||||
m_Octaves.push_back(cOctave(m_Seed * ((int)m_Octaves.size() + 4) * 4 + 1024, a_Frequency, a_Amplitude));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPerlinNoise::Generate2D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
|
||||
int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
|
||||
NOISE_DATATYPE * a_Workspace ///< Workspace that this function can use and trash
|
||||
void cImprovedNoise::Generate2D(
|
||||
NOISE_DATATYPE * a_Array,
|
||||
int a_SizeX, int a_SizeY,
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX,
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY
|
||||
) const
|
||||
{
|
||||
if (m_Octaves.empty())
|
||||
size_t idx = 0;
|
||||
for (int y = 0; y < a_SizeY; y++)
|
||||
{
|
||||
// No work to be done
|
||||
ASSERT(!"Perlin: No octaves to generate!");
|
||||
return;
|
||||
}
|
||||
|
||||
bool ShouldFreeWorkspace = (a_Workspace == nullptr);
|
||||
int ArrayCount = a_SizeX * a_SizeY;
|
||||
if (ShouldFreeWorkspace)
|
||||
{
|
||||
a_Workspace = new NOISE_DATATYPE[ArrayCount];
|
||||
}
|
||||
|
||||
// Generate the first octave directly into array:
|
||||
const cOctave & FirstOctave = m_Octaves.front();
|
||||
|
||||
FirstOctave.m_Noise.Generate2D(
|
||||
a_Workspace, a_SizeX, a_SizeY,
|
||||
a_StartX * FirstOctave.m_Frequency, a_EndX * FirstOctave.m_Frequency,
|
||||
a_StartY * FirstOctave.m_Frequency, a_EndY * FirstOctave.m_Frequency
|
||||
);
|
||||
NOISE_DATATYPE Amplitude = FirstOctave.m_Amplitude;
|
||||
for (int i = 0; i < ArrayCount; i++)
|
||||
{
|
||||
a_Array[i] = a_Workspace[i] * Amplitude;
|
||||
}
|
||||
|
||||
// Add each octave:
|
||||
for (cOctaves::const_iterator itr = m_Octaves.begin() + 1, end = m_Octaves.end(); itr != end; ++itr)
|
||||
{
|
||||
// Generate cubic noise for the octave:
|
||||
itr->m_Noise.Generate2D(
|
||||
a_Workspace, a_SizeX, a_SizeY,
|
||||
a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency,
|
||||
a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency
|
||||
);
|
||||
// Add the cubic noise into the output:
|
||||
NOISE_DATATYPE Amplitude = itr->m_Amplitude;
|
||||
for (int i = 0; i < ArrayCount; i++)
|
||||
NOISE_DATATYPE ratioY = static_cast<NOISE_DATATYPE>(y) / (a_SizeY - 1);
|
||||
NOISE_DATATYPE noiseY = Lerp(a_StartY, a_EndY, ratioY);
|
||||
int noiseYInt = FAST_FLOOR(noiseY);
|
||||
int yCoord = noiseYInt & 255;
|
||||
NOISE_DATATYPE noiseYFrac = noiseY - noiseYInt;
|
||||
NOISE_DATATYPE fadeY = Fade(noiseYFrac);
|
||||
for (int x = 0; x < a_SizeX; x++)
|
||||
{
|
||||
a_Array[i] += a_Workspace[i] * Amplitude;
|
||||
}
|
||||
}
|
||||
|
||||
if (ShouldFreeWorkspace)
|
||||
{
|
||||
delete[] a_Workspace;
|
||||
a_Workspace = nullptr;
|
||||
}
|
||||
NOISE_DATATYPE ratioX = static_cast<NOISE_DATATYPE>(x) / (a_SizeX - 1);
|
||||
NOISE_DATATYPE noiseX = Lerp(a_StartX, a_EndX, ratioX);
|
||||
int noiseXInt = FAST_FLOOR(noiseX);
|
||||
int xCoord = noiseXInt & 255;
|
||||
NOISE_DATATYPE noiseXFrac = noiseX - noiseXInt;
|
||||
NOISE_DATATYPE fadeX = Fade(noiseXFrac);
|
||||
|
||||
// Hash the coordinates:
|
||||
int A = m_Perm[xCoord] + yCoord;
|
||||
int AA = m_Perm[A];
|
||||
int AB = m_Perm[A + 1];
|
||||
int B = m_Perm[xCoord + 1] + yCoord;
|
||||
int BA = m_Perm[B];
|
||||
int BB = m_Perm[B + 1];
|
||||
|
||||
// Lerp the gradients:
|
||||
a_Array[idx++] = Lerp(
|
||||
Lerp(Grad(m_Perm[AA], noiseXFrac, noiseYFrac, 0), Grad(m_Perm[BA], noiseXFrac - 1, noiseYFrac, 0), fadeX),
|
||||
Lerp(Grad(m_Perm[AB], noiseXFrac, noiseYFrac - 1, 0), Grad(m_Perm[BB], noiseXFrac - 1, noiseYFrac - 1, 0), fadeX),
|
||||
fadeY
|
||||
);
|
||||
} // for x
|
||||
} // for y
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPerlinNoise::Generate3D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
|
||||
int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
|
||||
NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction
|
||||
NOISE_DATATYPE * a_Workspace ///< Workspace that this function can use and trash
|
||||
void cImprovedNoise::Generate3D(
|
||||
NOISE_DATATYPE * a_Array,
|
||||
int a_SizeX, int a_SizeY, int a_SizeZ,
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX,
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY,
|
||||
NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ
|
||||
) const
|
||||
{
|
||||
if (m_Octaves.empty())
|
||||
size_t idx = 0;
|
||||
for (int z = 0; z < a_SizeZ; z++)
|
||||
{
|
||||
// No work to be done
|
||||
ASSERT(!"Perlin: No octaves to generate!");
|
||||
return;
|
||||
}
|
||||
|
||||
bool ShouldFreeWorkspace = (a_Workspace == nullptr);
|
||||
int ArrayCount = a_SizeX * a_SizeY * a_SizeZ;
|
||||
if (ShouldFreeWorkspace)
|
||||
{
|
||||
a_Workspace = new NOISE_DATATYPE[ArrayCount];
|
||||
}
|
||||
|
||||
// Generate the first octave directly into array:
|
||||
const cOctave & FirstOctave = m_Octaves.front();
|
||||
|
||||
FirstOctave.m_Noise.Generate3D(
|
||||
a_Workspace, a_SizeX, a_SizeY, a_SizeZ,
|
||||
a_StartX * FirstOctave.m_Frequency, a_EndX * FirstOctave.m_Frequency,
|
||||
a_StartY * FirstOctave.m_Frequency, a_EndY * FirstOctave.m_Frequency,
|
||||
a_StartZ * FirstOctave.m_Frequency, a_EndZ * FirstOctave.m_Frequency
|
||||
);
|
||||
NOISE_DATATYPE Amplitude = FirstOctave.m_Amplitude;
|
||||
for (int i = 0; i < ArrayCount; i++)
|
||||
{
|
||||
a_Array[i] = a_Workspace[i] * Amplitude;
|
||||
}
|
||||
|
||||
// Add each octave:
|
||||
for (cOctaves::const_iterator itr = m_Octaves.begin() + 1, end = m_Octaves.end(); itr != end; ++itr)
|
||||
{
|
||||
// Generate cubic noise for the octave:
|
||||
itr->m_Noise.Generate3D(
|
||||
a_Workspace, a_SizeX, a_SizeY, a_SizeZ,
|
||||
a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency,
|
||||
a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency,
|
||||
a_StartZ * itr->m_Frequency, a_EndZ * itr->m_Frequency
|
||||
);
|
||||
// Add the cubic noise into the output:
|
||||
NOISE_DATATYPE Amplitude = itr->m_Amplitude;
|
||||
for (int i = 0; i < ArrayCount; i++)
|
||||
NOISE_DATATYPE ratioZ = static_cast<NOISE_DATATYPE>(z) / (a_SizeZ - 1);
|
||||
NOISE_DATATYPE noiseZ = Lerp(a_StartZ, a_EndZ, ratioZ);
|
||||
int noiseZInt = FAST_FLOOR(noiseZ);
|
||||
int zCoord = noiseZInt & 255;
|
||||
NOISE_DATATYPE noiseZFrac = noiseZ - noiseZInt;
|
||||
NOISE_DATATYPE fadeZ = Fade(noiseZFrac);
|
||||
for (int y = 0; y < a_SizeY; y++)
|
||||
{
|
||||
a_Array[i] += a_Workspace[i] * Amplitude;
|
||||
}
|
||||
}
|
||||
|
||||
if (ShouldFreeWorkspace)
|
||||
{
|
||||
delete[] a_Workspace;
|
||||
a_Workspace = nullptr;
|
||||
}
|
||||
NOISE_DATATYPE ratioY = static_cast<NOISE_DATATYPE>(y) / (a_SizeY - 1);
|
||||
NOISE_DATATYPE noiseY = Lerp(a_StartY, a_EndY, ratioY);
|
||||
int noiseYInt = FAST_FLOOR(noiseY);
|
||||
int yCoord = noiseYInt & 255;
|
||||
NOISE_DATATYPE noiseYFrac = noiseY - noiseYInt;
|
||||
NOISE_DATATYPE fadeY = Fade(noiseYFrac);
|
||||
for (int x = 0; x < a_SizeX; x++)
|
||||
{
|
||||
NOISE_DATATYPE ratioX = static_cast<NOISE_DATATYPE>(x) / (a_SizeX - 1);
|
||||
NOISE_DATATYPE noiseX = Lerp(a_StartX, a_EndX, ratioX);
|
||||
int noiseXInt = FAST_FLOOR(noiseX);
|
||||
int xCoord = noiseXInt & 255;
|
||||
NOISE_DATATYPE noiseXFrac = noiseX - noiseXInt;
|
||||
NOISE_DATATYPE fadeX = Fade(noiseXFrac);
|
||||
|
||||
// Hash the coordinates:
|
||||
int A = m_Perm[xCoord] + yCoord;
|
||||
int AA = m_Perm[A] + zCoord;
|
||||
int AB = m_Perm[A + 1] + zCoord;
|
||||
int B = m_Perm[xCoord + 1] + yCoord;
|
||||
int BA = m_Perm[B] + zCoord;
|
||||
int BB = m_Perm[B + 1] + zCoord;
|
||||
|
||||
// Lerp the gradients:
|
||||
// TODO: This may be optimized by swapping the coords and recalculating most lerps only "once every x"
|
||||
a_Array[idx++] = Lerp(
|
||||
Lerp(
|
||||
Lerp(Grad(m_Perm[AA], noiseXFrac, noiseYFrac, noiseZFrac), Grad(m_Perm[BA], noiseXFrac - 1, noiseYFrac, noiseZFrac), fadeX),
|
||||
Lerp(Grad(m_Perm[AB], noiseXFrac, noiseYFrac - 1, noiseZFrac), Grad(m_Perm[BB], noiseXFrac - 1, noiseYFrac - 1, noiseZFrac), fadeX),
|
||||
fadeY
|
||||
),
|
||||
Lerp(
|
||||
Lerp(Grad(m_Perm[AA + 1], noiseXFrac, noiseYFrac, noiseZFrac - 1), Grad(m_Perm[BA + 1], noiseXFrac - 1, noiseYFrac, noiseZFrac - 1), fadeX),
|
||||
Lerp(Grad(m_Perm[AB + 1], noiseXFrac, noiseYFrac - 1, noiseZFrac - 1), Grad(m_Perm[BB + 1], noiseXFrac - 1, noiseYFrac - 1, noiseZFrac - 1), fadeX),
|
||||
fadeY
|
||||
),
|
||||
fadeZ
|
||||
);
|
||||
} // for x
|
||||
} // for y
|
||||
} // for z
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cRidgedMultiNoise:
|
||||
|
||||
cRidgedMultiNoise::cRidgedMultiNoise(void) :
|
||||
m_Seed(0)
|
||||
NOISE_DATATYPE cImprovedNoise::GetValueAt(int a_X, int a_Y, int a_Z)
|
||||
{
|
||||
// Hash the coordinates:
|
||||
a_X = a_X & 255;
|
||||
a_Y = a_Y & 255;
|
||||
a_Z = a_Z & 255;
|
||||
int A = m_Perm[a_X] + a_Y;
|
||||
int AA = m_Perm[A] + a_Z;
|
||||
|
||||
return Grad(m_Perm[AA], 1, 1, 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cRidgedMultiNoise::cRidgedMultiNoise(int a_Seed) :
|
||||
m_Seed(a_Seed)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cRidgedMultiNoise::SetSeed(int a_Seed)
|
||||
{
|
||||
m_Seed = a_Seed;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cRidgedMultiNoise::AddOctave(float a_Frequency, float a_Amplitude)
|
||||
{
|
||||
m_Octaves.push_back(cOctave(m_Seed * ((int)m_Octaves.size() + 4) * 4 + 1024, a_Frequency, a_Amplitude));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cRidgedMultiNoise::Generate2D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
|
||||
int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
|
||||
NOISE_DATATYPE * a_Workspace ///< Workspace that this function can use and trash
|
||||
) const
|
||||
{
|
||||
if (m_Octaves.empty())
|
||||
{
|
||||
// No work to be done
|
||||
ASSERT(!"RidgedMulti: No octaves to generate!");
|
||||
return;
|
||||
}
|
||||
|
||||
bool ShouldFreeWorkspace = (a_Workspace == nullptr);
|
||||
int ArrayCount = a_SizeX * a_SizeY;
|
||||
if (ShouldFreeWorkspace)
|
||||
{
|
||||
a_Workspace = new NOISE_DATATYPE[ArrayCount];
|
||||
}
|
||||
|
||||
// Generate the first octave directly into array:
|
||||
const cOctave & FirstOctave = m_Octaves.front();
|
||||
|
||||
FirstOctave.m_Noise.Generate2D(
|
||||
a_Workspace, a_SizeX, a_SizeY,
|
||||
a_StartX * FirstOctave.m_Frequency, a_EndX * FirstOctave.m_Frequency,
|
||||
a_StartY * FirstOctave.m_Frequency, a_EndY * FirstOctave.m_Frequency
|
||||
);
|
||||
NOISE_DATATYPE Amplitude = FirstOctave.m_Amplitude;
|
||||
for (int i = 0; i < ArrayCount; i++)
|
||||
{
|
||||
a_Array[i] = fabs(a_Workspace[i] * Amplitude);
|
||||
}
|
||||
|
||||
// Add each octave:
|
||||
for (cOctaves::const_iterator itr = m_Octaves.begin() + 1, end = m_Octaves.end(); itr != end; ++itr)
|
||||
{
|
||||
// Generate cubic noise for the octave:
|
||||
itr->m_Noise.Generate2D(
|
||||
a_Workspace, a_SizeX, a_SizeY,
|
||||
a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency,
|
||||
a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency
|
||||
);
|
||||
// Add the cubic noise into the output:
|
||||
NOISE_DATATYPE Amplitude = itr->m_Amplitude;
|
||||
for (int i = 0; i < ArrayCount; i++)
|
||||
{
|
||||
a_Array[i] += fabs(a_Workspace[i] * Amplitude);
|
||||
}
|
||||
}
|
||||
|
||||
if (ShouldFreeWorkspace)
|
||||
{
|
||||
delete[] a_Workspace;
|
||||
a_Workspace = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cRidgedMultiNoise::Generate3D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
|
||||
int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
|
||||
NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction
|
||||
NOISE_DATATYPE * a_Workspace ///< Workspace that this function can use and trash
|
||||
) const
|
||||
{
|
||||
if (m_Octaves.empty())
|
||||
{
|
||||
// No work to be done
|
||||
ASSERT(!"RidgedMulti: No octaves to generate!");
|
||||
return;
|
||||
}
|
||||
|
||||
bool ShouldFreeWorkspace = (a_Workspace == nullptr);
|
||||
int ArrayCount = a_SizeX * a_SizeY * a_SizeZ;
|
||||
if (ShouldFreeWorkspace)
|
||||
{
|
||||
a_Workspace = new NOISE_DATATYPE[ArrayCount];
|
||||
}
|
||||
|
||||
// Generate the first octave directly into array:
|
||||
const cOctave & FirstOctave = m_Octaves.front();
|
||||
|
||||
FirstOctave.m_Noise.Generate3D(
|
||||
a_Workspace, a_SizeX, a_SizeY, a_SizeZ,
|
||||
a_StartX * FirstOctave.m_Frequency, a_EndX * FirstOctave.m_Frequency,
|
||||
a_StartY * FirstOctave.m_Frequency, a_EndY * FirstOctave.m_Frequency,
|
||||
a_StartZ * FirstOctave.m_Frequency, a_EndZ * FirstOctave.m_Frequency
|
||||
);
|
||||
NOISE_DATATYPE Amplitude = FirstOctave.m_Amplitude;
|
||||
for (int i = 0; i < ArrayCount; i++)
|
||||
{
|
||||
a_Array[i] = a_Workspace[i] * Amplitude;
|
||||
}
|
||||
|
||||
// Add each octave:
|
||||
for (cOctaves::const_iterator itr = m_Octaves.begin() + 1, end = m_Octaves.end(); itr != end; ++itr)
|
||||
{
|
||||
// Generate cubic noise for the octave:
|
||||
itr->m_Noise.Generate3D(
|
||||
a_Workspace, a_SizeX, a_SizeY, a_SizeZ,
|
||||
a_StartX * itr->m_Frequency, a_EndX * itr->m_Frequency,
|
||||
a_StartY * itr->m_Frequency, a_EndY * itr->m_Frequency,
|
||||
a_StartZ * itr->m_Frequency, a_EndZ * itr->m_Frequency
|
||||
);
|
||||
// Add the cubic noise into the output:
|
||||
NOISE_DATATYPE Amplitude = itr->m_Amplitude;
|
||||
for (int i = 0; i < ArrayCount; i++)
|
||||
{
|
||||
a_Array[i] += a_Workspace[i] * Amplitude;
|
||||
}
|
||||
}
|
||||
|
||||
if (ShouldFreeWorkspace)
|
||||
{
|
||||
delete[] a_Workspace;
|
||||
a_Workspace = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -7,22 +7,11 @@
|
||||
|
||||
#include <cmath>
|
||||
|
||||
/** The datatype used by all the noise generators. */
|
||||
typedef float NOISE_DATATYPE;
|
||||
|
||||
|
||||
|
||||
|
||||
// Some settings
|
||||
#define NOISE_DATATYPE float
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define INLINE __forceinline
|
||||
#else
|
||||
#define INLINE inline
|
||||
#endif
|
||||
#include "OctavedNoise.h"
|
||||
#include "RidgedNoise.h"
|
||||
|
||||
|
||||
|
||||
@ -35,20 +24,20 @@ public:
|
||||
cNoise(const cNoise & a_Noise);
|
||||
|
||||
// The following functions, if not marked INLINE, are about 20 % slower
|
||||
INLINE NOISE_DATATYPE IntNoise1D(int a_X) const;
|
||||
INLINE NOISE_DATATYPE IntNoise2D(int a_X, int a_Y) const;
|
||||
INLINE NOISE_DATATYPE IntNoise3D(int a_X, int a_Y, int a_Z) const;
|
||||
inline NOISE_DATATYPE IntNoise1D(int a_X) const;
|
||||
inline NOISE_DATATYPE IntNoise2D(int a_X, int a_Y) const;
|
||||
inline NOISE_DATATYPE IntNoise3D(int a_X, int a_Y, int a_Z) const;
|
||||
|
||||
// Return a float number in the specified range:
|
||||
INLINE NOISE_DATATYPE IntNoise2DInRange(int a_X, int a_Y, float a_Min, float a_Max) const
|
||||
inline NOISE_DATATYPE IntNoise2DInRange(int a_X, int a_Y, float a_Min, float a_Max) const
|
||||
{
|
||||
return a_Min + std::abs(IntNoise2D(a_X, a_Y)) * (a_Max - a_Min);
|
||||
}
|
||||
|
||||
// Note: These functions have a mod8-irregular chance - each of the mod8 remainders has different chance of occurrence. Divide by 8 to rectify.
|
||||
INLINE int IntNoise1DInt(int a_X) const;
|
||||
INLINE int IntNoise2DInt(int a_X, int a_Y) const;
|
||||
INLINE int IntNoise3DInt(int a_X, int a_Y, int a_Z) const;
|
||||
inline int IntNoise1DInt(int a_X) const;
|
||||
inline int IntNoise2DInt(int a_X, int a_Y) const;
|
||||
inline int IntNoise3DInt(int a_X, int a_Y, int a_Z) const;
|
||||
|
||||
NOISE_DATATYPE LinearNoise1D(NOISE_DATATYPE a_X) const;
|
||||
NOISE_DATATYPE CosineNoise1D(NOISE_DATATYPE a_X) const;
|
||||
@ -61,9 +50,9 @@ public:
|
||||
|
||||
void SetSeed(int a_Seed) { m_Seed = a_Seed; }
|
||||
|
||||
INLINE static NOISE_DATATYPE CubicInterpolate (NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_C, NOISE_DATATYPE a_D, NOISE_DATATYPE a_Pct);
|
||||
INLINE static NOISE_DATATYPE CosineInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct);
|
||||
INLINE static NOISE_DATATYPE LinearInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct);
|
||||
inline static NOISE_DATATYPE CubicInterpolate (NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_C, NOISE_DATATYPE a_D, NOISE_DATATYPE a_Pct);
|
||||
inline static NOISE_DATATYPE CosineInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct);
|
||||
inline static NOISE_DATATYPE LinearInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct);
|
||||
|
||||
private:
|
||||
int m_Seed;
|
||||
@ -76,19 +65,15 @@ private:
|
||||
class cCubicNoise
|
||||
{
|
||||
public:
|
||||
static const int MAX_SIZE = 512; ///< Maximum size of each dimension of the query arrays.
|
||||
/** Maximum size of each dimension of the query arrays. */
|
||||
static const int MAX_SIZE = 512;
|
||||
|
||||
|
||||
/** Creates a new instance with the specified seed. */
|
||||
cCubicNoise(int a_Seed);
|
||||
|
||||
|
||||
void Generate1D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into
|
||||
int a_SizeX, ///< Count of the array
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX ///< Noise-space coords of the array
|
||||
) const;
|
||||
|
||||
|
||||
/** Fills a 2D array with the values of the noise. */
|
||||
void Generate2D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
|
||||
int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
|
||||
@ -97,6 +82,7 @@ public:
|
||||
) const;
|
||||
|
||||
|
||||
/** Fills a 3D array with the values of the noise. */
|
||||
void Generate3D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
|
||||
int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
|
||||
@ -106,163 +92,87 @@ public:
|
||||
) const;
|
||||
|
||||
protected:
|
||||
typedef NOISE_DATATYPE Workspace1D[4];
|
||||
typedef NOISE_DATATYPE Workspace2D[4][4];
|
||||
|
||||
cNoise m_Noise; // Used for integral rnd values
|
||||
/** Noise used for integral random values. */
|
||||
cNoise m_Noise;
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Statistics on the noise-space coords:
|
||||
static int m_NumSingleX;
|
||||
static int m_NumSingleXY;
|
||||
static int m_NumSingleY;
|
||||
static int m_NumCalls;
|
||||
#endif // _DEBUG
|
||||
|
||||
/// Calculates the integral and fractional parts along one axis.
|
||||
|
||||
/** Calculates the integral and fractional parts along one axis.
|
||||
a_Floor will receive the integral parts (array of a_Size ints).
|
||||
a_Frac will receive the fractional parts (array of a_Size floats).
|
||||
a_Same will receive the counts of items that have the same integral parts (array of up to a_Size ints).
|
||||
a_NumSame will receive the count of a_Same elements (total count of different integral parts). */
|
||||
void CalcFloorFrac(
|
||||
int a_Size,
|
||||
NOISE_DATATYPE a_Start, NOISE_DATATYPE a_End,
|
||||
int * a_Floor, NOISE_DATATYPE * a_Frac,
|
||||
int * a_Same, int & a_NumSame
|
||||
) const;
|
||||
|
||||
void UpdateWorkRnds2DX(
|
||||
Workspace2D & a_WorkRnds,
|
||||
Workspace1D & a_Interps,
|
||||
int a_LastFloorX, int a_NewFloorX,
|
||||
int a_FloorY,
|
||||
NOISE_DATATYPE a_FractionY
|
||||
) const;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cPerlinNoise
|
||||
/** Improved noise, as described by Ken Perlin: http://mrl.nyu.edu/~perlin/paper445.pdf
|
||||
Implementation adapted from Perlin's Java implementation: http://mrl.nyu.edu/~perlin/noise/ */
|
||||
class cImprovedNoise
|
||||
{
|
||||
public:
|
||||
cPerlinNoise(void);
|
||||
cPerlinNoise(int a_Seed);
|
||||
|
||||
|
||||
void SetSeed(int a_Seed);
|
||||
|
||||
void AddOctave(NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude);
|
||||
|
||||
void Generate1D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into
|
||||
int a_SizeX, ///< Count of the array
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array
|
||||
NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash
|
||||
) const;
|
||||
|
||||
|
||||
/** Constructs a new instance of the noise obbject.
|
||||
Note that this operation is quite expensive (the permutation array being constructed). */
|
||||
cImprovedNoise(int a_Seed);
|
||||
|
||||
|
||||
/** Fills a 2D array with the values of the noise. */
|
||||
void Generate2D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
|
||||
int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
|
||||
NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY ///< Noise-space coords of the array in the Y direction
|
||||
) const;
|
||||
|
||||
|
||||
/** Fills a 3D array with the values of the noise. */
|
||||
void Generate3D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
|
||||
int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
|
||||
NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction
|
||||
NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash
|
||||
NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ ///< Noise-space coords of the array in the Z direction
|
||||
) const;
|
||||
|
||||
|
||||
/** Returns the value at the specified integral coords. Used for raw speed measurement. */
|
||||
NOISE_DATATYPE GetValueAt(int a_X, int a_Y, int a_Z);
|
||||
|
||||
protected:
|
||||
class cOctave
|
||||
|
||||
/** The permutation table used by the noise function. Initialized using seed. */
|
||||
int m_Perm[512];
|
||||
|
||||
|
||||
/** Calculates the fade curve, 6 * t^5 - 15 * t^4 + 10 * t^3. */
|
||||
inline static NOISE_DATATYPE Fade(NOISE_DATATYPE a_T)
|
||||
{
|
||||
public:
|
||||
cCubicNoise m_Noise;
|
||||
|
||||
NOISE_DATATYPE m_Frequency; // Coord multiplier
|
||||
NOISE_DATATYPE m_Amplitude; // Value multiplier
|
||||
|
||||
cOctave(int a_Seed, NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude) :
|
||||
m_Noise(a_Seed),
|
||||
m_Frequency(a_Frequency),
|
||||
m_Amplitude(a_Amplitude)
|
||||
{
|
||||
}
|
||||
} ;
|
||||
|
||||
typedef std::vector<cOctave> cOctaves;
|
||||
|
||||
int m_Seed;
|
||||
cOctaves m_Octaves;
|
||||
} ;
|
||||
return a_T * a_T * a_T * (a_T * (a_T * 6 - 15) + 10);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cRidgedMultiNoise
|
||||
{
|
||||
public:
|
||||
cRidgedMultiNoise(void);
|
||||
cRidgedMultiNoise(int a_Seed);
|
||||
|
||||
|
||||
void SetSeed(int a_Seed);
|
||||
|
||||
void AddOctave(NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude);
|
||||
|
||||
void Generate1D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into
|
||||
int a_SizeX, ///< Count of the array
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array
|
||||
NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash
|
||||
) const;
|
||||
|
||||
|
||||
void Generate2D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y]
|
||||
int a_SizeX, int a_SizeY, ///< Count of the array, in each direction
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
|
||||
NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash
|
||||
) const;
|
||||
|
||||
|
||||
void Generate3D(
|
||||
NOISE_DATATYPE * a_Array, ///< Array to generate into [x + a_SizeX * y + a_SizeX * a_SizeY * z]
|
||||
int a_SizeX, int a_SizeY, int a_SizeZ, ///< Count of the array, in each direction
|
||||
NOISE_DATATYPE a_StartX, NOISE_DATATYPE a_EndX, ///< Noise-space coords of the array in the X direction
|
||||
NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY, ///< Noise-space coords of the array in the Y direction
|
||||
NOISE_DATATYPE a_StartZ, NOISE_DATATYPE a_EndZ, ///< Noise-space coords of the array in the Z direction
|
||||
NOISE_DATATYPE * a_Workspace = nullptr ///< Workspace that this function can use and trash
|
||||
) const;
|
||||
|
||||
protected:
|
||||
class cOctave
|
||||
/** Returns the gradient value based on the hash. */
|
||||
inline static NOISE_DATATYPE Grad(int a_Hash, NOISE_DATATYPE a_X, NOISE_DATATYPE a_Y, NOISE_DATATYPE a_Z)
|
||||
{
|
||||
public:
|
||||
cCubicNoise m_Noise;
|
||||
|
||||
NOISE_DATATYPE m_Frequency; // Coord multiplier
|
||||
NOISE_DATATYPE m_Amplitude; // Value multiplier
|
||||
|
||||
cOctave(int a_Seed, NOISE_DATATYPE a_Frequency, NOISE_DATATYPE a_Amplitude) :
|
||||
m_Noise(a_Seed),
|
||||
m_Frequency(a_Frequency),
|
||||
m_Amplitude(a_Amplitude)
|
||||
{
|
||||
}
|
||||
} ;
|
||||
|
||||
typedef std::vector<cOctave> cOctaves;
|
||||
|
||||
int m_Seed;
|
||||
cOctaves m_Octaves;
|
||||
} ;
|
||||
int hash = a_Hash % 16;
|
||||
NOISE_DATATYPE u = (hash < 8) ? a_X : a_Y;
|
||||
NOISE_DATATYPE v = (hash < 4) ? a_Y : (((hash == 12) || (hash == 14)) ? a_X : a_Z);
|
||||
return (((hash & 1) == 0) ? u : -u) + (((hash & 2) == 0) ? v : -v);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
typedef cOctavedNoise<cCubicNoise> cPerlinNoise;
|
||||
typedef cOctavedNoise<cRidgedNoise<cCubicNoise>> cRidgedMultiNoise;
|
||||
|
||||
|
||||
|
||||
@ -376,8 +286,46 @@ NOISE_DATATYPE cNoise::LinearInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B,
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Global functions:
|
||||
|
||||
extern void Debug2DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, const AString & a_FileNameBase);
|
||||
extern void Debug3DNoise(const NOISE_DATATYPE * a_Noise, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase);
|
||||
/** Exports the noise array into a file.
|
||||
a_Coeff specifies the value that each array value is multiplied by before being converted into a byte. */
|
||||
extern void Debug2DNoise(const NOISE_DATATYPE * a_Array, int a_SizeX, int a_SizeY, const AString & a_FileNameBase, NOISE_DATATYPE a_Coeff = 32);
|
||||
|
||||
/** Exports the noise array into a set of files, ordered by XY and XZ.
|
||||
a_Coeff specifies the value that each array value is multiplied by before being converted into a byte. */
|
||||
extern void Debug3DNoise(const NOISE_DATATYPE * a_Array, int a_SizeX, int a_SizeY, int a_SizeZ, const AString & a_FileNameBase, NOISE_DATATYPE a_Coeff = 32);
|
||||
|
||||
|
||||
|
||||
|
||||
/** Linearly interpolates between two values.
|
||||
Assumes that a_Ratio is in range [0, 1]. */
|
||||
inline NOISE_DATATYPE Lerp(NOISE_DATATYPE a_Val1, NOISE_DATATYPE a_Val2, NOISE_DATATYPE a_Ratio)
|
||||
{
|
||||
return a_Val1 + (a_Val2 - a_Val1) * a_Ratio;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Linearly interpolates between two values, clamping the ratio to [0, 1] first. */
|
||||
inline NOISE_DATATYPE ClampedLerp(NOISE_DATATYPE a_Val1, NOISE_DATATYPE a_Val2, NOISE_DATATYPE a_Ratio)
|
||||
{
|
||||
if (a_Ratio < 0)
|
||||
{
|
||||
return a_Val1;
|
||||
}
|
||||
if (a_Ratio > 1)
|
||||
{
|
||||
return a_Val2;
|
||||
}
|
||||
return Lerp(a_Val1, a_Val2, a_Ratio);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
196
src/Noise/OctavedNoise.h
Normal file
196
src/Noise/OctavedNoise.h
Normal 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
91
src/Noise/RidgedNoise.h
Normal 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;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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>(
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Noise.h"
|
||||
#include "Noise/Noise.h"
|
||||
|
||||
|
||||
|
||||
|
277
src/World.cpp
277
src/World.cpp
@ -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
|
||||
{
|
||||
public:
|
||||
cWorldLoadProgress(cWorld * a_World) :
|
||||
cIsThread("cWorldLoadProgress"),
|
||||
m_World(a_World)
|
||||
{
|
||||
Start();
|
||||
}
|
||||
|
||||
void Stop(void)
|
||||
{
|
||||
m_ShouldTerminate = true;
|
||||
Wait();
|
||||
}
|
||||
|
||||
protected:
|
||||
typedef cIsThread super;
|
||||
|
||||
cWorld * m_World;
|
||||
|
||||
public:
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
// cIsThread override:
|
||||
virtual void Execute(void) override
|
||||
{
|
||||
for (;;)
|
||||
// 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++)
|
||||
{
|
||||
LOG("" SIZE_T_FMT " chunks to load, %d chunks to generate",
|
||||
m_World->GetStorage().GetLoadQueueLength(),
|
||||
m_World->GetGenerator().GetQueueLength()
|
||||
);
|
||||
|
||||
// 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)
|
||||
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();
|
||||
}
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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:
|
||||
cWorld & m_World;
|
||||
int m_SpawnChunkX;
|
||||
int m_SpawnChunkZ;
|
||||
int m_PrepareDistance;
|
||||
|
||||
cLightingThread * m_Lighting;
|
||||
|
||||
virtual void Execute(void) override
|
||||
/** The index of the next chunk to be queued in the lighting thread. */
|
||||
int m_NextIdx;
|
||||
|
||||
/** 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 remaining to light", m_Lighting->GetQueueLength()
|
||||
m_EvtFinished.Set();
|
||||
}
|
||||
|
||||
// Queue another chunk, if appropriate:
|
||||
if (m_NextIdx < m_MaxIdx)
|
||||
{
|
||||
int chunkX, chunkZ;
|
||||
DecodeChunkCoords(m_NextIdx, chunkX, chunkZ);
|
||||
m_World.GetLightingThread().QueueChunk(chunkX, chunkZ, this);
|
||||
m_NextIdx += 1;
|
||||
}
|
||||
|
||||
// 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
|
||||
);
|
||||
|
||||
// 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)
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -432,53 +467,9 @@ void cWorld::InitializeSpawn(void)
|
||||
IniFile.ReadFile(m_IniFileName);
|
||||
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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -182,7 +182,7 @@ int main( int argc, char **argv)
|
||||
#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
|
||||
InitLeakFinder();
|
||||
#endif
|
||||
|
||||
|
||||
// Magic code to produce dump-files on Windows if the server crashes:
|
||||
#if defined(_WIN32) && !defined(_WIN64) && defined(_MSC_VER)
|
||||
HINSTANCE hDbgHelp = LoadLibrary("DBGHELP.DLL");
|
||||
|
Loading…
Reference in New Issue
Block a user