From 187abe3f5e2219e0a80c0cdca4db362e223b60ae Mon Sep 17 00:00:00 2001 From: Mattes D Date: Sun, 26 Feb 2017 22:49:23 +0100 Subject: [PATCH] Gen: Moved PiecePool into a separate file. Also rewritten the PieceGenerator to use std::unique_ptr. --- src/Generating/CMakeLists.txt | 6 +- src/Generating/PieceGeneratorBFSTree.cpp | 345 ++++++++++++++ src/Generating/PieceGeneratorBFSTree.h | 102 +++++ .../{PieceGenerator.cpp => PiecePool.cpp} | 419 ++---------------- .../{PieceGenerator.h => PiecePool.h} | 137 +----- 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 +- 20 files changed, 635 insertions(+), 636 deletions(-) create mode 100644 src/Generating/PieceGeneratorBFSTree.cpp create mode 100644 src/Generating/PieceGeneratorBFSTree.h rename src/Generating/{PieceGenerator.cpp => PiecePool.cpp} (52%) rename src/Generating/{PieceGenerator.h => PiecePool.h} (72%) 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/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/PieceGenerator.cpp b/src/Generating/PiecePool.cpp similarity index 52% rename from src/Generating/PieceGenerator.cpp rename to src/Generating/PiecePool.cpp index f8ae1d961..201c70afd 100644 --- a/src/Generating/PieceGenerator.cpp +++ b/src/Generating/PiecePool.cpp @@ -1,11 +1,11 @@ +// PiecePool.cpp -// PieceGenerator.cpp - -// Implements the cBFSPieceGenerator class and cDFSPieceGenerator class -// representing base classes for generating structures composed of individual "pieces" +// 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 "PieceGenerator.h" +#include "PiecePool.h" #include "VerticalStrategy.h" #include "VerticalLimit.h" @@ -358,6 +358,37 @@ cPiece::cConnector::eDirection cPiece::cConnector::RotateDirectionCW(eDirection +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: @@ -470,381 +501,3 @@ void cPlacedPiece::MoveToGroundBy(int a_OffsetY) - -//////////////////////////////////////////////////////////////////////////////// -// 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/PiecePool.h similarity index 72% rename from src/Generating/PieceGenerator.h rename to src/Generating/PiecePool.h index 1900d9d02..84f511950 100644 --- a/src/Generating/PieceGenerator.h +++ b/src/Generating/PiecePool.h @@ -1,16 +1,6 @@ +// PiecePool.h -// 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). -*/ +// Declares the cPiecePool class representing a pool of cPieces - "parts" of a structure, used in piece-generators @@ -21,7 +11,6 @@ Each uses a slightly different approach to generating: #include "ComposableGenerator.h" #include "../Defines.h" #include "../Cuboid.h" -#include "../Noise/Noise.h" @@ -91,6 +80,12 @@ public: /** 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+". */ @@ -229,7 +224,6 @@ typedef std::vector cPieces; - // fwd: class cPlacedPiece; @@ -237,8 +231,9 @@ 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. */ +/** 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: @@ -331,116 +326,10 @@ protected: bool m_HasBeenMovedToGround; }; -typedef std::vector cPlacedPieces; +typedef std::unique_ptr cPlacedPiecePtr; +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/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