diff options
author | Howaner <franzi.moos@googlemail.com> | 2014-11-29 12:05:35 +0100 |
---|---|---|
committer | Howaner <franzi.moos@googlemail.com> | 2014-11-29 12:05:35 +0100 |
commit | 648fee1a087cb97da9a4646d72ffc590e7837a67 (patch) | |
tree | eb55b68428a303089bc1120e9c3593542059f18e | |
parent | Finished mob spawner implementation. (diff) | |
parent | Merge pull request #1619 from mc-server/WarningFixes (diff) | |
download | cuberite-648fee1a087cb97da9a4646d72ffc590e7837a67.tar cuberite-648fee1a087cb97da9a4646d72ffc590e7837a67.tar.gz cuberite-648fee1a087cb97da9a4646d72ffc590e7837a67.tar.bz2 cuberite-648fee1a087cb97da9a4646d72ffc590e7837a67.tar.lz cuberite-648fee1a087cb97da9a4646d72ffc590e7837a67.tar.xz cuberite-648fee1a087cb97da9a4646d72ffc590e7837a67.tar.zst cuberite-648fee1a087cb97da9a4646d72ffc590e7837a67.zip |
Diffstat (limited to '')
83 files changed, 3204 insertions, 2253 deletions
diff --git a/MCServer/Plugins/InfoReg.lua b/MCServer/Plugins/InfoReg.lua index da5a9972c..92c4a2e59 100644 --- a/MCServer/Plugins/InfoReg.lua +++ b/MCServer/Plugins/InfoReg.lua @@ -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 diff --git a/src/BlockEntities/BeaconEntity.cpp b/src/BlockEntities/BeaconEntity.cpp index 85819446c..409f2937c 100644 --- a/src/BlockEntities/BeaconEntity.cpp +++ b/src/BlockEntities/BeaconEntity.cpp @@ -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); diff --git a/src/BlockEntities/BeaconEntity.h b/src/BlockEntities/BeaconEntity.h index 783e04fe2..bc27e92b0 100644 --- a/src/BlockEntities/BeaconEntity.h +++ b/src/BlockEntities/BeaconEntity.h @@ -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); diff --git a/src/BlockEntities/BlockEntityWithItems.h b/src/BlockEntities/BlockEntityWithItems.h index 8c7d4749b..2c2ced1cb 100644 --- a/src/BlockEntities/BlockEntityWithItems.h +++ b/src/BlockEntities/BlockEntityWithItems.h @@ -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 diff --git a/src/BlockEntities/ChestEntity.h b/src/BlockEntities/ChestEntity.h index a8a765430..645dbf4bc 100644 --- a/src/BlockEntities/ChestEntity.h +++ b/src/BlockEntities/ChestEntity.h @@ -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); diff --git a/src/BlockEntities/CommandBlockEntity.h b/src/BlockEntities/CommandBlockEntity.h index 8b5a2b87f..d8ac054f0 100644 --- a/src/BlockEntities/CommandBlockEntity.h +++ b/src/BlockEntities/CommandBlockEntity.h @@ -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); diff --git a/src/BlockEntities/DispenserEntity.h b/src/BlockEntities/DispenserEntity.h index 5ba87b716..12e12942a 100644 --- a/src/BlockEntities/DispenserEntity.h +++ b/src/BlockEntities/DispenserEntity.h @@ -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); diff --git a/src/BlockEntities/DropSpenserEntity.h b/src/BlockEntities/DropSpenserEntity.h index 8ce7f8bb8..6c23a402f 100644 --- a/src/BlockEntities/DropSpenserEntity.h +++ b/src/BlockEntities/DropSpenserEntity.h @@ -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(); diff --git a/src/BlockEntities/DropperEntity.h b/src/BlockEntities/DropperEntity.h index 91adf660f..c638dafa8 100644 --- a/src/BlockEntities/DropperEntity.h +++ b/src/BlockEntities/DropperEntity.h @@ -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); diff --git a/src/BlockEntities/EnderChestEntity.h b/src/BlockEntities/EnderChestEntity.h index 17abd880a..af59cf170 100644 --- a/src/BlockEntities/EnderChestEntity.h +++ b/src/BlockEntities/EnderChestEntity.h @@ -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(); diff --git a/src/BlockEntities/FlowerPotEntity.h b/src/BlockEntities/FlowerPotEntity.h index e98d0a395..a4246bb7d 100644 --- a/src/BlockEntities/FlowerPotEntity.h +++ b/src/BlockEntities/FlowerPotEntity.h @@ -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); diff --git a/src/BlockEntities/FurnaceEntity.h b/src/BlockEntities/FurnaceEntity.h index 0a66c60e3..fbe9d6c75 100644 --- a/src/BlockEntities/FurnaceEntity.h +++ b/src/BlockEntities/FurnaceEntity.h @@ -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); diff --git a/src/BlockEntities/HopperEntity.h b/src/BlockEntities/HopperEntity.h index 7070bbad3..da65aa671 100644 --- a/src/BlockEntities/HopperEntity.h +++ b/src/BlockEntities/HopperEntity.h @@ -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); diff --git a/src/BlockEntities/JukeboxEntity.h b/src/BlockEntities/JukeboxEntity.h index 67a8aa62a..000f7d87e 100644 --- a/src/BlockEntities/JukeboxEntity.h +++ b/src/BlockEntities/JukeboxEntity.h @@ -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(); diff --git a/src/BlockEntities/MobHeadEntity.h b/src/BlockEntities/MobHeadEntity.h index 55e45f0a6..e26c0bdd0 100644 --- a/src/BlockEntities/MobHeadEntity.h +++ b/src/BlockEntities/MobHeadEntity.h @@ -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); diff --git a/src/BlockEntities/NoteEntity.h b/src/BlockEntities/NoteEntity.h index b33edba4e..d3f85e9d2 100644 --- a/src/BlockEntities/NoteEntity.h +++ b/src/BlockEntities/NoteEntity.h @@ -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); diff --git a/src/BlockEntities/SignEntity.h b/src/BlockEntities/SignEntity.h index 09ab46f0b..9480537ed 100644 --- a/src/BlockEntities/SignEntity.h +++ b/src/BlockEntities/SignEntity.h @@ -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); diff --git a/src/BlockID.cpp b/src/BlockID.cpp index c98e0cad1..06f4232d3 100644 --- a/src/BlockID.cpp +++ b/src/BlockID.cpp @@ -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)) diff --git a/src/BlockID.h b/src/BlockID.h index 24de2dc8a..8f2cee02e 100644 --- a/src/BlockID.h +++ b/src/BlockID.h @@ -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); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 096fa824d..d6218ff42 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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++ ) diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 45004fa22..d271b4cb4 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -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; diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index c4ce721c3..a6cbad32a 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -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 diff --git a/src/CraftingRecipes.cpp b/src/CraftingRecipes.cpp index 64fb21181..2c2b02a69 100644 --- a/src/CraftingRecipes.cpp +++ b/src/CraftingRecipes.cpp @@ -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; diff --git a/src/Enchantments.cpp b/src/Enchantments.cpp index 80a9810b6..36c451b81 100644 --- a/src/Enchantments.cpp +++ b/src/Enchantments.cpp @@ -6,7 +6,7 @@ #include "Enchantments.h" #include "WorldStorage/FastNBT.h" #include "FastRandom.h" -#include "Noise.h" +#include "Noise/Noise.h" diff --git a/src/Entities/Minecart.h b/src/Entities/Minecart.h index 40e22c641..f7d0d5dda 100644 --- a/src/Entities/Minecart.h +++ b/src/Entities/Minecart.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; diff --git a/src/FurnaceRecipe.cpp b/src/FurnaceRecipe.cpp index 9b3b2ecbe..112aa8146 100644 --- a/src/FurnaceRecipe.cpp +++ b/src/FurnaceRecipe.cpp @@ -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) diff --git a/src/Generating/BioGen.h b/src/Generating/BioGen.h index 5fd0844d9..13fb40c5f 100644 --- a/src/Generating/BioGen.h +++ b/src/Generating/BioGen.h @@ -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" diff --git a/src/Generating/CMakeLists.txt b/src/Generating/CMakeLists.txt index 1a26bd0d5..a28510d40 100644 --- a/src/Generating/CMakeLists.txt +++ b/src/Generating/CMakeLists.txt @@ -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}) diff --git a/src/Generating/Caves.h b/src/Generating/Caves.h index 0e17acf9e..691ef3e62 100644 --- a/src/Generating/Caves.h +++ b/src/Generating/Caves.h @@ -13,7 +13,6 @@ #pragma once #include "GridStructGen.h" -#include "../Noise.h" diff --git a/src/Generating/ChunkDesc.cpp b/src/Generating/ChunkDesc.cpp index 020d3bd0f..4a5ac5a18 100644 --- a/src/Generating/ChunkDesc.cpp +++ b/src/Generating/ChunkDesc.cpp @@ -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, diff --git a/src/Generating/ChunkDesc.h b/src/Generating/ChunkDesc.h index 570132790..480106fb5 100644 --- a/src/Generating/ChunkDesc.h +++ b/src/Generating/ChunkDesc.h @@ -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( diff --git a/src/Generating/ChunkGenerator.cpp b/src/Generating/ChunkGenerator.cpp index 92e1bb31d..3ee02c767 100644 --- a/src/Generating/ChunkGenerator.cpp +++ b/src/Generating/ChunkGenerator.cpp @@ -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); } diff --git a/src/Generating/CompoGen.cpp b/src/Generating/CompoGen.cpp index 29b831dfd..23cc64d78 100644 --- a/src/Generating/CompoGen.cpp +++ b/src/Generating/CompoGen.cpp @@ -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); @@ -210,323 +213,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: cCompoGenNether::cCompoGenNether(int a_Seed) : @@ -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; } diff --git a/src/Generating/CompoGen.h b/src/Generating/CompoGen.h index b145b6ba3..3847688cd 100644 --- a/src/Generating/CompoGen.h +++ b/src/Generating/CompoGen.h @@ -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 diff --git a/src/Generating/CompoGenBiomal.cpp b/src/Generating/CompoGenBiomal.cpp new file mode 100644 index 000000000..030c2baa5 --- /dev/null +++ b/src/Generating/CompoGenBiomal.cpp @@ -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); +} + + + diff --git a/src/Generating/CompoGenBiomal.h b/src/Generating/CompoGenBiomal.h new file mode 100644 index 000000000..a3a65d3dc --- /dev/null +++ b/src/Generating/CompoGenBiomal.h @@ -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); + + + + diff --git a/src/Generating/ComposableGenerator.cpp b/src/Generating/ComposableGenerator.cpp index 5f46574c7..4192dfa72 100644 --- a/src/Generating/ComposableGenerator.cpp +++ b/src/Generating/ComposableGenerator.cpp @@ -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; + res = CreateCompoGenBiomal(a_Seed); } - else if (NoCaseCompare(CompoGenName, "debugbiomes") == 0) + else if (NoCaseCompare(CompoGenName, "BiomalNoise3D") == 0) { - res = new cCompoGenDebugBiomes; + // 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) + else if (NoCaseCompare(CompoGenName, "Classic") == 0) { - res = new cCompoGenClassic; + res = std::make_shared<cCompoGenClassic>(); } - else if (NoCaseCompare(CompoGenName, "DistortedHeightmap") == 0) + else if (NoCaseCompare(CompoGenName, "DebugBiomes") == 0) { - res = new cDistortedHeightmap(a_Seed, a_BiomeGen); + res = std::make_shared<cCompoGenDebugBiomes>(); } - else if (NoCaseCompare(CompoGenName, "end") == 0) + else if (NoCaseCompare(CompoGenName, "DistortedHeightmap") == 0) { - res = new cEndGen(a_Seed); + // The composition that used to be provided with DistortedHeightmap is now provided by the Biomal compositor: + res = CreateCompoGenBiomal(a_Seed); } - else if (NoCaseCompare(CompoGenName, "nether") == 0) + else if (NoCaseCompare(CompoGenName, "End") == 0) { - res = new cCompoGenNether(a_Seed); + res = std::make_shared<cEndGen>(a_Seed); } - else if (NoCaseCompare(CompoGenName, "BiomalNoise3D") == 0) + else if (NoCaseCompare(CompoGenName, "Nether") == 0) { - res = new cBiomalNoise3DComposable(a_Seed, a_BiomeGen); + 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) { diff --git a/src/Generating/ComposableGenerator.h b/src/Generating/ComposableGenerator.h index a091f8d53..86c30e090 100644 --- a/src/Generating/ComposableGenerator.h +++ b/src/Generating/ComposableGenerator.h @@ -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 cTerrainHeightGen +class cTerrainShapeGen { public: - virtual ~cTerrainHeightGen() {} // Force a virtual destructor in descendants + virtual ~cTerrainShapeGen() {} // Force a virtual destructor in descendants - /** Generates heightmap for the given chunk */ - virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) = 0; + /** 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 InitializeHeightGen(cIniFile & a_IniFile) {} + virtual void InitializeShapeGen(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 + /** 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 HeiGen.cpp! + 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 + + /** Retrieves the heightmap for the specified chunk. */ + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) = 0; + + /** Initializes the generator, reading its parameters from the INI file. */ + virtual void InitializeHeightGen(cIniFile & a_IniFile) {} + + /** 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); diff --git a/src/Generating/CompositedHeiGen.h b/src/Generating/CompositedHeiGen.h new file mode 100644 index 000000000..fa33a7861 --- /dev/null +++ b/src/Generating/CompositedHeiGen.h @@ -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; +}; + + + + diff --git a/src/Generating/DistortedHeightmap.cpp b/src/Generating/DistortedHeightmap.cpp index d5bc6ab55..e1ed9b450 100644 --- a/src/Generating/DistortedHeightmap.cpp +++ b/src/Generating/DistortedHeightmap.cpp @@ -15,163 +15,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: /** This table assigns a relative maximum overhang size in each direction to biomes. @@ -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(); - } -} - - - - diff --git a/src/Generating/DistortedHeightmap.h b/src/Generating/DistortedHeightmap.h index d073f29e4..79fc35542 100644 --- a/src/Generating/DistortedHeightmap.h +++ b/src/Generating/DistortedHeightmap.h @@ -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; } ; diff --git a/src/Generating/DungeonRoomsFinisher.cpp b/src/Generating/DungeonRoomsFinisher.cpp index 3f328868d..7ab22c2c5 100644 --- a/src/Generating/DungeonRoomsFinisher.cpp +++ b/src/Generating/DungeonRoomsFinisher.cpp @@ -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)); } diff --git a/src/Generating/DungeonRoomsFinisher.h b/src/Generating/DungeonRoomsFinisher.h index 09dd0448a..e5828f989 100644 --- a/src/Generating/DungeonRoomsFinisher.h +++ b/src/Generating/DungeonRoomsFinisher.h @@ -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; diff --git a/src/Generating/EndGen.cpp b/src/Generating/EndGen.cpp index 0111d2fa3..89d6117bb 100644 --- a/src/Generating/EndGen.cpp +++ b/src/Generating/EndGen.cpp @@ -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) - { - a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_END_STONE, 0); - } - else + if (a_Shape[(x + 16 * z) * 256 + y] != 0) { - a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_AIR, 0); + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_END_STONE); } } // for y } // for x diff --git a/src/Generating/EndGen.h b/src/Generating/EndGen.h index 322061810..f9e3f6e53 100644 --- a/src/Generating/EndGen.h +++ b/src/Generating/EndGen.h @@ -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; } ; diff --git a/src/Generating/FinishGen.cpp b/src/Generating/FinishGen.cpp index b8afac09a..2ff3e7f67 100644 --- a/src/Generating/FinishGen.cpp +++ b/src/Generating/FinishGen.cpp @@ -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); diff --git a/src/Generating/FinishGen.h b/src/Generating/FinishGen.h index 4a08d70c8..991a85787 100644 --- a/src/Generating/FinishGen.h +++ b/src/Generating/FinishGen.h @@ -16,7 +16,7 @@ #include "ComposableGenerator.h" -#include "../Noise.h" +#include "../Noise/Noise.h" #include "../ProbabDistrib.h" diff --git a/src/Generating/GridStructGen.h b/src/Generating/GridStructGen.h index 03131fce9..b92fb2e9d 100644 --- a/src/Generating/GridStructGen.h +++ b/src/Generating/GridStructGen.h @@ -10,7 +10,7 @@ #pragma once #include "ComposableGenerator.h" -#include "../Noise.h" +#include "../Noise/Noise.h" diff --git a/src/Generating/HeiGen.cpp b/src/Generating/HeiGen.cpp index 1d9f1e3aa..61d087c17 100644 --- a/src/Generating/HeiGen.cpp +++ b/src/Generating/HeiGen.cpp @@ -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++) @@ -160,6 +150,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: cHeiGenClassic::cHeiGenClassic(int a_Seed) : @@ -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; } diff --git a/src/Generating/HeiGen.h b/src/Generating/HeiGen.h index 6ae5ba362..62bb227c6 100644 --- a/src/Generating/HeiGen.h +++ b/src/Generating/HeiGen.h @@ -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,32 +17,13 @@ Interfaces to the various height generators: #pragma once #include "ComposableGenerator.h" -#include "../Noise.h" - - - - - -class cHeiGenFlat : - public cTerrainHeightGen -{ -public: - cHeiGenFlat(void) : m_Height(5) {} - -protected: - - int m_Height; - - // cTerrainHeightGen overrides: - virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; - virtual void InitializeHeightGen(cIniFile & a_IniFile) override; -} ; +#include "../Noise/Noise.h" -/// A simple cache that stores N most recently generated chunks' heightmaps; N being settable upon creation +/** A simple cache that stores N most recently generated chunks' heightmaps; N being settable upon creation */ class cHeiGenCache : public cTerrainHeightGen { @@ -50,15 +33,11 @@ public: // 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 + /** 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; @@ -66,6 +45,9 @@ protected: 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 @@ -81,6 +63,57 @@ protected: +/** 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; +}; + + + + + +class cHeiGenFlat : + public cTerrainHeightGen +{ +public: + cHeiGenFlat(void) : m_Height(5) {} + +protected: + + int m_Height; + + // cTerrainHeightGen overrides: + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override; + virtual void InitializeHeightGen(cIniFile & a_IniFile) override; +} ; + + + + + 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); } ; diff --git a/src/Generating/MineShafts.h b/src/Generating/MineShafts.h index 2850db571..efb11cfee 100644 --- a/src/Generating/MineShafts.h +++ b/src/Generating/MineShafts.h @@ -10,7 +10,6 @@ #pragma once #include "GridStructGen.h" -#include "../Noise.h" diff --git a/src/Generating/Noise3DGenerator.cpp b/src/Generating/Noise3DGenerator.cpp index f2af75999..a7af87bac 100644 --- a/src/Generating/Noise3DGenerator.cpp +++ b/src/Generating/Noise3DGenerator.cpp @@ -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; -} - +public: + cInterpolNoiseSpeedTest(void) + { + TestSpeed2D(); + TestSpeed3D(); + printf("InterpolNoise speed comparison finished.\n"); + } + /** Compare the speed of the 3D InterpolNoise vs 3D CubicNoise. */ + void TestSpeed3D(void) + { + 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); -/** 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) - { - return a_Val1; + // 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"); } - if (a_Ratio > 1) + + + /** Compare the speed of the 2D InterpolNoise vs 2D CubicNoise. */ + void TestSpeed2D(void) { - return a_Val2; + 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"); } - return Lerp(a_Val1, a_Val2, a_Ratio); -} +} 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); - - #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_Perlin.AddOctave(1, 1); + m_Perlin.AddOctave(2, 0.5); + m_Perlin.AddOctave(4, 0.25); + m_Perlin.AddOctave(8, 0.125); + m_Perlin.AddOctave(16, 0.0625); + + m_Cubic.AddOctave(1, 1); + m_Cubic.AddOctave(2, 0.5); + m_Cubic.AddOctave(4, 0.25); + m_Cubic.AddOctave(8, 0.125); + m_Cubic.AddOctave(16, 0.0625); } @@ -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) - { - AddHeight *= 4; - } - - for (int z = 0; z < 5; z++) + for (int x = 0; x < 5; x++) { - 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]; - } - } - } - LinearUpscale3DArray<NOISE_DATATYPE>(Workspace, 5, 5, 33, m_NoiseArray, 4, 4, 8); -} - - + 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; + } - -void cNoise3DComposable::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) -{ - GenerateNoiseArrayIfNeeded(a_ChunkX, a_ChunkZ); - - 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--) - { - if (m_NoiseArray[y * 17 * 17 + z * 17 + x] <= m_AirThreshold) + // If too high, cut off any terrain: + if (y > 28) { - cChunkDef::SetHeight(a_HeightMap, x, z, y); - break; + AddHeight = AddHeight + static_cast<NOISE_DATATYPE>(y - 28) / 4; } - } // for y - } // for x - } // for z + + // 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, 33, 5, 5, m_NoiseArray, 8, 4, 4); } -void cNoise3DComposable::ComposeTerrain(cChunkDesc & a_ChunkDesc) +void cNoise3DComposable::GenShape(int a_ChunkX, int a_ChunkZ, cChunkDesc::Shape & a_Shape) { - GenerateNoiseArrayIfNeeded(a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ()); - - a_ChunkDesc.FillBlocks(E_BLOCK_AIR, 0); + GenerateNoiseArrayIfNeeded(a_ChunkX, a_ChunkZ); - // Make basic terrain composition: + // Translate the noise array into Shape: 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++) + for (int y = 0; y < cChunkDef::Height; 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--) - { - 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++) + for (int y = 0; y < cChunkDef::Height; 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) + diff --git a/src/Generating/Noise3DGenerator.h b/src/Generating/Noise3DGenerator.h index ba541fbcc..35b1e4c94 100644 --- a/src/Generating/Noise3DGenerator.h +++ b/src/Generating/Noise3DGenerator.h @@ -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); } } ; diff --git a/src/Generating/PieceGenerator.h b/src/Generating/PieceGenerator.h index f06029280..43ffed7a2 100644 --- a/src/Generating/PieceGenerator.h +++ b/src/Generating/PieceGenerator.h @@ -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" diff --git a/src/Generating/Ravines.h b/src/Generating/Ravines.h index 3e41c5ce6..b11037433 100644 --- a/src/Generating/Ravines.h +++ b/src/Generating/Ravines.h @@ -10,7 +10,6 @@ #pragma once #include "GridStructGen.h" -#include "../Noise.h" diff --git a/src/Generating/ShapeGen.cpp b/src/Generating/ShapeGen.cpp new file mode 100644 index 000000000..45a9c3b93 --- /dev/null +++ b/src/Generating/ShapeGen.cpp @@ -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; +} + + + + diff --git a/src/Generating/StructGen.cpp b/src/Generating/StructGen.cpp index bdefcd8c1..2f685c808 100644 --- a/src/Generating/StructGen.cpp +++ b/src/Generating/StructGen.cpp @@ -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; diff --git a/src/Generating/StructGen.h b/src/Generating/StructGen.h index 906fdd722..796abf0f5 100644 --- a/src/Generating/StructGen.h +++ b/src/Generating/StructGen.h @@ -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); } ; diff --git a/src/Generating/Trees.cpp b/src/Generating/Trees.cpp index 7fd6d6f07..be8b0cd6b 100644 --- a/src/Generating/Trees.cpp +++ b/src/Generating/Trees.cpp @@ -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; + } } diff --git a/src/Generating/Trees.h b/src/Generating/Trees.h index c9eb7de80..a2c8274f5 100644 --- a/src/Generating/Trees.h +++ b/src/Generating/Trees.h @@ -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); diff --git a/src/Generating/TwoHeights.cpp b/src/Generating/TwoHeights.cpp new file mode 100644 index 000000000..e75c301de --- /dev/null +++ b/src/Generating/TwoHeights.cpp @@ -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); +} + + + + diff --git a/src/Generating/TwoHeights.h b/src/Generating/TwoHeights.h new file mode 100644 index 000000000..353598011 --- /dev/null +++ b/src/Generating/TwoHeights.h @@ -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); + + + + diff --git a/src/Generating/VillageGen.cpp b/src/Generating/VillageGen.cpp index a9b359b6a..f7d9a8316 100644 --- a/src/Generating/VillageGen.cpp +++ b/src/Generating/VillageGen.cpp @@ -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), diff --git a/src/ItemGrid.cpp b/src/ItemGrid.cpp index 55fb36a17..d49ea9df1 100644 --- a/src/ItemGrid.cpp +++ b/src/ItemGrid.cpp @@ -6,7 +6,7 @@ #include "Globals.h" #include "ItemGrid.h" #include "Items/ItemHandler.h" -#include "Noise.h" +#include "Noise/Noise.h" diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 23b4d9f45..5319bdf91 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -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++; } diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h index 96c2a6bdd..fd4e8a659 100644 --- a/src/Mobs/Monster.h +++ b/src/Mobs/Monster.h @@ -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) { diff --git a/src/Mobs/Pig.cpp b/src/Mobs/Pig.cpp index 55a4412ca..50b69e44f 100644 --- a/src/Mobs/Pig.cpp +++ b/src/Mobs/Pig.cpp @@ -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; +} + + + + diff --git a/src/Mobs/Pig.h b/src/Mobs/Pig.h index 953850b3a..0e026933a 100644 --- a/src/Mobs/Pig.h +++ b/src/Mobs/Pig.h @@ -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; diff --git a/src/Mobs/Villager.cpp b/src/Mobs/Villager.cpp index 5c9999a59..963595347 100644 --- a/src/Mobs/Villager.cpp +++ b/src/Mobs/Villager.cpp @@ -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; } diff --git a/src/Noise/CMakeLists.txt b/src/Noise/CMakeLists.txt new file mode 100644 index 000000000..e1837500f --- /dev/null +++ b/src/Noise/CMakeLists.txt @@ -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() diff --git a/src/Noise/InterpolNoise.h b/src/Noise/InterpolNoise.h new file mode 100644 index 000000000..683b54563 --- /dev/null +++ b/src/Noise/InterpolNoise.h @@ -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; + + + diff --git a/src/Noise.cpp b/src/Noise/Noise.cpp index 8fcfe2920..509be7d6c 100644 --- a/src/Noise.cpp +++ b/src/Noise/Noise.cpp @@ -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,101 +869,28 @@ void cCubicNoise::CalcFloorFrac( //////////////////////////////////////////////////////////////////////////////// -// cPerlinNoise: - -cPerlinNoise::cPerlinNoise(void) : - m_Seed(0) -{ -} - - - +// cImprovedNoise: - -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 -) const +cImprovedNoise::cImprovedNoise(int a_Seed) { - if (m_Octaves.empty()) - { - // 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++) + // Initialize the permutations with identity: + for (int i = 0; i < 256; i++) { - a_Array[i] = a_Workspace[i] * Amplitude; + m_Perm[i] = i; } - - // Add each octave: - for (cOctaves::const_iterator itr = m_Octaves.begin() + 1, end = m_Octaves.end(); itr != end; ++itr) + + // Randomize the permutation table - swap each element with a random other element: + cNoise noise(a_Seed); + for (int i = 0; i < 256; i++) { - // 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] += a_Workspace[i] * Amplitude; - } + int rnd = (noise.IntNoise1DInt(i) / 7) % 256; + std::swap(m_Perm[i], m_Perm[rnd]); } - - if (ShouldFreeWorkspace) + + // Copy the lower 256 entries into upper 256 entries: + for (int i = 0; i < 256; i++) { - delete[] a_Workspace; - a_Workspace = nullptr; + m_Perm[i + 256] = m_Perm[i]; } } @@ -894,239 +898,132 @@ void cPerlinNoise::Generate2D( -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::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()) - { - // 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) + size_t idx = 0; + for (int y = 0; y < a_SizeY; y++) { - 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 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; - } -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cRidgedMultiNoise: - -cRidgedMultiNoise::cRidgedMultiNoise(void) : - m_Seed(0) -{ -} - - - - - -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)); + 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 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 +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()) - { - // 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++) + size_t idx = 0; + for (int z = 0; z < a_SizeZ; z++) { - 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++) + 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] += fabs(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 } -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 +NOISE_DATATYPE cImprovedNoise::GetValueAt(int a_X, int a_Y, int a_Z) { - 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; - } + // 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); } + diff --git a/src/Noise.h b/src/Noise/Noise.h index b7a90d5b7..323194bfd 100644 --- a/src/Noise.h +++ b/src/Noise/Noise.h @@ -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 - - #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. + /** Noise used for integral random values. */ + 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; - - 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); + } + /** 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) + { + 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); + } +}; -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 - { - 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; -} ; + +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); +} + + + + + diff --git a/src/Noise/OctavedNoise.h b/src/Noise/OctavedNoise.h new file mode 100644 index 000000000..efb9a0167 --- /dev/null +++ b/src/Noise/OctavedNoise.h @@ -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; +}; + + + + diff --git a/src/Noise/RidgedNoise.h b/src/Noise/RidgedNoise.h new file mode 100644 index 000000000..f59a0512f --- /dev/null +++ b/src/Noise/RidgedNoise.h @@ -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; +} ; + + + + + diff --git a/src/Root.cpp b/src/Root.cpp index 55e1c1156..e309bb174 100644 --- a/src/Root.cpp +++ b/src/Root.cpp @@ -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(); diff --git a/src/Root.h b/src/Root.h index ec6b83fcc..29753a47d 100644 --- a/src/Root.h +++ b/src/Root.h @@ -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; diff --git a/src/Vector3.h b/src/Vector3.h index 1854e42e3..1e4a1f5d9 100644 --- a/src/Vector3.h +++ b/src/Vector3.h @@ -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>( diff --git a/src/VoronoiMap.h b/src/VoronoiMap.h index dfb11e9ce..56022849e 100644 --- a/src/VoronoiMap.h +++ b/src/VoronoiMap.h @@ -9,7 +9,7 @@ #pragma once -#include "Noise.h" +#include "Noise/Noise.h" diff --git a/src/World.cpp b/src/World.cpp index df1a97460..0dec0bd96 100644 --- a/src/World.cpp +++ b/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 { + typedef cIsThread super; + public: - cWorldLoadProgress(cWorld * a_World) : - cIsThread("cWorldLoadProgress"), - m_World(a_World) - { + cSpawnPrepare(cWorld & a_World, int a_SpawnChunkX, int a_SpawnChunkZ, int a_PrepareDistance): + super("SpawnPrepare"), + m_World(a_World), + m_SpawnChunkX(a_SpawnChunkX), + m_SpawnChunkZ(a_SpawnChunkZ), + m_PrepareDistance(a_PrepareDistance), + m_MaxIdx(a_PrepareDistance * a_PrepareDistance), + m_NumPrepared(0), + m_LastReportTime(0), + m_LastReportChunkCount(0) + { + // Start the thread: Start(); + + // Wait for start confirmation, so that the thread can be waited-upon after the constructor returns: + m_EvtStarted.Wait(); } - - void Stop(void) - { - m_ShouldTerminate = true; - Wait(); - } - -protected: - cWorld * m_World; - + + // 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(); } - -} ; +protected: + cWorld & m_World; + int m_SpawnChunkX; + int m_SpawnChunkZ; + int m_PrepareDistance; + /** 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; -//////////////////////////////////////////////////////////////////////////////// -// cWorldLightingProgress: + /** Event used to signal that the thread has started. */ + cEvent m_EvtStarted; -/// 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) + /** 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) { - m_ShouldTerminate = true; - Wait(); + // Check if this was the last chunk: + m_NumPrepared += 1; + if (m_NumPrepared >= m_MaxIdx) + { + 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 + ); + m_LastReportTime = now; + m_LastReportChunkCount = m_NumPrepared; + } } - -protected: - cLightingThread * m_Lighting; - - virtual void Execute(void) override + + /** Decodes the index into chunk coords. Provides the specific chunk ordering. */ + void DecodeChunkCoords(int a_Idx, int & a_ChunkX, int & a_ChunkZ) { - for (;;) + // 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) { - LOG("" SIZE_T_FMT " chunks remaining to light", m_Lighting->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) + // 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; } diff --git a/src/World.h b/src/World.h index fe57b0789..68d0654ee 100644 --- a/src/World.h +++ b/src/World.h @@ -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 */ diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index 451f20ca5..4520751c7 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -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; + } } } } diff --git a/src/main.cpp b/src/main.cpp index 463c54c28..c60e13a8c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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"); |