From 187abe3f5e2219e0a80c0cdca4db362e223b60ae Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 26 Feb 2017 22:49:23 +0100 Subject: Gen: Moved PiecePool into a separate file. Also rewritten the PieceGenerator to use std::unique_ptr. --- src/Generating/CMakeLists.txt | 6 +- src/Generating/PieceGenerator.cpp | 850 --------------------------- src/Generating/PieceGenerator.h | 446 -------------- src/Generating/PieceGeneratorBFSTree.cpp | 345 +++++++++++ src/Generating/PieceGeneratorBFSTree.h | 102 ++++ src/Generating/PiecePool.cpp | 503 ++++++++++++++++ src/Generating/PiecePool.h | 335 +++++++++++ src/Generating/PieceStructuresGen.cpp | 165 ++++-- src/Generating/PieceStructuresGen.h | 37 +- src/Generating/Prefab.h | 2 +- src/Generating/PrefabPiecePool.h | 2 +- src/Generating/PrefabStructure.cpp | 15 +- src/Generating/PrefabStructure.h | 6 +- src/Generating/VerticalLimit.h | 2 +- src/Generating/VerticalStrategy.cpp | 1 + src/Generating/VerticalStrategy.h | 2 +- src/Generating/VillageGen.cpp | 14 +- tests/LoadablePieces/CMakeLists.txt | 4 +- tests/LuaThreadStress/CMakeLists.txt | 4 +- tests/PieceRotation/CMakeLists.txt | 4 +- tests/PieceRotation/PieceRotationTest.cpp | 2 +- tests/SchematicFileSerializer/CMakeLists.txt | 2 +- 22 files changed, 1424 insertions(+), 1425 deletions(-) delete mode 100644 src/Generating/PieceGenerator.cpp delete mode 100644 src/Generating/PieceGenerator.h create mode 100644 src/Generating/PieceGeneratorBFSTree.cpp create mode 100644 src/Generating/PieceGeneratorBFSTree.h create mode 100644 src/Generating/PiecePool.cpp create mode 100644 src/Generating/PiecePool.h diff --git a/src/Generating/CMakeLists.txt b/src/Generating/CMakeLists.txt index 2cd923a99..67e0e8b22 100644 --- a/src/Generating/CMakeLists.txt +++ b/src/Generating/CMakeLists.txt @@ -18,7 +18,8 @@ SET (SRCS HeiGen.cpp MineShafts.cpp Noise3DGenerator.cpp - PieceGenerator.cpp + PieceGeneratorBFSTree.cpp + PiecePool.cpp PieceStructuresGen.cpp Prefab.cpp PrefabPiecePool.cpp @@ -51,7 +52,8 @@ SET (HDRS IntGen.h MineShafts.h Noise3DGenerator.h - PieceGenerator.h + PieceGeneratorBFSTree.h + PiecePool.h PieceStructuresGen.h Prefab.h PrefabPiecePool.h diff --git a/src/Generating/PieceGenerator.cpp b/src/Generating/PieceGenerator.cpp deleted file mode 100644 index f8ae1d961..000000000 --- a/src/Generating/PieceGenerator.cpp +++ /dev/null @@ -1,850 +0,0 @@ - -// PieceGenerator.cpp - -// Implements the cBFSPieceGenerator class and cDFSPieceGenerator class -// representing base classes for generating structures composed of individual "pieces" - -#include "Globals.h" -#include "PieceGenerator.h" -#include "VerticalStrategy.h" -#include "VerticalLimit.h" - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cPiece: - -bool cPiece::SetVerticalStrategyFromString(const AString & a_StrategyDesc, bool a_LogWarnings) -{ - auto strategy = CreateVerticalStrategyFromString(a_StrategyDesc, a_LogWarnings); - if (strategy == nullptr) - { - return false; - } - m_VerticalStrategy = strategy; - return true; -} - - - - - -bool cPiece::SetVerticalLimitFromString(const AString & a_LimitDesc, bool a_LogWarnings) -{ - auto limit = CreateVerticalLimitFromString(a_LimitDesc, a_LogWarnings); - if (limit == nullptr) - { - return false; - } - m_VerticalLimit = limit; - return true; -} - - - - - -Vector3i cPiece::RotatePos(const Vector3i & a_Pos, int a_NumCCWRotations) const -{ - Vector3i Size = GetSize(); - switch (a_NumCCWRotations) - { - case 0: - { - // No rotation needed - return a_Pos; - } - case 1: - { - // 1 CCW rotation: - return Vector3i(a_Pos.z, a_Pos.y, Size.x - a_Pos.x - 1); - } - case 2: - { - // 2 rotations ( = axis flip): - return Vector3i(Size.x - a_Pos.x - 1, a_Pos.y, Size.z - a_Pos.z - 1); - } - case 3: - { - // 1 CW rotation: - return Vector3i(Size.z - a_Pos.z - 1, a_Pos.y, a_Pos.x); - } - } - ASSERT(!"Unhandled rotation"); - return a_Pos; -} - - - - - -cPiece::cConnector cPiece::RotateMoveConnector(const cConnector & a_Connector, int a_NumCCWRotations, int a_MoveX, int a_MoveY, int a_MoveZ) const -{ - cPiece::cConnector res(a_Connector); - - // Rotate the res connector: - switch (a_NumCCWRotations) - { - case 0: - { - // No rotation needed - break; - } - case 1: - { - // 1 CCW rotation: - res.m_Direction = cConnector::RotateDirectionCCW(res.m_Direction); - break; - } - case 2: - { - // 2 rotations ( = axis flip): - res.m_Direction = cConnector::RotateDirection(res.m_Direction); - break; - } - case 3: - { - // 1 CW rotation: - res.m_Direction = cConnector::RotateDirectionCW(res.m_Direction); - break; - } - } - res.m_Pos = RotatePos(a_Connector.m_Pos, a_NumCCWRotations); - - // Move the res connector: - res.m_Pos.x += a_MoveX; - res.m_Pos.y += a_MoveY; - res.m_Pos.z += a_MoveZ; - - return res; -} - - - - - -cCuboid cPiece::RotateHitBoxToConnector( - const cPiece::cConnector & a_MyConnector, - const Vector3i & a_ToConnectorPos, - int a_NumCCWRotations -) const -{ - ASSERT(a_NumCCWRotations == (a_NumCCWRotations % 4)); - Vector3i ConnPos = RotatePos(a_MyConnector.m_Pos, a_NumCCWRotations); - ConnPos = a_ToConnectorPos - ConnPos; - return RotateMoveHitBox(a_NumCCWRotations, ConnPos.x, ConnPos.y, ConnPos.z); -} - - - - - -cCuboid cPiece::RotateMoveHitBox(int a_NumCCWRotations, int a_MoveX, int a_MoveY, int a_MoveZ) const -{ - ASSERT(a_NumCCWRotations == (a_NumCCWRotations % 4)); - cCuboid res = GetHitBox(); - res.p1 = RotatePos(res.p1, a_NumCCWRotations); - res.p2 = RotatePos(res.p2, a_NumCCWRotations); - res.p1.Move(a_MoveX, a_MoveY, a_MoveZ); - res.p2.Move(a_MoveX, a_MoveY, a_MoveZ); - return res; -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cPiece::cConnector: - -cPiece::cConnector::cConnector(int a_X, int a_Y, int a_Z, int a_Type, eDirection a_Direction) : - m_Pos(a_X, a_Y, a_Z), - m_Type(a_Type), - m_Direction(a_Direction) -{ -} - - - - - -cPiece::cConnector::cConnector(const Vector3i & a_Pos, int a_Type, eDirection a_Direction) : - m_Pos(a_Pos), - m_Type(a_Type), - m_Direction(a_Direction) -{ -} - - - - - -Vector3i cPiece::cConnector::AddDirection(const Vector3i & a_Pos, eDirection a_Direction) -{ - switch (a_Direction) - { - case dirXM: return Vector3i(a_Pos.x - 1, a_Pos.y, a_Pos.z); - case dirXP: return Vector3i(a_Pos.x + 1, a_Pos.y, a_Pos.z); - case dirYM: return Vector3i(a_Pos.x, a_Pos.y - 1, a_Pos.z); - case dirYP: return Vector3i(a_Pos.x, a_Pos.y + 1, a_Pos.z); - case dirZM: return Vector3i(a_Pos.x, a_Pos.y, a_Pos.z - 1); - case dirZP: return Vector3i(a_Pos.x, a_Pos.y, a_Pos.z + 1); - case dirYM_XM_ZM: return Vector3i(a_Pos.x, a_Pos.y - 1, a_Pos.z); - case dirYM_XM_ZP: return Vector3i(a_Pos.x, a_Pos.y - 1, a_Pos.z); - case dirYM_XP_ZM: return Vector3i(a_Pos.x, a_Pos.y - 1, a_Pos.z); - case dirYM_XP_ZP: return Vector3i(a_Pos.x, a_Pos.y - 1, a_Pos.z); - case dirYP_XM_ZM: return Vector3i(a_Pos.x, a_Pos.y + 1, a_Pos.z); - case dirYP_XM_ZP: return Vector3i(a_Pos.x, a_Pos.y + 1, a_Pos.z); - case dirYP_XP_ZM: return Vector3i(a_Pos.x, a_Pos.y + 1, a_Pos.z); - case dirYP_XP_ZP: return Vector3i(a_Pos.x, a_Pos.y + 1, a_Pos.z); - } - #if !defined(__clang__) - ASSERT(!"Unknown connector direction"); - return a_Pos; - #endif -} - - - - - -const char * cPiece::cConnector::DirectionToString(eDirection a_Direction) -{ - switch (a_Direction) - { - case dirXM: return "x-"; - case dirXP: return "x+"; - case dirYM: return "y-"; - case dirYP: return "y+"; - case dirZM: return "z-"; - case dirZP: return "z+"; - case dirYM_XM_ZM: return "y-x-z-"; - case dirYM_XM_ZP: return "y-x-z+"; - case dirYM_XP_ZM: return "y-x+z-"; - case dirYM_XP_ZP: return "y-x+z+"; - case dirYP_XM_ZM: return "y+x-z-"; - case dirYP_XM_ZP: return "y+x-z+"; - case dirYP_XP_ZM: return "y+x+z-"; - case dirYP_XP_ZP: return "y+x+z+"; - } - #if !defined(__clang__) - ASSERT(!"Unknown connector direction"); - return ""; - #endif -} - - - - - -bool cPiece::cConnector::IsValidDirection(int a_Direction) -{ - switch (a_Direction) - { - case dirXM: - case dirXP: - case dirYM: - case dirYP: - case dirZM: - case dirZP: - case dirYM_XM_ZM: - case dirYM_XM_ZP: - case dirYM_XP_ZM: - case dirYM_XP_ZP: - case dirYP_XM_ZM: - case dirYP_XM_ZP: - case dirYP_XP_ZM: - case dirYP_XP_ZP: - { - return true; - } - } - return false; -} - - - - - -cPiece::cConnector::eDirection cPiece::cConnector::RotateDirection(eDirection a_Direction) -{ - // 180-degree rotation: - switch (a_Direction) - { - case dirXM: return dirXP; - case dirXP: return dirXM; - case dirYM: return dirYM; - case dirYP: return dirYP; - case dirZM: return dirZM; - case dirZP: return dirZP; - case dirYM_XM_ZM: return dirYM_XP_ZP; - case dirYM_XM_ZP: return dirYM_XP_ZM; - case dirYM_XP_ZM: return dirYM_XM_ZP; - case dirYM_XP_ZP: return dirYM_XM_ZM; - case dirYP_XM_ZM: return dirYP_XP_ZP; - case dirYP_XM_ZP: return dirYP_XP_ZM; - case dirYP_XP_ZM: return dirYP_XM_ZP; - case dirYP_XP_ZP: return dirYP_XM_ZM; - } - #if !defined(__clang__) - ASSERT(!"Unknown connector direction"); - return a_Direction; - #endif -} - - - - - -cPiece::cConnector::eDirection cPiece::cConnector::RotateDirectionCCW(eDirection a_Direction) -{ - // 90 degrees CCW rotation: - switch (a_Direction) - { - case dirXM: return dirZP; - case dirXP: return dirZM; - case dirYM: return dirYM; - case dirYP: return dirYP; - case dirZM: return dirXM; - case dirZP: return dirXP; - case dirYM_XM_ZM: return dirYM_XM_ZP; - case dirYM_XM_ZP: return dirYM_XP_ZP; - case dirYM_XP_ZM: return dirYM_XM_ZM; - case dirYM_XP_ZP: return dirYM_XP_ZM; - case dirYP_XM_ZM: return dirYP_XM_ZP; - case dirYP_XM_ZP: return dirYP_XP_ZP; - case dirYP_XP_ZM: return dirYP_XM_ZM; - case dirYP_XP_ZP: return dirYP_XP_ZM; - } - #if !defined(__clang__) - ASSERT(!"Unknown connector direction"); - return a_Direction; - #endif -} - - - - - -cPiece::cConnector::eDirection cPiece::cConnector::RotateDirectionCW(eDirection a_Direction) -{ - // 90 degrees CW rotation: - switch (a_Direction) - { - case dirXM: return dirZM; - case dirXP: return dirZP; - case dirYM: return dirYM; - case dirYP: return dirYP; - case dirZM: return dirXP; - case dirZP: return dirXM; - case dirYM_XM_ZM: return dirYM_XP_ZM; - case dirYM_XM_ZP: return dirYM_XM_ZM; - case dirYM_XP_ZM: return dirYM_XP_ZP; - case dirYM_XP_ZP: return dirYM_XM_ZP; - case dirYP_XM_ZM: return dirYP_XP_ZM; - case dirYP_XM_ZP: return dirYP_XM_ZM; - case dirYP_XP_ZM: return dirYP_XP_ZP; - case dirYP_XP_ZP: return dirYP_XM_ZP; - } - #if !defined(__clang__) - ASSERT(!"Unknown connector direction"); - return a_Direction; - #endif -} - - - - - -bool cPiece::cConnector::StringToDirection(const AString & a_Value, eDirection & a_Out) -{ - // First try converting as a number: - int dirInt; - if (StringToInteger(a_Value, dirInt)) - { - if (!IsValidDirection(dirInt)) - { - return false; - } - a_Out = static_cast(dirInt); - return true; - } - - // Compare to string representation: - static const struct - { - const char * m_String; - eDirection m_Value; - } StringDirections[] = - { - {"x-", dirXM}, - {"x+", dirXP}, - {"y-", dirYM}, - {"y+", dirYP}, - {"z-", dirZM}, - {"z+", dirZP}, - {"y-x-z-", dirYM_XM_ZM}, - {"y-x-z+", dirYM_XM_ZP}, - {"y-x+z-", dirYM_XP_ZM}, - {"y-x+z+", dirYM_XP_ZP}, - {"y+x-z-", dirYP_XM_ZM}, - {"y+x-z+", dirYP_XM_ZP}, - {"y+x+z-", dirYP_XP_ZM}, - {"y+x+z+", dirYP_XP_ZP}, - - // Alternate names, with slashes: - {"y-/x-/z-", dirYM_XM_ZM}, - {"y-/x-/z+", dirYM_XM_ZP}, - {"y-/x+/z-", dirYM_XP_ZM}, - {"y-/x+/z+", dirYM_XP_ZP}, - {"y+/x-/z-", dirYP_XM_ZM}, - {"y+/x-/z+", dirYP_XM_ZP}, - {"y+/x+/z-", dirYP_XP_ZM}, - {"y+/x+/z+", dirYP_XP_ZP}, - }; - auto lcValue = StrToLower(a_Value); - for (size_t i = 0; i < ARRAYCOUNT(StringDirections); i++) - { - if (strcmp(lcValue.c_str(), StringDirections[i].m_String) == 0) - { - a_Out = StringDirections[i].m_Value; - return true; - } - } - - // Not understood, failure: - return false; -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cPlacedPiece: - -cPlacedPiece::cPlacedPiece(const cPlacedPiece * a_Parent, const cPiece & a_Piece, const Vector3i & a_Coords, int a_NumCCWRotations) : - m_Parent(a_Parent), - m_Piece(&a_Piece), - m_Coords(a_Coords), - m_NumCCWRotations(a_NumCCWRotations), - m_HasBeenMovedToGround(false) -{ - m_Depth = (m_Parent == nullptr) ? 0 : (m_Parent->GetDepth() + 1); - m_HitBox = a_Piece.RotateMoveHitBox(a_NumCCWRotations, a_Coords.x, a_Coords.y, a_Coords.z); - m_HitBox.Sort(); -} - - - - - -cPiece::cConnector cPlacedPiece::GetRotatedConnector(size_t a_Index) const -{ - cPiece::cConnectors Connectors = m_Piece->GetConnectors(); - ASSERT(Connectors.size() >= a_Index); - return m_Piece->RotateMoveConnector(Connectors[a_Index], m_NumCCWRotations, m_Coords.x, m_Coords.y, m_Coords.z); -} - - - - - -cPiece::cConnector cPlacedPiece::GetRotatedConnector(const cPiece::cConnector & a_Connector) const -{ - return m_Piece->RotateMoveConnector(a_Connector, m_NumCCWRotations, m_Coords.x, m_Coords.y, m_Coords.z); -} - - - - - -void cPlacedPiece::MoveToGroundBy(int a_OffsetY) -{ - m_Coords.y += a_OffsetY; - m_HasBeenMovedToGround = true; -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cPieceGenerator: - -cPieceGenerator::cPieceGenerator(cPiecePool & a_PiecePool, int a_Seed) : - m_PiecePool(a_PiecePool), - m_Noise(a_Seed), - m_Seed(a_Seed) -{ -} - - - - - -void cPieceGenerator::FreePieces(cPlacedPieces & a_PlacedPieces) -{ - for (cPlacedPieces::iterator itr = a_PlacedPieces.begin(), end = a_PlacedPieces.end(); itr != end; ++itr) - { - delete *itr; - } // for itr - a_PlacedPieces[] - a_PlacedPieces.clear(); -} - - - - - -cPlacedPiece * cPieceGenerator::PlaceStartingPiece(int a_BlockX, int a_BlockZ, cFreeConnectors & a_OutConnectors) -{ - m_PiecePool.Reset(); - int rnd = m_Noise.IntNoise2DInt(a_BlockX, a_BlockZ) / 7; - - // Choose a random one of the starting pieces: - cPieces StartingPieces = m_PiecePool.GetStartingPieces(); - int Total = 0; - for (cPieces::const_iterator itr = StartingPieces.begin(), end = StartingPieces.end(); itr != end; ++itr) - { - Total += m_PiecePool.GetStartingPieceWeight(**itr); - } - cPiece * StartingPiece; - if (Total > 0) - { - int Chosen = rnd % Total; - StartingPiece = StartingPieces.front(); - for (cPieces::const_iterator itr = StartingPieces.begin(), end = StartingPieces.end(); itr != end; ++itr) - { - Chosen -= m_PiecePool.GetStartingPieceWeight(**itr); - if (Chosen <= 0) - { - StartingPiece = *itr; - break; - } - } - } - else - { - // All pieces returned zero weight, but we need one to start. Choose with equal chance: - StartingPiece = StartingPieces[static_cast(rnd) % StartingPieces.size()]; - } - rnd = rnd >> 16; - - // Choose a random supported rotation: - int Rotations[4] = {0}; - int NumRotations = 1; - for (size_t i = 1; i < ARRAYCOUNT(Rotations); i++) - { - if (StartingPiece->CanRotateCCW(static_cast(i))) - { - Rotations[NumRotations] = static_cast(i); - NumRotations += 1; - } - } - int Rotation = Rotations[rnd % NumRotations]; - int BlockY = StartingPiece->GetStartingPieceHeight(a_BlockX, a_BlockZ); - ASSERT(BlockY >= 0); // The vertical strategy should have been provided and should give valid coords - - cPlacedPiece * res = new cPlacedPiece(nullptr, *StartingPiece, Vector3i(a_BlockX, BlockY, a_BlockZ), Rotation); - - // Place the piece's connectors into a_OutConnectors: - const cPiece::cConnectors & Conn = StartingPiece->GetConnectors(); - for (cPiece::cConnectors::const_iterator itr = Conn.begin(), end = Conn.end(); itr != end; ++itr) - { - a_OutConnectors.push_back( - cFreeConnector(res, StartingPiece->RotateMoveConnector(*itr, Rotation, a_BlockX, BlockY, a_BlockZ)) - ); - } - - return res; -} - - - - - -bool cPieceGenerator::TryPlacePieceAtConnector( - const cPlacedPiece & a_ParentPiece, - const cPiece::cConnector & a_Connector, - cPlacedPieces & a_OutPieces, - cPieceGenerator::cFreeConnectors & a_OutConnectors -) -{ - // Translation of direction - direction -> number of CCW rotations needed: - // You need DirectionRotationTable[rot2][rot1] CCW turns to connect rot1 to rot2 (they are opposite) - // -1 if not possible - static const int DirectionRotationTable[14][14] = - { - /* YM, YP, ZM, ZP, XM, XP, YM-XM-ZM, YM-XM-ZP, YM-XP-ZM, YM-XP-ZP, YP-XM-ZM, YP-XM-ZP, YP-XP-ZM, YP-XP-ZP */ - /* YM */ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - /* YP */ {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - /* ZM */ {-1, -1, 2, 0, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1}, - /* ZP */ {-1, -1, 0, 2, 3, 1, -1, -1, -1, -1, -1, -1, -1, -1}, - /* XM */ {-1, -1, 3, 1, 2, 0, -1, -1, -1, -1, -1, -1, -1, -1}, - /* XP */ {-1, -1, 1, 3, 0, 2, -1, -1, -1, -1, -1, -1, -1, -1}, - /* YM-XM-ZM */ {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 3, 1, 2}, - /* YM-XM-ZP */ {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 0, 2, 3}, - /* YM-XP-ZM */ {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3, 2, 0, 1}, - /* YM-XP-ZP */ {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 1, 3, 0}, - /* YP-XM-ZM */ {-1, -1, -1, -1, -1, -1, 0, 3, 1, 2, -1, -1, -1, -1}, - /* YP-XM-ZP */ {-1, -1, -1, -1, -1, -1, 1, 0, 2, 3, -1, -1, -1, -1}, - /* YP-XP-ZM */ {-1, -1, -1, -1, -1, -1, 3, 2, 0, 1, -1, -1, -1, -1}, - /* YP-XP-ZP */ {-1, -1, -1, -1, -1, -1, 2, 1, 3, 0, -1, -1, -1, -1}, - }; - - // Get a list of available connections: - ASSERT(a_Connector.m_Direction < ARRAYCOUNT(DirectionRotationTable)); - const int * RotTable = DirectionRotationTable[a_Connector.m_Direction]; - cConnections Connections; - int WantedConnectorType = -a_Connector.m_Type; - cPieces AvailablePieces = m_PiecePool.GetPiecesWithConnector(WantedConnectorType); - Connections.reserve(AvailablePieces.size()); - Vector3i ConnPos = cPiece::cConnector::AddDirection(a_Connector.m_Pos, a_Connector.m_Direction); // The position at which the new connector should be placed - 1 block away from the current connector - int WeightTotal = 0; - for (cPieces::iterator itrP = AvailablePieces.begin(), endP = AvailablePieces.end(); itrP != endP; ++itrP) - { - // Get the relative chance of this piece being generated in this path: - int Weight = m_PiecePool.GetPieceWeight(a_ParentPiece, a_Connector, **itrP); - if (Weight <= 0) - { - continue; - } - - // Try fitting each of the piece's connector: - cPiece::cConnectors Connectors = (*itrP)->GetConnectors(); - auto verticalLimit = (*itrP)->GetVerticalLimit(); - for (cPiece::cConnectors::iterator itrC = Connectors.begin(), endC = Connectors.end(); itrC != endC; ++itrC) - { - if (itrC->m_Type != WantedConnectorType) - { - continue; - } - // This is a same-type connector, find out how to rotate to it: - ASSERT(itrC->m_Direction < ARRAYCOUNT(DirectionRotationTable[0])); - int NumCCWRotations = RotTable[itrC->m_Direction]; - if ((NumCCWRotations < 0) || !(*itrP)->CanRotateCCW(NumCCWRotations)) - { - // Doesn't support this rotation - continue; - } - - // Check if the piece's VerticalLimit allows this connection: - if ((verticalLimit != nullptr) && (!verticalLimit->CanBeAtHeight(ConnPos.x, ConnPos.z, ConnPos.y - itrC->m_Pos.y))) - { - continue; - } - - if (!CheckConnection(a_Connector, ConnPos, **itrP, *itrC, NumCCWRotations, a_OutPieces)) - { - // Doesn't fit in this rotation - continue; - } - // Fits, add it to list of possibile connections: - Connections.push_back(cConnection(**itrP, *itrC, NumCCWRotations, Weight)); - WeightTotal += Weight; - } // for itrC - Connectors[] - } // for itrP - AvailablePieces[] - if (Connections.empty()) - { - // No available connections, bail out - return false; - } - ASSERT(WeightTotal > 0); - - // Choose a random connection from the list, based on the weights: - int rnd = (m_Noise.IntNoise3DInt(a_Connector.m_Pos.x, a_Connector.m_Pos.y, a_Connector.m_Pos.z) / 7) % WeightTotal; - size_t ChosenIndex = 0; - for (cConnections::const_iterator itr = Connections.begin(), end = Connections.end(); itr != end; ++itr, ++ChosenIndex) - { - rnd -= itr->m_Weight; - if (rnd <= 0) - { - // This is the piece to choose - break; - } - } - cConnection & Conn = Connections[ChosenIndex]; - - // Place the piece: - Vector3i NewPos = Conn.m_Piece->RotatePos(Conn.m_Connector.m_Pos, Conn.m_NumCCWRotations); - ConnPos -= NewPos; - cPlacedPiece * PlacedPiece = new cPlacedPiece(&a_ParentPiece, *(Conn.m_Piece), ConnPos, Conn.m_NumCCWRotations); - a_OutPieces.push_back(PlacedPiece); - - // Add the new piece's connectors to the list of free connectors: - cPiece::cConnectors Connectors = Conn.m_Piece->GetConnectors(); - for (cPiece::cConnectors::const_iterator itr = Connectors.begin(), end = Connectors.end(); itr != end; ++itr) - { - if (itr->m_Pos.Equals(Conn.m_Connector.m_Pos)) - { - // This is the connector through which we have been connected to the parent, don't add - continue; - } - a_OutConnectors.push_back(cFreeConnector(PlacedPiece, Conn.m_Piece->RotateMoveConnector(*itr, Conn.m_NumCCWRotations, ConnPos.x, ConnPos.y, ConnPos.z))); - } - - return true; -} - - - - - -bool cPieceGenerator::CheckConnection( - const cPiece::cConnector & a_ExistingConnector, - const Vector3i & a_ToPos, - const cPiece & a_Piece, - const cPiece::cConnector & a_NewConnector, - int a_NumCCWRotations, - const cPlacedPieces & a_OutPieces -) -{ - // For each placed piece, test the hitbox against the new piece: - cCuboid RotatedHitBox = a_Piece.RotateHitBoxToConnector(a_NewConnector, a_ToPos, a_NumCCWRotations); - RotatedHitBox.Sort(); - for (cPlacedPieces::const_iterator itr = a_OutPieces.begin(), end = a_OutPieces.end(); itr != end; ++itr) - { - if ((*itr)->GetHitBox().DoesIntersect(RotatedHitBox)) - { - return false; - } - } - return true; -} - - - - - -//* -// DEBUG: -void cPieceGenerator::DebugConnectorPool(const cPieceGenerator::cFreeConnectors & a_ConnectorPool, size_t a_NumProcessed) -{ - printf(" Connector pool: " SIZE_T_FMT " items\n", a_ConnectorPool.size() - a_NumProcessed); - size_t idx = 0; - - typedef cPieceGenerator::cFreeConnectors::difference_type difType; - - for (auto itr = a_ConnectorPool.cbegin() + static_cast(a_NumProcessed), end = a_ConnectorPool.cend(); itr != end; ++itr, ++idx) - { - printf(" " SIZE_T_FMT ": {%d, %d, %d}, type %d, direction %s, depth %d\n", - idx, - itr->m_Connector.m_Pos.x, itr->m_Connector.m_Pos.y, itr->m_Connector.m_Pos.z, - itr->m_Connector.m_Type, - cPiece::cConnector::DirectionToString(itr->m_Connector.m_Direction), - itr->m_Piece->GetDepth() - ); - } // for itr - a_ConnectorPool[] -} -//*/ - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cPieceGenerator::cConnection: - -cPieceGenerator::cConnection::cConnection(cPiece & a_Piece, cPiece::cConnector & a_Connector, int a_NumCCWRotations, int a_Weight) : - m_Piece(&a_Piece), - m_Connector(a_Connector), - m_NumCCWRotations(a_NumCCWRotations), - m_Weight(a_Weight) -{ -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cPieceGenerator::cFreeConnector: - -cPieceGenerator::cFreeConnector::cFreeConnector(cPlacedPiece * a_Piece, const cPiece::cConnector & a_Connector) : - m_Piece(a_Piece), - m_Connector(a_Connector) -{ -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cBFSPieceGenerator: - -cBFSPieceGenerator::cBFSPieceGenerator(cPiecePool & a_PiecePool, int a_Seed) : - super(a_PiecePool, a_Seed) -{ -} - - - - - -void cBFSPieceGenerator::PlacePieces(int a_BlockX, int a_BlockZ, int a_MaxDepth, cPlacedPieces & a_OutPieces) -{ - a_OutPieces.clear(); - cFreeConnectors ConnectorPool; - - // Place the starting piece: - a_OutPieces.push_back(PlaceStartingPiece(a_BlockX, a_BlockZ, ConnectorPool)); - - /* - // DEBUG: - printf("Placed the starting piece at {%d, %d, %d}\n", a_BlockX, a_BlockY, a_BlockZ); - cCuboid Hitbox = a_OutPieces[0]->GetHitBox(); - Hitbox.Sort(); - printf(" Hitbox: {%d, %d, %d} - {%d, %d, %d} (%d * %d * %d)\n", - Hitbox.p1.x, Hitbox.p1.y, Hitbox.p1.z, - Hitbox.p2.x, Hitbox.p2.y, Hitbox.p2.z, - Hitbox.DifX() + 1, Hitbox.DifY() + 1, Hitbox.DifZ() + 1 - ); - DebugConnectorPool(ConnectorPool, 0); - //*/ - - // Place pieces at the available connectors: - /* - Instead of removing them one by one from the pool, we process them sequentially and take note of the last - processed one. To save on memory, once the number of processed connectors reaches a big number, a chunk - of the connectors is removed. - */ - size_t NumProcessed = 0; - while (ConnectorPool.size() > NumProcessed) - { - cFreeConnector & Conn = ConnectorPool[NumProcessed]; - if (Conn.m_Piece->GetDepth() < a_MaxDepth) - { - if (TryPlacePieceAtConnector(*Conn.m_Piece, Conn.m_Connector, a_OutPieces, ConnectorPool)) - { - /* - // DEBUG: - const cPlacedPiece * NewPiece = a_OutPieces.back(); - const Vector3i & Coords = NewPiece->GetCoords(); - printf("Placed a new piece at {%d, %d, %d}, rotation %d\n", Coords.x, Coords.y, Coords.z, NewPiece->GetNumCCWRotations()); - cCuboid Hitbox = NewPiece->GetHitBox(); - Hitbox.Sort(); - printf(" Hitbox: {%d, %d, %d} - {%d, %d, %d} (%d * %d * %d)\n", - Hitbox.p1.x, Hitbox.p1.y, Hitbox.p1.z, - Hitbox.p2.x, Hitbox.p2.y, Hitbox.p2.z, - Hitbox.DifX() + 1, Hitbox.DifY() + 1, Hitbox.DifZ() + 1 - ); - DebugConnectorPool(ConnectorPool, NumProcessed + 1); - //*/ - } - } - NumProcessed++; - if (NumProcessed > 1000) - { - typedef cPieceGenerator::cFreeConnectors::difference_type difType; - ConnectorPool.erase(ConnectorPool.begin(), ConnectorPool.begin() + static_cast(NumProcessed)); - NumProcessed = 0; - } - } -} - - - - diff --git a/src/Generating/PieceGenerator.h b/src/Generating/PieceGenerator.h deleted file mode 100644 index 1900d9d02..000000000 --- a/src/Generating/PieceGenerator.h +++ /dev/null @@ -1,446 +0,0 @@ - -// PieceGenerator.h - -// Declares the cBFSPieceGenerator class and cDFSPieceGenerator class -// representing base classes for generating structures composed of individual "pieces" - -/* -Each uses a slightly different approach to generating: - - DFS extends pieces one by one until it hits the configured depth (or can't connect another piece anymore), - then starts looking at adjacent connectors (like depth-first search). - - BFS keeps a pool of currently-open connectors, chooses one at random and tries to place a piece on it, - thus possibly extending the pool of open connectors (like breadth-first search). -*/ - - - - - -#pragma once - -#include "ComposableGenerator.h" -#include "../Defines.h" -#include "../Cuboid.h" -#include "../Noise/Noise.h" - - - - - -/** Represents a single piece. Can have multiple connectors of different types where other pieces can connect. */ -class cPiece -{ -public: - // Force a virtual destructor in all descendants - virtual ~cPiece() {} - - struct cConnector - { - enum eDirection - { - // The following values correspond to equivalent eBlockFace values: - dirXM = BLOCK_FACE_XM, // Pointing towards the X- side of the prefab - dirXP = BLOCK_FACE_XP, // Pointing towards the X+ side of the prefab - dirYM = BLOCK_FACE_YM, // Pointing towards the Y- side of the prefab, doesn't change with rotation around the Y axis - dirYP = BLOCK_FACE_YP, // Pointing towards the Y+ side of the prefab, doesn't change with rotation around the Y axis - dirZM = BLOCK_FACE_ZM, // Pointing towards the Z- side of the prefab - dirZP = BLOCK_FACE_ZP, // Pointing towards the Z+ side of the prefab - - // Special kind of the vertical connectors (changes with rotation around the Y axis) - dirYM_XM_ZM = BLOCK_FACE_MAX + 1, // Pointing towards the Y- side of the prefab, conceptually at the X- Z- corner of the block - dirYM_XM_ZP, // Pointing towards the Y- side of the prefab, conceptually at the X- Z+ corner of the block - dirYM_XP_ZM, // Pointing towards the Y- side of the prefab, conceptually at the X+ Z- corner of the block - dirYM_XP_ZP, // Pointing towards the Y- side of the prefab, conceptually at the X+ Z+ corner of the block - dirYP_XM_ZM, // Pointing towards the Y+ side of the prefab, conceptually at the X- Z- corner of the block - dirYP_XM_ZP, // Pointing towards the Y+ side of the prefab, conceptually at the X- Z+ corner of the block - dirYP_XP_ZM, // Pointing towards the Y+ side of the prefab, conceptually at the X+ Z- corner of the block - dirYP_XP_ZP, // Pointing towards the Y+ side of the prefab, conceptually at the X+ Z+ corner of the block - }; - - /** Position relative to the piece */ - Vector3i m_Pos; - - /** Type of the connector. Any arbitrary number; the generator connects only connectors of opposite - (negative) types. */ - int m_Type; - - /** Direction in which the connector is facing. - Will be matched by the opposite direction for the connecting connector. */ - eDirection m_Direction; - - cConnector(int a_X, int a_Y, int a_Z, int a_Type, eDirection a_Direction); - cConnector(const Vector3i & a_Pos, int a_Type, eDirection a_Direction); - - /** Returns the position of the block that has the specified direction from the specified position. - Similar to ::AddFaceDirection() */ - static Vector3i AddDirection(const Vector3i & a_Pos, eDirection a_Direction); - - /** Returns the string representation of the direction. - For debugging purposes. */ - static const char * DirectionToString(eDirection a_Direction); - - /** Returns true if the specified number corresponds to a valid eDirection. */ - static bool IsValidDirection(int a_Direction); - - /** Returns the direction corresponding to the given direction rotated 180 degrees around the Y axis. */ - static eDirection RotateDirection(eDirection a_Direction); - - /** Returns the direction corresponding to the given direction rotated 90 degrees CCW around the Y axis. */ - static eDirection RotateDirectionCCW(eDirection a_Direction); - - /** Returns the direction corresponding to the given direction rotated 90 degrees CW around the Y axis. */ - static eDirection RotateDirectionCW(eDirection a_Direction); - - /** Converts the string representation of a direction into the eDirection enum value. - Returns true if successful, false on failure. - Accepts both numbers and string representations such as "x+" or "Y+X-Z+". */ - static bool StringToDirection(const AString & a_Value, eDirection & a_Out); - }; - - typedef std::vector cConnectors; - - - /** Base class (interface) for strategies for placing the starting pieces vertically. - Descendants can override the GetVerticalPlacement() method to provide custom placement decisions. */ - class cVerticalStrategy - { - public: - // Force a virtual destructor in descendants - virtual ~cVerticalStrategy() {} - - /** Returns the Y coord of the piece */ - virtual int GetVerticalPlacement(int a_BlockX, int a_BlockZ) = 0; - - /** Initializes the strategy's parameters from the string representation. - a_Params is the string containing only the parameters (substring after the first pipe character in the strategy description string). - If a_LogWarnings is true, logs any problems to the console. - Returns true if successful, false if the string parsing failed. - Used when loading the strategy from a file. */ - virtual bool InitializeFromString(const AString & a_Params, bool a_LogWarnings) = 0; - - /** Called when the piece pool is assigned to a generator, - so that the strategies may bind to the underlying subgenerators. */ - virtual void AssignGens(int a_Seed, cBiomeGenPtr & a_BiomeGen, cTerrainHeightGenPtr & a_TerrainHeightGen, int a_SeaLevel) {} - }; - - typedef SharedPtr cVerticalStrategyPtr; - - - /** Base class (interface) for the vertical limit of piece placement. - Each placed piece can have a limit, represented by this class, that gets queried for validity of the placement. */ - class cVerticalLimit - { - public: - virtual ~cVerticalLimit() {} - - /** Called to inquire whether the specified piece can be placed at the specified height. - a_BlockX, a_BlockZ is the column of the connector that is being queried. - a_Height is the requested height of the piece's lowest block. */ - virtual bool CanBeAtHeight(int a_BlockX, int a_BlockZ, int a_Height) = 0; - - /** Initializes the limit's parameters from the string representation. - a_Params is the string containing only the parameters (substring after the first pipe character in the limit description string). - Returns true if successful, false if the string parsing failed. - If a_LogWarnings is true, any error while parsing the string is output to the server console. - Used when loading the limit from a file. */ - virtual bool InitializeFromString(const AString & a_Params, bool a_LogWarnings) = 0; - - /** Called when the piece pool is assigned to a generator, - so that the limits may bind to the underlying subgenerators. */ - virtual void AssignGens(int a_Seed, cBiomeGenPtr & a_BiomeGen, cTerrainHeightGenPtr & a_TerrainHeightGen, int a_SeaLevel) {} - }; - - typedef SharedPtr cVerticalLimitPtr; - - - /** The strategy used for vertical placement of this piece when it is used as a starting piece. */ - cVerticalStrategyPtr m_VerticalStrategy; - - /** The checker that verifies each placement's vertical position. */ - cVerticalLimitPtr m_VerticalLimit; - - - /** Returns all of the available connectors that the piece has. - Each connector has a (relative) position in the piece, and a type associated with it. */ - virtual cConnectors GetConnectors(void) const = 0; - - /** Returns the dimensions of this piece. - The dimensions cover the entire piece, there is no block that the piece generates outside of this size. */ - virtual Vector3i GetSize(void) const = 0; - - /** Returns the "hitbox" of this piece. - A hitbox is what is compared and must not intersect other pieces' hitboxes when generating. */ - virtual cCuboid GetHitBox(void) const = 0; - - /** Returns true if the piece can be rotated CCW the specific number of 90-degree turns. */ - virtual bool CanRotateCCW(int a_NumRotations) const = 0; - - /** Returns the height, based on m_VerticalStrategy, for this piece when used as the starting piece. - If there's no strategy assigned to this piece, returns -1. */ - int GetStartingPieceHeight(int a_BlockX, int a_BlockZ) - { - if (m_VerticalStrategy != nullptr) - { - return m_VerticalStrategy->GetVerticalPlacement(a_BlockX, a_BlockZ); - } - return -1; - } - - void SetVerticalStrategy(cVerticalStrategyPtr a_VerticalStrategy) - { - m_VerticalStrategy = a_VerticalStrategy; - } - - cVerticalStrategyPtr GetVerticalStrategy(void) const - { - return m_VerticalStrategy; - } - - cVerticalLimitPtr GetVerticalLimit(void) const - { - return m_VerticalLimit; - } - - /** Sets the vertical strategy based on the description in the string. - If a_LogWarnings is true, logs the parsing problems into the server console. - Returns true if successful, false if strategy parsing failed (no strategy assigned). */ - bool SetVerticalStrategyFromString(const AString & a_StrategyDesc, bool a_LogWarnings); - - /** Sets the vertical limit based on the description string. - Returns true if successful, false if limit parsing failed (no limit assigned). - If a_LogWarnings is true, any problem is reported into the server console. */ - bool SetVerticalLimitFromString(const AString & a_LimitDesc, bool a_LogWarnings); - - /** Returns a copy of the a_Pos after rotating the piece the specified number of CCW rotations. */ - Vector3i RotatePos(const Vector3i & a_Pos, int a_NumCCWRotations) const; - - /** Returns a copy of the connector that is rotated and then moved by the specified amounts. */ - cConnector RotateMoveConnector(const cConnector & a_Connector, int a_NumCCWRotations, int a_MoveX, int a_MoveY, int a_MoveZ) const; - - /** Returns the hitbox after the specified number of rotations and moved so that a_MyConnector is placed at a_ToConnectorPos. */ - cCuboid RotateHitBoxToConnector(const cConnector & a_MyConnector, const Vector3i & a_ToConnectorPos, int a_NumCCWRotations) const; - - /** Returns the hitbox after the specified number of CCW rotations and moved by the specified amounts. */ - cCuboid RotateMoveHitBox(int a_NumCCWRotations, int a_MoveX, int a_MoveY, int a_MoveZ) const; -}; - -typedef std::vector cPieces; - - - - - -// fwd: -class cPlacedPiece; - - - - - -/** This class is an interface that provides pieces for the generator. It can keep track of what pieces were -placed and adjust the returned piece vectors. */ -class cPiecePool -{ -public: - // Force a virtual destructor in all descendants: - virtual ~cPiecePool() {} - - /** Returns a list of pieces that contain the specified connector type. - The cPiece pointers returned are managed by the pool and the caller doesn't free them. */ - virtual cPieces GetPiecesWithConnector(int a_ConnectorType) = 0; - - /** Returns the pieces that should be used as the starting point. - Multiple starting points are supported, one of the returned piece will be chosen. */ - virtual cPieces GetStartingPieces(void) = 0; - - /** Returns the relative weight with which the a_NewPiece is to be selected for placing under a_PlacedPiece through a_ExistingConnector. - a_ExistingConnector is the original connector, before any movement or rotation is applied to it. - This allows the pool to tweak the piece's chances, based on the previous pieces in the tree and the connector used. - The higher the number returned, the higher the chance the piece will be chosen. 0 means the piece will never be chosen. */ - virtual int GetPieceWeight( - const cPlacedPiece & a_PlacedPiece, - const cPiece::cConnector & a_ExistingConnector, - const cPiece & a_NewPiece - ) - { - return 1; - } - - /** Returns the relative weight with which the a_NewPiece is to be selected for placing as the first piece. - This allows the pool to tweak the piece's chances. - The higher the number returned, the higher the chance the piece will be chosen. 0 means the piece will not be chosen. - If all pieces return 0, a random piece is chosen, with all equal chances. */ - virtual int GetStartingPieceWeight(const cPiece & a_NewPiece) - { - return 1; - } - - /** Called after a piece is placed, to notify the pool that it has been used. - The pool may adjust the pieces it will return the next time. */ - virtual void PiecePlaced(const cPiece & a_Piece) = 0; - - /** Called when the pool has finished the current structure and should reset any piece-counters it has - for a new structure. */ - virtual void Reset(void) = 0; -}; - - - - - -/** Represents a single piece that has been placed to specific coords in the world. */ -class cPlacedPiece -{ -public: - cPlacedPiece(const cPlacedPiece * a_Parent, const cPiece & a_Piece, const Vector3i & a_Coords, int a_NumCCWRotations); - - const cPlacedPiece * GetParent (void) const { return m_Parent; } - const cPiece & GetPiece (void) const { return *m_Piece; } - const Vector3i & GetCoords (void) const { return m_Coords; } - int GetNumCCWRotations (void) const { return m_NumCCWRotations; } - const cCuboid & GetHitBox (void) const { return m_HitBox; } - int GetDepth (void) const { return m_Depth; } - bool HasBeenMovedToGround(void) const { return m_HasBeenMovedToGround; } - - /** Returns the coords as a modifiable object. */ - Vector3i & GetCoords(void) { return m_Coords; } - - /** Returns the connector at the specified index, rotated in the actual placement. - Undefined behavior if a_Index is out of range. */ - cPiece::cConnector GetRotatedConnector(size_t a_Index) const; - - /** Returns a copy of the specified connector, modified to account for the translation and rotation for - this placement. */ - cPiece::cConnector GetRotatedConnector(const cPiece::cConnector & a_Connector) const; - - /** Moves the placed piece Y-wise by the specified offset. - Sets m_HasBeenMovedToGround to true, too. - Used eg. by village houses. */ - void MoveToGroundBy(int a_OffsetY); - -protected: - const cPlacedPiece * m_Parent; - const cPiece * m_Piece; - Vector3i m_Coords; - int m_NumCCWRotations; - cCuboid m_HitBox; // Hitbox of the placed piece, in world coords - int m_Depth; // Depth in the generated piece tree - - /** Set to true once the piece has been moved Y-wise. - Used eg. by village houses. */ - bool m_HasBeenMovedToGround; -}; - -typedef std::vector cPlacedPieces; - - - - - -class cPieceGenerator -{ -public: - cPieceGenerator(cPiecePool & a_PiecePool, int a_Seed); - - /** Cleans up all the memory used by the placed pieces. - Call this utility function instead of freeing the items on your own. */ - static void FreePieces(cPlacedPieces & a_PlacedPieces); - -protected: - /** The type used for storing a connection from one piece to another, while building the piece tree. */ - struct cConnection - { - cPiece * m_Piece; // The piece being connected - cPiece::cConnector m_Connector; // The piece's connector being used (relative non-rotated coords) - int m_NumCCWRotations; // Number of rotations necessary to match the two connectors - int m_Weight; // Relative chance that this connection will be chosen - - cConnection(cPiece & a_Piece, cPiece::cConnector & a_Connector, int a_NumCCWRotations, int a_Weight); - }; - typedef std::vector cConnections; - - /** The type used for storing a pool of connectors that will be attempted to expand by another piece. */ - struct cFreeConnector - { - cPlacedPiece * m_Piece; - cPiece::cConnector m_Connector; - - cFreeConnector(cPlacedPiece * a_Piece, const cPiece::cConnector & a_Connector); - }; - typedef std::vector cFreeConnectors; - - - cPiecePool & m_PiecePool; - cNoise m_Noise; - int m_Seed; - - - /** Selects a starting piece and places it, including its height and rotation. - Also puts the piece's connectors in a_OutConnectors. */ - cPlacedPiece * PlaceStartingPiece(int a_BlockX, int a_BlockZ, cFreeConnectors & a_OutConnectors); - - /** Tries to place a new piece at the specified (placed) connector. Returns true if successful. */ - bool TryPlacePieceAtConnector( - const cPlacedPiece & a_ParentPiece, // The existing piece to a new piece should be placed - const cPiece::cConnector & a_Connector, // The existing connector (world-coords) to which a new piece should be placed - cPlacedPieces & a_OutPieces, // Already placed pieces, to be checked for intersections - cFreeConnectors & a_OutConnectors // List of free connectors to which the new connectors will be placed - ); - - /** Checks if the specified piece would fit with the already-placed pieces, using the specified connector - and number of CCW rotations. - a_ExistingConnector is in world-coords and is already rotated properly - a_ToPos is the world-coords position on which the new connector should be placed (1 block away from a_ExistingConnector, in its Direction) - a_NewConnector is in the original (non-rotated) coords. - Returns true if the piece fits, false if not. */ - bool CheckConnection( - const cPiece::cConnector & a_ExistingConnector, // The existing connector - const Vector3i & a_ToPos, // The position on which the new connector should be placed - const cPiece & a_Piece, // The new piece - const cPiece::cConnector & a_NewConnector, // The connector of the new piece - int a_NumCCWRotations, // Number of rotations for the new piece to align the connector - const cPlacedPieces & a_OutPieces // All the already-placed pieces to check - ); - - /** DEBUG: Outputs all the connectors in the pool into stdout. - a_NumProcessed signals the number of connectors from the pool that should be considered processed (not listed). */ - void DebugConnectorPool(const cPieceGenerator::cFreeConnectors & a_ConnectorPool, size_t a_NumProcessed); -} ; - - - - - -class cBFSPieceGenerator : - public cPieceGenerator -{ - typedef cPieceGenerator super; - -public: - cBFSPieceGenerator(cPiecePool & a_PiecePool, int a_Seed); - - /** Generates a placement for pieces at the specified coords. - The Y coord is generated automatically based on the starting piece that is chosen. - Caller must free each individual cPlacedPiece in a_OutPieces using cPieceGenerator::FreePieces(). */ - void PlacePieces(int a_BlockX, int a_BlockZ, int a_MaxDepth, cPlacedPieces & a_OutPieces); -}; - - - - - -class cDFSPieceGenerator : - public cPieceGenerator -{ -public: - cDFSPieceGenerator(cPiecePool & a_PiecePool, int a_Seed); - - /** Generates a placement for pieces at the specified coords. - The Y coord is generated automatically based on the starting piece that is chosen. - Caller must free each individual cPlacedPiece in a_OutPieces using cPieceGenerator::FreePieces(). */ - void PlacePieces(int a_BlockX, int a_BlockZ, cPlacedPieces & a_OutPieces); -}; - - - - diff --git a/src/Generating/PieceGeneratorBFSTree.cpp b/src/Generating/PieceGeneratorBFSTree.cpp new file mode 100644 index 000000000..0078d53c9 --- /dev/null +++ b/src/Generating/PieceGeneratorBFSTree.cpp @@ -0,0 +1,345 @@ + +// PieceGeneratorBFSTree.cpp + +// Implements the cPieceGeneratorBFSTree class for generating structures composed of individual "pieces" in a simple tree +/* +The generator keeps a pool of currently-open connectors, chooses one at random and tries to place a piece on it, +thus possibly extending the pool of open connectors with the new piece's ones (like breadth-first search). +*/ + +#include "Globals.h" +#include "PieceGeneratorBFSTree.h" +#include "VerticalStrategy.h" +#include "VerticalLimit.h" + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cPieceGeneratorBFSTree: + +cPieceGeneratorBFSTree::cPieceGeneratorBFSTree(cPiecePool & a_PiecePool, int a_Seed): + m_PiecePool(a_PiecePool), + m_Noise(a_Seed), + m_Seed(a_Seed) +{ +} + + + + + +cPlacedPiecePtr cPieceGeneratorBFSTree::PlaceStartingPiece(int a_BlockX, int a_BlockZ, cFreeConnectors & a_OutConnectors) +{ + m_PiecePool.Reset(); + int rnd = m_Noise.IntNoise2DInt(a_BlockX, a_BlockZ) / 7; + + // Choose a random one of the starting pieces: + cPieces StartingPieces = m_PiecePool.GetStartingPieces(); + int Total = 0; + for (cPieces::const_iterator itr = StartingPieces.begin(), end = StartingPieces.end(); itr != end; ++itr) + { + Total += m_PiecePool.GetStartingPieceWeight(**itr); + } + cPiece * StartingPiece; + if (Total > 0) + { + int Chosen = rnd % Total; + StartingPiece = StartingPieces.front(); + for (cPieces::const_iterator itr = StartingPieces.begin(), end = StartingPieces.end(); itr != end; ++itr) + { + Chosen -= m_PiecePool.GetStartingPieceWeight(**itr); + if (Chosen <= 0) + { + StartingPiece = *itr; + break; + } + } + } + else + { + // All pieces returned zero weight, but we need one to start. Choose with equal chance: + StartingPiece = StartingPieces[static_cast(rnd) % StartingPieces.size()]; + } + rnd = rnd >> 16; + + // Choose a random supported rotation: + int Rotations[4] = {0}; + int NumRotations = 1; + for (size_t i = 1; i < ARRAYCOUNT(Rotations); i++) + { + if (StartingPiece->CanRotateCCW(static_cast(i))) + { + Rotations[NumRotations] = static_cast(i); + NumRotations += 1; + } + } + int Rotation = Rotations[rnd % NumRotations]; + int BlockY = StartingPiece->GetStartingPieceHeight(a_BlockX, a_BlockZ); + ASSERT(BlockY >= 0); // The vertical strategy should have been provided and should give valid coords + + cPlacedPiece * res = new cPlacedPiece(nullptr, *StartingPiece, Vector3i(a_BlockX, BlockY, a_BlockZ), Rotation); + + // Place the piece's connectors into a_OutConnectors: + const cPiece::cConnectors & Conn = StartingPiece->GetConnectors(); + for (cPiece::cConnectors::const_iterator itr = Conn.begin(), end = Conn.end(); itr != end; ++itr) + { + a_OutConnectors.push_back( + cFreeConnector(res, StartingPiece->RotateMoveConnector(*itr, Rotation, a_BlockX, BlockY, a_BlockZ)) + ); + } + + return cPlacedPiecePtr(res); +} + + + + + +bool cPieceGeneratorBFSTree::TryPlacePieceAtConnector( + const cPlacedPiece & a_ParentPiece, + const cPiece::cConnector & a_Connector, + cPlacedPieces & a_OutPieces, + cPieceGeneratorBFSTree::cFreeConnectors & a_OutConnectors +) +{ + // Get a list of available connections: + cConnections Connections; + int WantedConnectorType = -a_Connector.m_Type; + cPieces AvailablePieces = m_PiecePool.GetPiecesWithConnector(WantedConnectorType); + Connections.reserve(AvailablePieces.size()); + Vector3i ConnPos = cPiece::cConnector::AddDirection(a_Connector.m_Pos, a_Connector.m_Direction); // The position at which the new connector should be placed - 1 block away from the current connector + int WeightTotal = 0; + for (cPieces::iterator itrP = AvailablePieces.begin(), endP = AvailablePieces.end(); itrP != endP; ++itrP) + { + // Get the relative chance of this piece being generated in this path: + int Weight = m_PiecePool.GetPieceWeight(a_ParentPiece, a_Connector, **itrP); + if (Weight <= 0) + { + continue; + } + + // Try fitting each of the piece's connector: + cPiece::cConnectors Connectors = (*itrP)->GetConnectors(); + auto verticalLimit = (*itrP)->GetVerticalLimit(); + for (cPiece::cConnectors::iterator itrC = Connectors.begin(), endC = Connectors.end(); itrC != endC; ++itrC) + { + if (itrC->m_Type != WantedConnectorType) + { + continue; + } + // This is a same-type connector, find out how to rotate to it: + int NumCCWRotations = cPiece::cConnector::GetNumCCWRotationsToFit(a_Connector.m_Direction, itrC->m_Direction); + if ((NumCCWRotations < 0) || !(*itrP)->CanRotateCCW(NumCCWRotations)) + { + // Doesn't support this rotation + continue; + } + + // Check if the piece's VerticalLimit allows this connection: + if ((verticalLimit != nullptr) && (!verticalLimit->CanBeAtHeight(ConnPos.x, ConnPos.z, ConnPos.y - itrC->m_Pos.y))) + { + continue; + } + + if (!CheckConnection(a_Connector, ConnPos, **itrP, *itrC, NumCCWRotations, a_OutPieces)) + { + // Doesn't fit in this rotation + continue; + } + // Fits, add it to list of possibile connections: + Connections.push_back(cConnection(**itrP, *itrC, NumCCWRotations, Weight)); + WeightTotal += Weight; + } // for itrC - Connectors[] + } // for itrP - AvailablePieces[] + if (Connections.empty()) + { + // No available connections, bail out + return false; + } + ASSERT(WeightTotal > 0); + + // Choose a random connection from the list, based on the weights: + int rnd = (m_Noise.IntNoise3DInt(a_Connector.m_Pos.x, a_Connector.m_Pos.y, a_Connector.m_Pos.z) / 7) % WeightTotal; + size_t ChosenIndex = 0; + for (cConnections::const_iterator itr = Connections.begin(), end = Connections.end(); itr != end; ++itr, ++ChosenIndex) + { + rnd -= itr->m_Weight; + if (rnd <= 0) + { + // This is the piece to choose + break; + } + } + cConnection & Conn = Connections[ChosenIndex]; + + // Place the piece: + Vector3i NewPos = Conn.m_Piece->RotatePos(Conn.m_Connector.m_Pos, Conn.m_NumCCWRotations); + ConnPos -= NewPos; + auto PlacedPiece = cpp14::make_unique(&a_ParentPiece, *(Conn.m_Piece), ConnPos, Conn.m_NumCCWRotations); + + // Add the new piece's connectors to the list of free connectors: + cPiece::cConnectors Connectors = Conn.m_Piece->GetConnectors(); + for (cPiece::cConnectors::const_iterator itr = Connectors.begin(), end = Connectors.end(); itr != end; ++itr) + { + if (itr->m_Pos.Equals(Conn.m_Connector.m_Pos)) + { + // This is the connector through which we have been connected to the parent, don't add + continue; + } + a_OutConnectors.push_back(cFreeConnector(PlacedPiece.get(), Conn.m_Piece->RotateMoveConnector(*itr, Conn.m_NumCCWRotations, ConnPos.x, ConnPos.y, ConnPos.z))); + } + a_OutPieces.push_back(std::move(PlacedPiece)); + + return true; +} + + + + + +bool cPieceGeneratorBFSTree::CheckConnection( + const cPiece::cConnector & a_ExistingConnector, + const Vector3i & a_ToPos, + const cPiece & a_Piece, + const cPiece::cConnector & a_NewConnector, + int a_NumCCWRotations, + const cPlacedPieces & a_OutPieces +) +{ + // For each placed piece, test the hitbox against the new piece: + cCuboid RotatedHitBox = a_Piece.RotateHitBoxToConnector(a_NewConnector, a_ToPos, a_NumCCWRotations); + RotatedHitBox.Sort(); + for (cPlacedPieces::const_iterator itr = a_OutPieces.begin(), end = a_OutPieces.end(); itr != end; ++itr) + { + if ((*itr)->GetHitBox().DoesIntersect(RotatedHitBox)) + { + return false; + } + } + return true; +} + + + + + +void cPieceGeneratorBFSTree::PlacePieces(int a_BlockX, int a_BlockZ, int a_MaxDepth, cPlacedPieces & a_OutPieces) +{ + a_OutPieces.clear(); + cFreeConnectors ConnectorPool; + + // Place the starting piece: + a_OutPieces.push_back(PlaceStartingPiece(a_BlockX, a_BlockZ, ConnectorPool)); + + /* + // DEBUG: + printf("Placed the starting piece at {%d, %d, %d}\n", a_BlockX, a_BlockY, a_BlockZ); + cCuboid Hitbox = a_OutPieces[0]->GetHitBox(); + Hitbox.Sort(); + printf(" Hitbox: {%d, %d, %d} - {%d, %d, %d} (%d * %d * %d)\n", + Hitbox.p1.x, Hitbox.p1.y, Hitbox.p1.z, + Hitbox.p2.x, Hitbox.p2.y, Hitbox.p2.z, + Hitbox.DifX() + 1, Hitbox.DifY() + 1, Hitbox.DifZ() + 1 + ); + DebugConnectorPool(ConnectorPool, 0); + //*/ + + // Place pieces at the available connectors: + /* + Instead of removing them one by one from the pool, we process them sequentially and take note of the last + processed one. To save on memory, once the number of processed connectors reaches a big number, a chunk + of the connectors is removed. + */ + size_t NumProcessed = 0; + while (ConnectorPool.size() > NumProcessed) + { + cFreeConnector & Conn = ConnectorPool[NumProcessed]; + if (Conn.m_Piece->GetDepth() < a_MaxDepth) + { + if (TryPlacePieceAtConnector(*Conn.m_Piece, Conn.m_Connector, a_OutPieces, ConnectorPool)) + { + /* + // DEBUG: + const cPlacedPiece * NewPiece = a_OutPieces.back(); + const Vector3i & Coords = NewPiece->GetCoords(); + printf("Placed a new piece at {%d, %d, %d}, rotation %d\n", Coords.x, Coords.y, Coords.z, NewPiece->GetNumCCWRotations()); + cCuboid Hitbox = NewPiece->GetHitBox(); + Hitbox.Sort(); + printf(" Hitbox: {%d, %d, %d} - {%d, %d, %d} (%d * %d * %d)\n", + Hitbox.p1.x, Hitbox.p1.y, Hitbox.p1.z, + Hitbox.p2.x, Hitbox.p2.y, Hitbox.p2.z, + Hitbox.DifX() + 1, Hitbox.DifY() + 1, Hitbox.DifZ() + 1 + ); + DebugConnectorPool(ConnectorPool, NumProcessed + 1); + //*/ + } + } + NumProcessed++; + if (NumProcessed > 1000) + { + typedef cPieceGeneratorBFSTree::cFreeConnectors::difference_type difType; + ConnectorPool.erase(ConnectorPool.begin(), ConnectorPool.begin() + static_cast(NumProcessed)); + NumProcessed = 0; + } + } +} + + + + + +//* +// DEBUG: +void cPieceGeneratorBFSTree::DebugConnectorPool(const cPieceGeneratorBFSTree::cFreeConnectors & a_ConnectorPool, size_t a_NumProcessed) +{ + printf(" Connector pool: " SIZE_T_FMT " items\n", a_ConnectorPool.size() - a_NumProcessed); + size_t idx = 0; + + typedef cPieceGeneratorBFSTree::cFreeConnectors::difference_type difType; + + for (auto itr = a_ConnectorPool.cbegin() + static_cast(a_NumProcessed), end = a_ConnectorPool.cend(); itr != end; ++itr, ++idx) + { + printf(" " SIZE_T_FMT ": {%d, %d, %d}, type %d, direction %s, depth %d\n", + idx, + itr->m_Connector.m_Pos.x, itr->m_Connector.m_Pos.y, itr->m_Connector.m_Pos.z, + itr->m_Connector.m_Type, + cPiece::cConnector::DirectionToString(itr->m_Connector.m_Direction), + itr->m_Piece->GetDepth() + ); + } // for itr - a_ConnectorPool[] +} +//*/ + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cPieceGeneratorBFSTree::cConnection: + +cPieceGeneratorBFSTree::cConnection::cConnection(cPiece & a_Piece, cPiece::cConnector & a_Connector, int a_NumCCWRotations, int a_Weight) : + m_Piece(&a_Piece), + m_Connector(a_Connector), + m_NumCCWRotations(a_NumCCWRotations), + m_Weight(a_Weight) +{ +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cPieceGeneratorBFSTree::cFreeConnector: + +cPieceGeneratorBFSTree::cFreeConnector::cFreeConnector(cPlacedPiece * a_Piece, const cPiece::cConnector & a_Connector) : + m_Piece(a_Piece), + m_Connector(a_Connector) +{ +} + + + + diff --git a/src/Generating/PieceGeneratorBFSTree.h b/src/Generating/PieceGeneratorBFSTree.h new file mode 100644 index 000000000..5efb8a1f4 --- /dev/null +++ b/src/Generating/PieceGeneratorBFSTree.h @@ -0,0 +1,102 @@ + +// PieceGeneratorBFSTree.h + +// Declares the cPieceGeneratorBFSTree class for generating structures composed of individual "pieces" in a simple tree + + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "PiecePool.h" +#include "../Noise/Noise.h" + + + + + +class cPieceGeneratorBFSTree +{ +public: + /** Creates a new object tied to the specified PiecePool, using the specified seed. */ + cPieceGeneratorBFSTree(cPiecePool & a_PiecePool, int a_Seed); + + + /** Generates a placement for pieces at the specified coords. + The Y coord is generated automatically based on the starting piece that is chosen. */ + void PlacePieces(int a_BlockX, int a_BlockZ, int a_MaxDepth, cPlacedPieces & a_OutPieces); + + +protected: + + /** The type used for storing a connection from one piece to another, while building the piece tree. */ + struct cConnection + { + cPiece * m_Piece; // The piece being connected + cPiece::cConnector m_Connector; // The piece's connector being used (relative non-rotated coords) + int m_NumCCWRotations; // Number of rotations necessary to match the two connectors + int m_Weight; // Relative chance that this connection will be chosen + + cConnection(cPiece & a_Piece, cPiece::cConnector & a_Connector, int a_NumCCWRotations, int a_Weight); + }; + typedef std::vector cConnections; + + + /** The type used for storing a pool of connectors that will be attempted to expand by another piece. */ + struct cFreeConnector + { + cPlacedPiece * m_Piece; + cPiece::cConnector m_Connector; + + cFreeConnector(cPlacedPiece * a_Piece, const cPiece::cConnector & a_Connector); + }; + typedef std::vector cFreeConnectors; + + + /** The pool from which pieces are taken. */ + cPiecePool & m_PiecePool; + + /** The noise used for random number generation. */ + cNoise m_Noise; + + /** The seed used by this generator. */ + int m_Seed; + + + /** Selects a starting piece and places it, including its height and rotation. + Also puts the piece's connectors in a_OutConnectors. */ + cPlacedPiecePtr PlaceStartingPiece(int a_BlockX, int a_BlockZ, cFreeConnectors & a_OutConnectors); + + /** Tries to place a new piece at the specified (placed) connector. Returns true if successful. */ + bool TryPlacePieceAtConnector( + const cPlacedPiece & a_ParentPiece, // The existing piece to a new piece should be placed + const cPiece::cConnector & a_Connector, // The existing connector (world-coords) to which a new piece should be placed + cPlacedPieces & a_OutPieces, // Already placed pieces, to be checked for intersections + cFreeConnectors & a_OutConnectors // List of free connectors to which the new connectors will be placed + ); + + /** Checks if the specified piece would fit with the already-placed pieces, using the specified connector + and number of CCW rotations. + a_ExistingConnector is in world-coords and is already rotated properly + a_ToPos is the world-coords position on which the new connector should be placed (1 block away from a_ExistingConnector, in its Direction) + a_NewConnector is in the original (non-rotated) coords. + Returns true if the piece fits, false if not. */ + bool CheckConnection( + const cPiece::cConnector & a_ExistingConnector, // The existing connector + const Vector3i & a_ToPos, // The position on which the new connector should be placed + const cPiece & a_Piece, // The new piece + const cPiece::cConnector & a_NewConnector, // The connector of the new piece + int a_NumCCWRotations, // Number of rotations for the new piece to align the connector + const cPlacedPieces & a_OutPieces // All the already-placed pieces to check + ); + + /** DEBUG: Outputs all the connectors in the pool into stdout. + a_NumProcessed signals the number of connectors from the pool that should be considered processed (not listed). */ + void DebugConnectorPool(const cFreeConnectors & a_ConnectorPool, size_t a_NumProcessed); +} ; + + + + diff --git a/src/Generating/PiecePool.cpp b/src/Generating/PiecePool.cpp new file mode 100644 index 000000000..201c70afd --- /dev/null +++ b/src/Generating/PiecePool.cpp @@ -0,0 +1,503 @@ +// PiecePool.cpp + +// Implements the cPiecePool class representing a pool of cPieces - "parts" of a structure, used in piece-generators +// A cPiece is a single static part of a structure that can rotate around the Y axis, has connectors to other pieces and knows how to draw itself into the world. +// The pool manages the pieces and provides lists of its pieces matching criteria, and provides relative weights for the random distribution of pieces. + +#include "Globals.h" +#include "PiecePool.h" +#include "VerticalStrategy.h" +#include "VerticalLimit.h" + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cPiece: + +bool cPiece::SetVerticalStrategyFromString(const AString & a_StrategyDesc, bool a_LogWarnings) +{ + auto strategy = CreateVerticalStrategyFromString(a_StrategyDesc, a_LogWarnings); + if (strategy == nullptr) + { + return false; + } + m_VerticalStrategy = strategy; + return true; +} + + + + + +bool cPiece::SetVerticalLimitFromString(const AString & a_LimitDesc, bool a_LogWarnings) +{ + auto limit = CreateVerticalLimitFromString(a_LimitDesc, a_LogWarnings); + if (limit == nullptr) + { + return false; + } + m_VerticalLimit = limit; + return true; +} + + + + + +Vector3i cPiece::RotatePos(const Vector3i & a_Pos, int a_NumCCWRotations) const +{ + Vector3i Size = GetSize(); + switch (a_NumCCWRotations) + { + case 0: + { + // No rotation needed + return a_Pos; + } + case 1: + { + // 1 CCW rotation: + return Vector3i(a_Pos.z, a_Pos.y, Size.x - a_Pos.x - 1); + } + case 2: + { + // 2 rotations ( = axis flip): + return Vector3i(Size.x - a_Pos.x - 1, a_Pos.y, Size.z - a_Pos.z - 1); + } + case 3: + { + // 1 CW rotation: + return Vector3i(Size.z - a_Pos.z - 1, a_Pos.y, a_Pos.x); + } + } + ASSERT(!"Unhandled rotation"); + return a_Pos; +} + + + + + +cPiece::cConnector cPiece::RotateMoveConnector(const cConnector & a_Connector, int a_NumCCWRotations, int a_MoveX, int a_MoveY, int a_MoveZ) const +{ + cPiece::cConnector res(a_Connector); + + // Rotate the res connector: + switch (a_NumCCWRotations) + { + case 0: + { + // No rotation needed + break; + } + case 1: + { + // 1 CCW rotation: + res.m_Direction = cConnector::RotateDirectionCCW(res.m_Direction); + break; + } + case 2: + { + // 2 rotations ( = axis flip): + res.m_Direction = cConnector::RotateDirection(res.m_Direction); + break; + } + case 3: + { + // 1 CW rotation: + res.m_Direction = cConnector::RotateDirectionCW(res.m_Direction); + break; + } + } + res.m_Pos = RotatePos(a_Connector.m_Pos, a_NumCCWRotations); + + // Move the res connector: + res.m_Pos.x += a_MoveX; + res.m_Pos.y += a_MoveY; + res.m_Pos.z += a_MoveZ; + + return res; +} + + + + + +cCuboid cPiece::RotateHitBoxToConnector( + const cPiece::cConnector & a_MyConnector, + const Vector3i & a_ToConnectorPos, + int a_NumCCWRotations +) const +{ + ASSERT(a_NumCCWRotations == (a_NumCCWRotations % 4)); + Vector3i ConnPos = RotatePos(a_MyConnector.m_Pos, a_NumCCWRotations); + ConnPos = a_ToConnectorPos - ConnPos; + return RotateMoveHitBox(a_NumCCWRotations, ConnPos.x, ConnPos.y, ConnPos.z); +} + + + + + +cCuboid cPiece::RotateMoveHitBox(int a_NumCCWRotations, int a_MoveX, int a_MoveY, int a_MoveZ) const +{ + ASSERT(a_NumCCWRotations == (a_NumCCWRotations % 4)); + cCuboid res = GetHitBox(); + res.p1 = RotatePos(res.p1, a_NumCCWRotations); + res.p2 = RotatePos(res.p2, a_NumCCWRotations); + res.p1.Move(a_MoveX, a_MoveY, a_MoveZ); + res.p2.Move(a_MoveX, a_MoveY, a_MoveZ); + return res; +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cPiece::cConnector: + +cPiece::cConnector::cConnector(int a_X, int a_Y, int a_Z, int a_Type, eDirection a_Direction) : + m_Pos(a_X, a_Y, a_Z), + m_Type(a_Type), + m_Direction(a_Direction) +{ +} + + + + + +cPiece::cConnector::cConnector(const Vector3i & a_Pos, int a_Type, eDirection a_Direction) : + m_Pos(a_Pos), + m_Type(a_Type), + m_Direction(a_Direction) +{ +} + + + + + +Vector3i cPiece::cConnector::AddDirection(const Vector3i & a_Pos, eDirection a_Direction) +{ + switch (a_Direction) + { + case dirXM: return Vector3i(a_Pos.x - 1, a_Pos.y, a_Pos.z); + case dirXP: return Vector3i(a_Pos.x + 1, a_Pos.y, a_Pos.z); + case dirYM: return Vector3i(a_Pos.x, a_Pos.y - 1, a_Pos.z); + case dirYP: return Vector3i(a_Pos.x, a_Pos.y + 1, a_Pos.z); + case dirZM: return Vector3i(a_Pos.x, a_Pos.y, a_Pos.z - 1); + case dirZP: return Vector3i(a_Pos.x, a_Pos.y, a_Pos.z + 1); + case dirYM_XM_ZM: return Vector3i(a_Pos.x, a_Pos.y - 1, a_Pos.z); + case dirYM_XM_ZP: return Vector3i(a_Pos.x, a_Pos.y - 1, a_Pos.z); + case dirYM_XP_ZM: return Vector3i(a_Pos.x, a_Pos.y - 1, a_Pos.z); + case dirYM_XP_ZP: return Vector3i(a_Pos.x, a_Pos.y - 1, a_Pos.z); + case dirYP_XM_ZM: return Vector3i(a_Pos.x, a_Pos.y + 1, a_Pos.z); + case dirYP_XM_ZP: return Vector3i(a_Pos.x, a_Pos.y + 1, a_Pos.z); + case dirYP_XP_ZM: return Vector3i(a_Pos.x, a_Pos.y + 1, a_Pos.z); + case dirYP_XP_ZP: return Vector3i(a_Pos.x, a_Pos.y + 1, a_Pos.z); + } + #if !defined(__clang__) + ASSERT(!"Unknown connector direction"); + return a_Pos; + #endif +} + + + + + +const char * cPiece::cConnector::DirectionToString(eDirection a_Direction) +{ + switch (a_Direction) + { + case dirXM: return "x-"; + case dirXP: return "x+"; + case dirYM: return "y-"; + case dirYP: return "y+"; + case dirZM: return "z-"; + case dirZP: return "z+"; + case dirYM_XM_ZM: return "y-x-z-"; + case dirYM_XM_ZP: return "y-x-z+"; + case dirYM_XP_ZM: return "y-x+z-"; + case dirYM_XP_ZP: return "y-x+z+"; + case dirYP_XM_ZM: return "y+x-z-"; + case dirYP_XM_ZP: return "y+x-z+"; + case dirYP_XP_ZM: return "y+x+z-"; + case dirYP_XP_ZP: return "y+x+z+"; + } + #if !defined(__clang__) + ASSERT(!"Unknown connector direction"); + return ""; + #endif +} + + + + + +bool cPiece::cConnector::IsValidDirection(int a_Direction) +{ + switch (a_Direction) + { + case dirXM: + case dirXP: + case dirYM: + case dirYP: + case dirZM: + case dirZP: + case dirYM_XM_ZM: + case dirYM_XM_ZP: + case dirYM_XP_ZM: + case dirYM_XP_ZP: + case dirYP_XM_ZM: + case dirYP_XM_ZP: + case dirYP_XP_ZM: + case dirYP_XP_ZP: + { + return true; + } + } + return false; +} + + + + + +cPiece::cConnector::eDirection cPiece::cConnector::RotateDirection(eDirection a_Direction) +{ + // 180-degree rotation: + switch (a_Direction) + { + case dirXM: return dirXP; + case dirXP: return dirXM; + case dirYM: return dirYM; + case dirYP: return dirYP; + case dirZM: return dirZM; + case dirZP: return dirZP; + case dirYM_XM_ZM: return dirYM_XP_ZP; + case dirYM_XM_ZP: return dirYM_XP_ZM; + case dirYM_XP_ZM: return dirYM_XM_ZP; + case dirYM_XP_ZP: return dirYM_XM_ZM; + case dirYP_XM_ZM: return dirYP_XP_ZP; + case dirYP_XM_ZP: return dirYP_XP_ZM; + case dirYP_XP_ZM: return dirYP_XM_ZP; + case dirYP_XP_ZP: return dirYP_XM_ZM; + } + #if !defined(__clang__) + ASSERT(!"Unknown connector direction"); + return a_Direction; + #endif +} + + + + + +cPiece::cConnector::eDirection cPiece::cConnector::RotateDirectionCCW(eDirection a_Direction) +{ + // 90 degrees CCW rotation: + switch (a_Direction) + { + case dirXM: return dirZP; + case dirXP: return dirZM; + case dirYM: return dirYM; + case dirYP: return dirYP; + case dirZM: return dirXM; + case dirZP: return dirXP; + case dirYM_XM_ZM: return dirYM_XM_ZP; + case dirYM_XM_ZP: return dirYM_XP_ZP; + case dirYM_XP_ZM: return dirYM_XM_ZM; + case dirYM_XP_ZP: return dirYM_XP_ZM; + case dirYP_XM_ZM: return dirYP_XM_ZP; + case dirYP_XM_ZP: return dirYP_XP_ZP; + case dirYP_XP_ZM: return dirYP_XM_ZM; + case dirYP_XP_ZP: return dirYP_XP_ZM; + } + #if !defined(__clang__) + ASSERT(!"Unknown connector direction"); + return a_Direction; + #endif +} + + + + + +cPiece::cConnector::eDirection cPiece::cConnector::RotateDirectionCW(eDirection a_Direction) +{ + // 90 degrees CW rotation: + switch (a_Direction) + { + case dirXM: return dirZM; + case dirXP: return dirZP; + case dirYM: return dirYM; + case dirYP: return dirYP; + case dirZM: return dirXP; + case dirZP: return dirXM; + case dirYM_XM_ZM: return dirYM_XP_ZM; + case dirYM_XM_ZP: return dirYM_XM_ZM; + case dirYM_XP_ZM: return dirYM_XP_ZP; + case dirYM_XP_ZP: return dirYM_XM_ZP; + case dirYP_XM_ZM: return dirYP_XP_ZM; + case dirYP_XM_ZP: return dirYP_XM_ZM; + case dirYP_XP_ZM: return dirYP_XP_ZP; + case dirYP_XP_ZP: return dirYP_XM_ZP; + } + #if !defined(__clang__) + ASSERT(!"Unknown connector direction"); + return a_Direction; + #endif +} + + + + + +int cPiece::cConnector::GetNumCCWRotationsToFit(eDirection a_FixedDir, eDirection a_RotatingDir) +{ + // Translation of direction - direction -> number of CCW rotations needed: + // You need DirectionRotationTable[fixed][rot] CCW turns to connect rot to fixed (they are opposite) + // -1 if not possible + static const int DirectionRotationTable[14][14] = + { + /* YM, YP, ZM, ZP, XM, XP, YM-XM-ZM, YM-XM-ZP, YM-XP-ZM, YM-XP-ZP, YP-XM-ZM, YP-XM-ZP, YP-XP-ZM, YP-XP-ZP */ + /* YM */ { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + /* YP */ {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + /* ZM */ {-1, -1, 2, 0, 1, 3, -1, -1, -1, -1, -1, -1, -1, -1}, + /* ZP */ {-1, -1, 0, 2, 3, 1, -1, -1, -1, -1, -1, -1, -1, -1}, + /* XM */ {-1, -1, 3, 1, 2, 0, -1, -1, -1, -1, -1, -1, -1, -1}, + /* XP */ {-1, -1, 1, 3, 0, 2, -1, -1, -1, -1, -1, -1, -1, -1}, + /* YM-XM-ZM */ {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 3, 1, 2}, + /* YM-XM-ZP */ {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 0, 2, 3}, + /* YM-XP-ZM */ {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3, 2, 0, 1}, + /* YM-XP-ZP */ {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 1, 3, 0}, + /* YP-XM-ZM */ {-1, -1, -1, -1, -1, -1, 0, 3, 1, 2, -1, -1, -1, -1}, + /* YP-XM-ZP */ {-1, -1, -1, -1, -1, -1, 1, 0, 2, 3, -1, -1, -1, -1}, + /* YP-XP-ZM */ {-1, -1, -1, -1, -1, -1, 3, 2, 0, 1, -1, -1, -1, -1}, + /* YP-XP-ZP */ {-1, -1, -1, -1, -1, -1, 2, 1, 3, 0, -1, -1, -1, -1}, + }; + + return DirectionRotationTable[a_FixedDir][a_RotatingDir]; +} + + + + + +bool cPiece::cConnector::StringToDirection(const AString & a_Value, eDirection & a_Out) +{ + // First try converting as a number: + int dirInt; + if (StringToInteger(a_Value, dirInt)) + { + if (!IsValidDirection(dirInt)) + { + return false; + } + a_Out = static_cast(dirInt); + return true; + } + + // Compare to string representation: + static const struct + { + const char * m_String; + eDirection m_Value; + } StringDirections[] = + { + {"x-", dirXM}, + {"x+", dirXP}, + {"y-", dirYM}, + {"y+", dirYP}, + {"z-", dirZM}, + {"z+", dirZP}, + {"y-x-z-", dirYM_XM_ZM}, + {"y-x-z+", dirYM_XM_ZP}, + {"y-x+z-", dirYM_XP_ZM}, + {"y-x+z+", dirYM_XP_ZP}, + {"y+x-z-", dirYP_XM_ZM}, + {"y+x-z+", dirYP_XM_ZP}, + {"y+x+z-", dirYP_XP_ZM}, + {"y+x+z+", dirYP_XP_ZP}, + + // Alternate names, with slashes: + {"y-/x-/z-", dirYM_XM_ZM}, + {"y-/x-/z+", dirYM_XM_ZP}, + {"y-/x+/z-", dirYM_XP_ZM}, + {"y-/x+/z+", dirYM_XP_ZP}, + {"y+/x-/z-", dirYP_XM_ZM}, + {"y+/x-/z+", dirYP_XM_ZP}, + {"y+/x+/z-", dirYP_XP_ZM}, + {"y+/x+/z+", dirYP_XP_ZP}, + }; + auto lcValue = StrToLower(a_Value); + for (size_t i = 0; i < ARRAYCOUNT(StringDirections); i++) + { + if (strcmp(lcValue.c_str(), StringDirections[i].m_String) == 0) + { + a_Out = StringDirections[i].m_Value; + return true; + } + } + + // Not understood, failure: + return false; +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cPlacedPiece: + +cPlacedPiece::cPlacedPiece(const cPlacedPiece * a_Parent, const cPiece & a_Piece, const Vector3i & a_Coords, int a_NumCCWRotations) : + m_Parent(a_Parent), + m_Piece(&a_Piece), + m_Coords(a_Coords), + m_NumCCWRotations(a_NumCCWRotations), + m_HasBeenMovedToGround(false) +{ + m_Depth = (m_Parent == nullptr) ? 0 : (m_Parent->GetDepth() + 1); + m_HitBox = a_Piece.RotateMoveHitBox(a_NumCCWRotations, a_Coords.x, a_Coords.y, a_Coords.z); + m_HitBox.Sort(); +} + + + + + +cPiece::cConnector cPlacedPiece::GetRotatedConnector(size_t a_Index) const +{ + cPiece::cConnectors Connectors = m_Piece->GetConnectors(); + ASSERT(Connectors.size() >= a_Index); + return m_Piece->RotateMoveConnector(Connectors[a_Index], m_NumCCWRotations, m_Coords.x, m_Coords.y, m_Coords.z); +} + + + + + +cPiece::cConnector cPlacedPiece::GetRotatedConnector(const cPiece::cConnector & a_Connector) const +{ + return m_Piece->RotateMoveConnector(a_Connector, m_NumCCWRotations, m_Coords.x, m_Coords.y, m_Coords.z); +} + + + + + +void cPlacedPiece::MoveToGroundBy(int a_OffsetY) +{ + m_Coords.y += a_OffsetY; + m_HasBeenMovedToGround = true; +} + + + + diff --git a/src/Generating/PiecePool.h b/src/Generating/PiecePool.h new file mode 100644 index 000000000..84f511950 --- /dev/null +++ b/src/Generating/PiecePool.h @@ -0,0 +1,335 @@ +// PiecePool.h + +// Declares the cPiecePool class representing a pool of cPieces - "parts" of a structure, used in piece-generators + + + + + +#pragma once + +#include "ComposableGenerator.h" +#include "../Defines.h" +#include "../Cuboid.h" + + + + + +/** Represents a single piece. Can have multiple connectors of different types where other pieces can connect. */ +class cPiece +{ +public: + // Force a virtual destructor in all descendants + virtual ~cPiece() {} + + struct cConnector + { + enum eDirection + { + // The following values correspond to equivalent eBlockFace values: + dirXM = BLOCK_FACE_XM, // Pointing towards the X- side of the prefab + dirXP = BLOCK_FACE_XP, // Pointing towards the X+ side of the prefab + dirYM = BLOCK_FACE_YM, // Pointing towards the Y- side of the prefab, doesn't change with rotation around the Y axis + dirYP = BLOCK_FACE_YP, // Pointing towards the Y+ side of the prefab, doesn't change with rotation around the Y axis + dirZM = BLOCK_FACE_ZM, // Pointing towards the Z- side of the prefab + dirZP = BLOCK_FACE_ZP, // Pointing towards the Z+ side of the prefab + + // Special kind of the vertical connectors (changes with rotation around the Y axis) + dirYM_XM_ZM = BLOCK_FACE_MAX + 1, // Pointing towards the Y- side of the prefab, conceptually at the X- Z- corner of the block + dirYM_XM_ZP, // Pointing towards the Y- side of the prefab, conceptually at the X- Z+ corner of the block + dirYM_XP_ZM, // Pointing towards the Y- side of the prefab, conceptually at the X+ Z- corner of the block + dirYM_XP_ZP, // Pointing towards the Y- side of the prefab, conceptually at the X+ Z+ corner of the block + dirYP_XM_ZM, // Pointing towards the Y+ side of the prefab, conceptually at the X- Z- corner of the block + dirYP_XM_ZP, // Pointing towards the Y+ side of the prefab, conceptually at the X- Z+ corner of the block + dirYP_XP_ZM, // Pointing towards the Y+ side of the prefab, conceptually at the X+ Z- corner of the block + dirYP_XP_ZP, // Pointing towards the Y+ side of the prefab, conceptually at the X+ Z+ corner of the block + }; + + /** Position relative to the piece */ + Vector3i m_Pos; + + /** Type of the connector. Any arbitrary number; the generator connects only connectors of opposite + (negative) types. */ + int m_Type; + + /** Direction in which the connector is facing. + Will be matched by the opposite direction for the connecting connector. */ + eDirection m_Direction; + + cConnector(int a_X, int a_Y, int a_Z, int a_Type, eDirection a_Direction); + cConnector(const Vector3i & a_Pos, int a_Type, eDirection a_Direction); + + /** Returns the position of the block that has the specified direction from the specified position. + Similar to ::AddFaceDirection() */ + static Vector3i AddDirection(const Vector3i & a_Pos, eDirection a_Direction); + + /** Returns the string representation of the direction. + For debugging purposes. */ + static const char * DirectionToString(eDirection a_Direction); + + /** Returns true if the specified number corresponds to a valid eDirection. */ + static bool IsValidDirection(int a_Direction); + + /** Returns the direction corresponding to the given direction rotated 180 degrees around the Y axis. */ + static eDirection RotateDirection(eDirection a_Direction); + + /** Returns the direction corresponding to the given direction rotated 90 degrees CCW around the Y axis. */ + static eDirection RotateDirectionCCW(eDirection a_Direction); + + /** Returns the direction corresponding to the given direction rotated 90 degrees CW around the Y axis. */ + static eDirection RotateDirectionCW(eDirection a_Direction); + + /** Returns the number of CCW rotations that a_RotatingDir requires in order to be the counter-direction of a_FixedDir. + Ie. if you have a connector with a_FixedDir and you're rotating a piece that has a connector with a_RotatingDir, + how many CCW rotations it needs to make the connectors compatible. + Returns -1 if it is impossible to fit the two directions. */ + static int GetNumCCWRotationsToFit(eDirection a_FixedDir, eDirection a_RotatingDir); + + /** Converts the string representation of a direction into the eDirection enum value. + Returns true if successful, false on failure. + Accepts both numbers and string representations such as "x+" or "Y+X-Z+". */ + static bool StringToDirection(const AString & a_Value, eDirection & a_Out); + }; + + typedef std::vector cConnectors; + + + /** Base class (interface) for strategies for placing the starting pieces vertically. + Descendants can override the GetVerticalPlacement() method to provide custom placement decisions. */ + class cVerticalStrategy + { + public: + // Force a virtual destructor in descendants + virtual ~cVerticalStrategy() {} + + /** Returns the Y coord of the piece */ + virtual int GetVerticalPlacement(int a_BlockX, int a_BlockZ) = 0; + + /** Initializes the strategy's parameters from the string representation. + a_Params is the string containing only the parameters (substring after the first pipe character in the strategy description string). + If a_LogWarnings is true, logs any problems to the console. + Returns true if successful, false if the string parsing failed. + Used when loading the strategy from a file. */ + virtual bool InitializeFromString(const AString & a_Params, bool a_LogWarnings) = 0; + + /** Called when the piece pool is assigned to a generator, + so that the strategies may bind to the underlying subgenerators. */ + virtual void AssignGens(int a_Seed, cBiomeGenPtr & a_BiomeGen, cTerrainHeightGenPtr & a_TerrainHeightGen, int a_SeaLevel) {} + }; + + typedef SharedPtr cVerticalStrategyPtr; + + + /** Base class (interface) for the vertical limit of piece placement. + Each placed piece can have a limit, represented by this class, that gets queried for validity of the placement. */ + class cVerticalLimit + { + public: + virtual ~cVerticalLimit() {} + + /** Called to inquire whether the specified piece can be placed at the specified height. + a_BlockX, a_BlockZ is the column of the connector that is being queried. + a_Height is the requested height of the piece's lowest block. */ + virtual bool CanBeAtHeight(int a_BlockX, int a_BlockZ, int a_Height) = 0; + + /** Initializes the limit's parameters from the string representation. + a_Params is the string containing only the parameters (substring after the first pipe character in the limit description string). + Returns true if successful, false if the string parsing failed. + If a_LogWarnings is true, any error while parsing the string is output to the server console. + Used when loading the limit from a file. */ + virtual bool InitializeFromString(const AString & a_Params, bool a_LogWarnings) = 0; + + /** Called when the piece pool is assigned to a generator, + so that the limits may bind to the underlying subgenerators. */ + virtual void AssignGens(int a_Seed, cBiomeGenPtr & a_BiomeGen, cTerrainHeightGenPtr & a_TerrainHeightGen, int a_SeaLevel) {} + }; + + typedef SharedPtr cVerticalLimitPtr; + + + /** The strategy used for vertical placement of this piece when it is used as a starting piece. */ + cVerticalStrategyPtr m_VerticalStrategy; + + /** The checker that verifies each placement's vertical position. */ + cVerticalLimitPtr m_VerticalLimit; + + + /** Returns all of the available connectors that the piece has. + Each connector has a (relative) position in the piece, and a type associated with it. */ + virtual cConnectors GetConnectors(void) const = 0; + + /** Returns the dimensions of this piece. + The dimensions cover the entire piece, there is no block that the piece generates outside of this size. */ + virtual Vector3i GetSize(void) const = 0; + + /** Returns the "hitbox" of this piece. + A hitbox is what is compared and must not intersect other pieces' hitboxes when generating. */ + virtual cCuboid GetHitBox(void) const = 0; + + /** Returns true if the piece can be rotated CCW the specific number of 90-degree turns. */ + virtual bool CanRotateCCW(int a_NumRotations) const = 0; + + /** Returns the height, based on m_VerticalStrategy, for this piece when used as the starting piece. + If there's no strategy assigned to this piece, returns -1. */ + int GetStartingPieceHeight(int a_BlockX, int a_BlockZ) + { + if (m_VerticalStrategy != nullptr) + { + return m_VerticalStrategy->GetVerticalPlacement(a_BlockX, a_BlockZ); + } + return -1; + } + + void SetVerticalStrategy(cVerticalStrategyPtr a_VerticalStrategy) + { + m_VerticalStrategy = a_VerticalStrategy; + } + + cVerticalStrategyPtr GetVerticalStrategy(void) const + { + return m_VerticalStrategy; + } + + cVerticalLimitPtr GetVerticalLimit(void) const + { + return m_VerticalLimit; + } + + /** Sets the vertical strategy based on the description in the string. + If a_LogWarnings is true, logs the parsing problems into the server console. + Returns true if successful, false if strategy parsing failed (no strategy assigned). */ + bool SetVerticalStrategyFromString(const AString & a_StrategyDesc, bool a_LogWarnings); + + /** Sets the vertical limit based on the description string. + Returns true if successful, false if limit parsing failed (no limit assigned). + If a_LogWarnings is true, any problem is reported into the server console. */ + bool SetVerticalLimitFromString(const AString & a_LimitDesc, bool a_LogWarnings); + + /** Returns a copy of the a_Pos after rotating the piece the specified number of CCW rotations. */ + Vector3i RotatePos(const Vector3i & a_Pos, int a_NumCCWRotations) const; + + /** Returns a copy of the connector that is rotated and then moved by the specified amounts. */ + cConnector RotateMoveConnector(const cConnector & a_Connector, int a_NumCCWRotations, int a_MoveX, int a_MoveY, int a_MoveZ) const; + + /** Returns the hitbox after the specified number of rotations and moved so that a_MyConnector is placed at a_ToConnectorPos. */ + cCuboid RotateHitBoxToConnector(const cConnector & a_MyConnector, const Vector3i & a_ToConnectorPos, int a_NumCCWRotations) const; + + /** Returns the hitbox after the specified number of CCW rotations and moved by the specified amounts. */ + cCuboid RotateMoveHitBox(int a_NumCCWRotations, int a_MoveX, int a_MoveY, int a_MoveZ) const; +}; + +typedef std::vector cPieces; + + + + +// fwd: +class cPlacedPiece; + + + + + +/** This class is an interface that stores pieces for a generator. +Provides lists of pieces based on criteria (IsStarting, HasConnector). +Provides per-piece weights for random distribution of individual pieces. */ +class cPiecePool +{ +public: + // Force a virtual destructor in all descendants: + virtual ~cPiecePool() {} + + /** Returns a list of pieces that contain the specified connector type. + The cPiece pointers returned are managed by the pool and the caller doesn't free them. */ + virtual cPieces GetPiecesWithConnector(int a_ConnectorType) = 0; + + /** Returns the pieces that should be used as the starting point. + Multiple starting points are supported, one of the returned piece will be chosen. */ + virtual cPieces GetStartingPieces(void) = 0; + + /** Returns the relative weight with which the a_NewPiece is to be selected for placing under a_PlacedPiece through a_ExistingConnector. + a_ExistingConnector is the original connector, before any movement or rotation is applied to it. + This allows the pool to tweak the piece's chances, based on the previous pieces in the tree and the connector used. + The higher the number returned, the higher the chance the piece will be chosen. 0 means the piece will never be chosen. */ + virtual int GetPieceWeight( + const cPlacedPiece & a_PlacedPiece, + const cPiece::cConnector & a_ExistingConnector, + const cPiece & a_NewPiece + ) + { + return 1; + } + + /** Returns the relative weight with which the a_NewPiece is to be selected for placing as the first piece. + This allows the pool to tweak the piece's chances. + The higher the number returned, the higher the chance the piece will be chosen. 0 means the piece will not be chosen. + If all pieces return 0, a random piece is chosen, with all equal chances. */ + virtual int GetStartingPieceWeight(const cPiece & a_NewPiece) + { + return 1; + } + + /** Called after a piece is placed, to notify the pool that it has been used. + The pool may adjust the pieces it will return the next time. */ + virtual void PiecePlaced(const cPiece & a_Piece) = 0; + + /** Called when the pool has finished the current structure and should reset any piece-counters it has + for a new structure. */ + virtual void Reset(void) = 0; +}; + + + + + +/** Represents a single piece that has been placed to specific coords in the world. */ +class cPlacedPiece +{ +public: + cPlacedPiece(const cPlacedPiece * a_Parent, const cPiece & a_Piece, const Vector3i & a_Coords, int a_NumCCWRotations); + + const cPlacedPiece * GetParent (void) const { return m_Parent; } + const cPiece & GetPiece (void) const { return *m_Piece; } + const Vector3i & GetCoords (void) const { return m_Coords; } + int GetNumCCWRotations (void) const { return m_NumCCWRotations; } + const cCuboid & GetHitBox (void) const { return m_HitBox; } + int GetDepth (void) const { return m_Depth; } + bool HasBeenMovedToGround(void) const { return m_HasBeenMovedToGround; } + + /** Returns the coords as a modifiable object. */ + Vector3i & GetCoords(void) { return m_Coords; } + + /** Returns the connector at the specified index, rotated in the actual placement. + Undefined behavior if a_Index is out of range. */ + cPiece::cConnector GetRotatedConnector(size_t a_Index) const; + + /** Returns a copy of the specified connector, modified to account for the translation and rotation for + this placement. */ + cPiece::cConnector GetRotatedConnector(const cPiece::cConnector & a_Connector) const; + + /** Moves the placed piece Y-wise by the specified offset. + Sets m_HasBeenMovedToGround to true, too. + Used eg. by village houses. */ + void MoveToGroundBy(int a_OffsetY); + +protected: + const cPlacedPiece * m_Parent; + const cPiece * m_Piece; + Vector3i m_Coords; + int m_NumCCWRotations; + cCuboid m_HitBox; // Hitbox of the placed piece, in world coords + int m_Depth; // Depth in the generated piece tree + + /** Set to true once the piece has been moved Y-wise. + Used eg. by village houses. */ + bool m_HasBeenMovedToGround; +}; + +typedef std::unique_ptr cPlacedPiecePtr; +typedef std::vector cPlacedPieces; + + + + + diff --git a/src/Generating/PieceStructuresGen.cpp b/src/Generating/PieceStructuresGen.cpp index e35048cb0..c8630fa13 100644 --- a/src/Generating/PieceStructuresGen.cpp +++ b/src/Generating/PieceStructuresGen.cpp @@ -6,6 +6,7 @@ #include "Globals.h" #include "PieceStructuresGen.h" #include "PrefabStructure.h" +#include "PieceGeneratorBFSTree.h" #include "IniFile.h" #include "../Stopwatch.h" @@ -14,6 +15,114 @@ +class cPieceStructuresGen::cGen: + public cGridStructGen +{ + typedef cGridStructGen Super; + +public: + cGen(int a_Seed, cBiomeGenPtr a_BiomeGen, cTerrainHeightGenPtr a_HeightGen, int a_SeaLevel, const AString & a_Name): + Super(a_Seed), + m_BiomeGen(a_BiomeGen), + m_HeightGen(a_HeightGen), + m_SeaLevel(a_SeaLevel), + m_Name(a_Name), + m_MaxDepth(5) + { + } + + + + /** Loads the piecepool from a file. + Returns true on success, logs warning and returns false on failure. */ + bool LoadFromFile(const AString & a_FileName) + { + // Load the piecepool from the file, log any warnings: + if (!m_PiecePool.LoadFromFile(a_FileName, true)) + { + return false; + } + if (NoCaseCompare(m_PiecePool.GetIntendedUse(), "PieceStructures") != 0) + { + LOGWARNING("PieceStructures generator: File %s is intended for use in \"%s\", rather than piece structures. Loading the file, but the generator may behave unexpectedly.", + a_FileName.c_str(), m_PiecePool.GetIntendedUse().c_str() + ); + } + m_PiecePool.AssignGens(m_Seed, m_BiomeGen, m_HeightGen, m_SeaLevel); + + // Apply generator params from the piecepool (in the metadata) into the generator: + auto & generatorParams = m_PiecePool.GetAllMetadata(); + SetGeneratorParams(generatorParams); + m_MaxDepth = GetStringMapInteger(generatorParams, "MaxDepth", m_MaxDepth); + + return true; + } + + + + // cGridStructGen overrides: + virtual cStructurePtr CreateStructure(int a_GridX, int a_GridZ, int a_OriginX, int a_OriginZ) override + { + cStopwatch sw(Printf("CreateStructure for %s at <%d, %d>", m_Name.c_str(), a_GridX, a_GridZ)); + cPlacedPieces outPieces; + cPieceGeneratorBFSTree pg(m_PiecePool, m_Seed); + pg.PlacePieces(a_OriginX, a_OriginZ, m_MaxDepth, outPieces); + return std::make_shared(a_GridX, a_GridZ, a_OriginX, a_OriginZ, std::move(outPieces), m_HeightGen); + } + + +protected: + + /** The type used for storing a connection from one piece to another, while building the piece tree. */ + struct cConnection + { + cPiece * m_Piece; // The piece being connected + cPiece::cConnector m_Connector; // The piece's connector being used (relative non-rotated coords) + int m_NumCCWRotations; // Number of rotations necessary to match the two connectors + int m_Weight; // Relative chance that this connection will be chosen + + cConnection(cPiece & a_Piece, cPiece::cConnector & a_Connector, int a_NumCCWRotations, int a_Weight); + }; + typedef std::vector cConnections; + + + /** The type used for storing a pool of connectors that will be attempted to expand by another piece. */ + struct cFreeConnector + { + cPlacedPiece * m_Piece; + cPiece::cConnector m_Connector; + + cFreeConnector(cPlacedPiece * a_Piece, const cPiece::cConnector & a_Connector); + }; + typedef std::vector cFreeConnectors; + + /** The underlying biome generator that defines whether the structure is created or not */ + cBiomeGenPtr m_BiomeGen; + + /** The underlying height generator, used to position the prefabs crossing chunk borders if they are set to FitGround. */ + cTerrainHeightGenPtr m_HeightGen; + + /** The world's sea level, if available. Used for some cVerticalStrategy descendants. */ + int m_SeaLevel; + + /** The name that is used for reporting. */ + AString m_Name; + + /** All available prefabs. */ + cPrefabPiecePool m_PiecePool; + + /** Maximum depth of the generated piece tree. */ + int m_MaxDepth; +}; + + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cPieceStructuresGen: + cPieceStructuresGen::cPieceStructuresGen(int a_Seed): m_Seed(a_Seed) { @@ -71,59 +180,3 @@ void cPieceStructuresGen::GenFinish(cChunkDesc & a_Chunk) -//////////////////////////////////////////////////////////////////////////////// -// cPieceStructuresGen::cGen: - -cPieceStructuresGen::cGen::cGen(int a_Seed, cBiomeGenPtr a_BiomeGen, cTerrainHeightGenPtr a_HeightGen, int a_SeaLevel, const AString & a_Name): - Super(a_Seed), - m_BiomeGen(a_BiomeGen), - m_HeightGen(a_HeightGen), - m_SeaLevel(a_SeaLevel), - m_Name(a_Name), - m_MaxDepth(5) -{ -} - - - - - -bool cPieceStructuresGen::cGen::LoadFromFile(const AString & a_FileName) -{ - // Load the piecepool from the file, log any warnings: - if (!m_Pool.LoadFromFile(a_FileName, true)) - { - return false; - } - if (NoCaseCompare(m_Pool.GetIntendedUse(), "PieceStructures") != 0) - { - LOGWARNING("PieceStructures generator: File %s is intended for use in \"%s\", rather than piece structures. Loading the file, but the generator may behave unexpectedly.", - a_FileName.c_str(), m_Pool.GetIntendedUse().c_str() - ); - } - m_Pool.AssignGens(m_Seed, m_BiomeGen, m_HeightGen, m_SeaLevel); - - // Apply generator params from the piecepool (in the metadata) into the generator: - auto & generatorParams = m_Pool.GetAllMetadata(); - SetGeneratorParams(generatorParams); - m_MaxDepth = GetStringMapInteger(generatorParams, "MaxDepth", m_MaxDepth); - - return true; -} - - - - - -cGridStructGen::cStructurePtr cPieceStructuresGen::cGen::CreateStructure(int a_GridX, int a_GridZ, int a_OriginX, int a_OriginZ) -{ - cStopwatch sw(Printf("CreateStructure for %s at <%d, %d>", m_Name.c_str(), a_GridX, a_GridZ)); - cBFSPieceGenerator pg(m_Pool, m_Seed); - cPlacedPieces outPieces; - pg.PlacePieces(a_OriginX, a_OriginZ, m_MaxDepth, outPieces); - return std::make_shared(a_GridX, a_GridZ, a_OriginX, a_OriginZ, outPieces, m_HeightGen); -} - - - - diff --git a/src/Generating/PieceStructuresGen.h b/src/Generating/PieceStructuresGen.h index c8e2f2b0c..0e36fe9d5 100644 --- a/src/Generating/PieceStructuresGen.h +++ b/src/Generating/PieceStructuresGen.h @@ -40,40 +40,9 @@ public: virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; protected: - class cGen: - public cGridStructGen - { - typedef cGridStructGen Super; - public: - cGen(int a_Seed, cBiomeGenPtr a_BiomeGen, cTerrainHeightGenPtr a_HeightGen, int a_SeaLevel, const AString & a_Name); - - /** Loads the piecepool from a file. - Returns true on success, logs warning and returns false on failure. */ - bool LoadFromFile(const AString & a_FileName); - - // cGridStructGen overrides: - virtual cStructurePtr CreateStructure(int a_GridX, int a_GridZ, int a_OriginX, int a_OriginZ) override; - - protected: - - /** The underlying biome generator that defines whether the structure is created or not */ - cBiomeGenPtr m_BiomeGen; - - /** The underlying height generator, used to position the prefabs crossing chunk borders if they are set to FitGround. */ - cTerrainHeightGenPtr m_HeightGen; - - /** The world's sea level, if available. Used for some cVerticalStrategy descendants. */ - int m_SeaLevel; - - /** The name that is used for reporting. */ - AString m_Name; - - /** All available prefabs. */ - cPrefabPiecePool m_Pool; - - /** Maximum depth of the generated piece tree. */ - int m_MaxDepth; - }; + /** The generator doing the work for a single prefab set. + Forward-declared so that its implementation changes don't affect the header. */ + class cGen; typedef SharedPtr cGenPtr; typedef std::vector cGenPtrs; diff --git a/src/Generating/Prefab.h b/src/Generating/Prefab.h index 59b80a8a8..6cec8caa0 100644 --- a/src/Generating/Prefab.h +++ b/src/Generating/Prefab.h @@ -14,7 +14,7 @@ declared in this file as well; the Gallery server exports areas in this format. #pragma once -#include "PieceGenerator.h" +#include "PiecePool.h" #include "../BlockArea.h" diff --git a/src/Generating/PrefabPiecePool.h b/src/Generating/PrefabPiecePool.h index af342f023..ff0446b56 100644 --- a/src/Generating/PrefabPiecePool.h +++ b/src/Generating/PrefabPiecePool.h @@ -10,7 +10,7 @@ #pragma once #include -#include "PieceGenerator.h" +#include "PiecePool.h" #include "Prefab.h" diff --git a/src/Generating/PrefabStructure.cpp b/src/Generating/PrefabStructure.cpp index 3ebbe8143..c37398be9 100644 --- a/src/Generating/PrefabStructure.cpp +++ b/src/Generating/PrefabStructure.cpp @@ -14,11 +14,11 @@ cPrefabStructure::cPrefabStructure( int a_GridX, int a_GridZ, int a_OriginX, int a_OriginZ, - cPlacedPieces & a_Pieces, + cPlacedPieces && a_Pieces, cTerrainHeightGenPtr a_HeightGen ): Super(a_GridX, a_GridZ, a_OriginX, a_OriginZ), - m_Pieces(a_Pieces), + m_Pieces(std::move(a_Pieces)), m_HeightGen(a_HeightGen) { } @@ -27,15 +27,6 @@ cPrefabStructure::cPrefabStructure( -cPrefabStructure::~cPrefabStructure() -{ - cPieceGenerator::FreePieces(m_Pieces); -} - - - - - void cPrefabStructure::DrawIntoChunk(cChunkDesc & a_Chunk) { // Iterate over all items @@ -47,7 +38,7 @@ void cPrefabStructure::DrawIntoChunk(cChunkDesc & a_Chunk) { PlacePieceOnGround(**itr); } - Prefab.Draw(a_Chunk, *itr); + Prefab.Draw(a_Chunk, itr->get()); } // for itr - m_PlacedPieces[] } diff --git a/src/Generating/PrefabStructure.h b/src/Generating/PrefabStructure.h index 29a506494..423408396 100644 --- a/src/Generating/PrefabStructure.h +++ b/src/Generating/PrefabStructure.h @@ -10,7 +10,7 @@ #pragma once #include "GridStructGen.h" -#include "PieceGenerator.h" +#include "PiecePool.h" @@ -25,12 +25,10 @@ public: cPrefabStructure( int a_GridX, int a_GridZ, int a_OriginX, int a_OriginZ, - cPlacedPieces & a_Pieces, + cPlacedPieces && a_Pieces, cTerrainHeightGenPtr a_HeightGen ); - virtual ~cPrefabStructure(); - protected: /** The pieces placed by the generator. */ cPlacedPieces m_Pieces; diff --git a/src/Generating/VerticalLimit.h b/src/Generating/VerticalLimit.h index 0e36424ac..aeb41c1e8 100644 --- a/src/Generating/VerticalLimit.h +++ b/src/Generating/VerticalLimit.h @@ -9,7 +9,7 @@ #pragma once -#include "PieceGenerator.h" +#include "PiecePool.h" diff --git a/src/Generating/VerticalStrategy.cpp b/src/Generating/VerticalStrategy.cpp index 08af5b272..4021dc4bb 100644 --- a/src/Generating/VerticalStrategy.cpp +++ b/src/Generating/VerticalStrategy.cpp @@ -5,6 +5,7 @@ #include "Globals.h" #include "VerticalStrategy.h" +#include "../Noise/Noise.h" diff --git a/src/Generating/VerticalStrategy.h b/src/Generating/VerticalStrategy.h index 77e86f660..d42f85d4b 100644 --- a/src/Generating/VerticalStrategy.h +++ b/src/Generating/VerticalStrategy.h @@ -9,7 +9,7 @@ #pragma once -#include "PieceGenerator.h" +#include "PiecePool.h" diff --git a/src/Generating/VillageGen.cpp b/src/Generating/VillageGen.cpp index 6d5216a16..11617c9ec 100644 --- a/src/Generating/VillageGen.cpp +++ b/src/Generating/VillageGen.cpp @@ -5,7 +5,7 @@ #include "Globals.h" #include "VillageGen.h" -#include "PieceGenerator.h" +#include "PieceGeneratorBFSTree.h" @@ -133,7 +133,7 @@ public: m_HeightGen(a_HeightGen) { // Generate the pieces for this village; don't care about the Y coord: - cBFSPieceGenerator pg(*this, a_Seed); + cPieceGeneratorBFSTree pg(*this, a_Seed); pg.PlacePieces(a_OriginX, a_OriginZ, a_MaxRoadDepth + 1, m_Pieces); if (m_Pieces.empty()) { @@ -141,10 +141,6 @@ public: } } - ~cVillage() - { - cPieceGenerator::FreePieces(m_Pieces); - } protected: /** Seed for the random functions */ @@ -193,7 +189,7 @@ protected: { PlacePieceOnGround(**itr); } - Prefab.Draw(a_Chunk, *itr); + Prefab.Draw(a_Chunk, itr->get()); } // for itr - m_PlacedPieces[] } @@ -304,11 +300,11 @@ protected: void MoveAllDescendants(cPlacedPieces & a_PlacedPieces, size_t a_Pivot, int a_HeightDifference) { size_t num = a_PlacedPieces.size(); - cPlacedPiece * Pivot = a_PlacedPieces[a_Pivot]; + auto & Pivot = a_PlacedPieces[a_Pivot]; for (size_t i = a_Pivot + 1; i < num; i++) { if ( - (a_PlacedPieces[i]->GetParent() == Pivot) && // It is a direct dependant of the pivot + (a_PlacedPieces[i]->GetParent() == Pivot.get()) && // It is a direct dependant of the pivot !(static_cast(a_PlacedPieces[i]->GetPiece())).ShouldMoveToGround() // It attaches strictly by connectors ) { diff --git a/tests/LoadablePieces/CMakeLists.txt b/tests/LoadablePieces/CMakeLists.txt index b71a64474..4c8f698bf 100644 --- a/tests/LoadablePieces/CMakeLists.txt +++ b/tests/LoadablePieces/CMakeLists.txt @@ -17,7 +17,7 @@ set (SHARED_SRCS ${CMAKE_SOURCE_DIR}/src/Bindings/LuaState.cpp ${CMAKE_SOURCE_DIR}/src/Generating/ChunkDesc.cpp - ${CMAKE_SOURCE_DIR}/src/Generating/PieceGenerator.cpp + ${CMAKE_SOURCE_DIR}/src/Generating/PiecePool.cpp ${CMAKE_SOURCE_DIR}/src/Generating/Prefab.cpp ${CMAKE_SOURCE_DIR}/src/Generating/PrefabPiecePool.cpp ${CMAKE_SOURCE_DIR}/src/Generating/VerticalLimit.cpp @@ -47,7 +47,7 @@ set (SHARED_HDRS ${CMAKE_SOURCE_DIR}/src/Bindings/LuaState.h ${CMAKE_SOURCE_DIR}/src/Generating/ChunkDesc.h - ${CMAKE_SOURCE_DIR}/src/Generating/PieceGenerator.h + ${CMAKE_SOURCE_DIR}/src/Generating/PiecePool.h ${CMAKE_SOURCE_DIR}/src/Generating/Prefab.h ${CMAKE_SOURCE_DIR}/src/Generating/PrefabPiecePool.h ${CMAKE_SOURCE_DIR}/src/Generating/VerticalLimit.h diff --git a/tests/LuaThreadStress/CMakeLists.txt b/tests/LuaThreadStress/CMakeLists.txt index 402b77d23..dcadb34aa 100644 --- a/tests/LuaThreadStress/CMakeLists.txt +++ b/tests/LuaThreadStress/CMakeLists.txt @@ -17,7 +17,7 @@ set (SHARED_SRCS ${CMAKE_SOURCE_DIR}/src/Bindings/LuaState.cpp ${CMAKE_SOURCE_DIR}/src/Generating/ChunkDesc.cpp - ${CMAKE_SOURCE_DIR}/src/Generating/PieceGenerator.cpp + ${CMAKE_SOURCE_DIR}/src/Generating/PiecePool.cpp ${CMAKE_SOURCE_DIR}/src/Generating/Prefab.cpp ${CMAKE_SOURCE_DIR}/src/Generating/PrefabPiecePool.cpp ${CMAKE_SOURCE_DIR}/src/Generating/VerticalLimit.cpp @@ -47,7 +47,7 @@ set (SHARED_HDRS ${CMAKE_SOURCE_DIR}/src/Bindings/LuaState.h ${CMAKE_SOURCE_DIR}/src/Generating/ChunkDesc.h - ${CMAKE_SOURCE_DIR}/src/Generating/PieceGenerator.h + ${CMAKE_SOURCE_DIR}/src/Generating/PiecePool.h ${CMAKE_SOURCE_DIR}/src/Generating/Prefab.h ${CMAKE_SOURCE_DIR}/src/Generating/PrefabPiecePool.h ${CMAKE_SOURCE_DIR}/src/Generating/VerticalLimit.h diff --git a/tests/PieceRotation/CMakeLists.txt b/tests/PieceRotation/CMakeLists.txt index 53b4f273f..bb1b37a53 100644 --- a/tests/PieceRotation/CMakeLists.txt +++ b/tests/PieceRotation/CMakeLists.txt @@ -17,7 +17,7 @@ set (SHARED_SRCS ${CMAKE_SOURCE_DIR}/src/Bindings/LuaState.cpp ${CMAKE_SOURCE_DIR}/src/Generating/ChunkDesc.cpp - ${CMAKE_SOURCE_DIR}/src/Generating/PieceGenerator.cpp + ${CMAKE_SOURCE_DIR}/src/Generating/PiecePool.cpp ${CMAKE_SOURCE_DIR}/src/Generating/Prefab.cpp ${CMAKE_SOURCE_DIR}/src/Generating/PrefabPiecePool.cpp ${CMAKE_SOURCE_DIR}/src/Generating/VerticalLimit.cpp @@ -47,7 +47,7 @@ set (SHARED_HDRS ${CMAKE_SOURCE_DIR}/src/Bindings/LuaState.h ${CMAKE_SOURCE_DIR}/src/Generating/ChunkDesc.h - ${CMAKE_SOURCE_DIR}/src/Generating/PieceGenerator.h + ${CMAKE_SOURCE_DIR}/src/Generating/PiecePool.h ${CMAKE_SOURCE_DIR}/src/Generating/Prefab.h ${CMAKE_SOURCE_DIR}/src/Generating/PrefabPiecePool.h ${CMAKE_SOURCE_DIR}/src/Generating/VerticalLimit.h diff --git a/tests/PieceRotation/PieceRotationTest.cpp b/tests/PieceRotation/PieceRotationTest.cpp index 3e325b54c..ace3bd489 100644 --- a/tests/PieceRotation/PieceRotationTest.cpp +++ b/tests/PieceRotation/PieceRotationTest.cpp @@ -5,7 +5,7 @@ #include "Globals.h" #include #include -#include "Generating/PieceGenerator.h" +#include "Generating/PiecePool.h" diff --git a/tests/SchematicFileSerializer/CMakeLists.txt b/tests/SchematicFileSerializer/CMakeLists.txt index 792349a9d..99589c999 100644 --- a/tests/SchematicFileSerializer/CMakeLists.txt +++ b/tests/SchematicFileSerializer/CMakeLists.txt @@ -38,7 +38,7 @@ set (SHARED_HDRS ${CMAKE_SOURCE_DIR}/src/Bindings/LuaState.h ${CMAKE_SOURCE_DIR}/src/Generating/ChunkDesc.h - ${CMAKE_SOURCE_DIR}/src/Generating/PieceGenerator.h + ${CMAKE_SOURCE_DIR}/src/Generating/PiecePool.h ${CMAKE_SOURCE_DIR}/src/Generating/Prefab.h ${CMAKE_SOURCE_DIR}/src/Generating/PrefabPiecePool.h ${CMAKE_SOURCE_DIR}/src/Generating/VerticalLimit.h -- cgit v1.2.3