summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/Generating/PieceGenerator.cpp332
-rw-r--r--src/Generating/PieceGenerator.h213
2 files changed, 545 insertions, 0 deletions
diff --git a/src/Generating/PieceGenerator.cpp b/src/Generating/PieceGenerator.cpp
new file mode 100644
index 000000000..04513666d
--- /dev/null
+++ b/src/Generating/PieceGenerator.cpp
@@ -0,0 +1,332 @@
+
+// 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"
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cPiece:
+
+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:
+ Vector3i Size = GetSize();
+ switch (a_NumCCWRotations)
+ {
+ case 0:
+ {
+ // No rotation needed
+ break;
+ }
+ case 1:
+ {
+ // 1 CCW rotation:
+ int NewX = Size.z - res.m_X;
+ int NewZ = res.m_Z;
+ res.m_X = NewX;
+ res.m_Z = NewZ;
+ res.m_Direction = RotateBlockFaceCCW(res.m_Direction);
+ break;
+ }
+ case 2:
+ {
+ // 2 rotations ( = axis flip):
+ res.m_X = Size.x - res.m_X;
+ res.m_Z = Size.z - res.m_Z;
+ res.m_Direction = MirrorBlockFaceY(res.m_Direction);
+ break;
+ }
+ case 3:
+ {
+ // 1 CW rotation:
+ int NewX = res.m_Z;
+ int NewZ = Size.x - res.m_X;
+ res.m_X = NewX;
+ res.m_Z = NewZ;
+ res.m_Direction = RotateBlockFaceCW(res.m_Direction);
+ break;
+ }
+ }
+
+ // Move the res connector:
+ res.m_X += a_MoveX;
+ res.m_Y += a_MoveY;
+ res.m_Z += a_MoveZ;
+
+ return res;
+}
+
+
+
+
+
+cCuboid cPiece::RotateHitBoxToConnector(
+ const cPiece::cConnector & a_MyConnector,
+ const cPiece::cConnector & a_ToConnector,
+ int a_NumCCWRotations
+) const
+{
+ cCuboid res = GetHitBox();
+ switch (a_NumCCWRotations)
+ {
+ case 0:
+ {
+ // No rotation, return the hitbox as-is
+ break;
+ }
+ case 1:
+ {
+ // 1 CCW rotation:
+ // TODO: res.p1.x =
+ break;
+ }
+ }
+ return res;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 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_Depth = (m_Parent == NULL) ? 0 : (m_Parent->GetDepth() + 1);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cPieceGenerator:
+
+cPieceGenerator::cPieceGenerator(cPiecePool & a_PiecePool, int a_Seed) :
+ m_PiecePool(a_PiecePool),
+ m_Noise(a_Seed),
+ m_Seed(a_Seed)
+{
+}
+
+
+
+
+
+cPlacedPiece cPieceGenerator::PlaceStartingPiece(int a_BlockX, int a_BlockY, int a_BlockZ, cFreeConnectors & a_OutConnectors)
+{
+ m_PiecePool.Reset();
+ int rnd = m_Noise.IntNoise3DInt(a_BlockX, a_BlockY, a_BlockZ) / 7;
+
+ // Choose a random one of the starting pieces:
+ cPieces StartingPieces = m_PiecePool.GetStartingPieces();
+ cPiece * StartingPiece = StartingPieces[rnd % StartingPieces.size()];
+ rnd = rnd >> 16;
+
+ // Choose a random supported rotation:
+ int Rotations[4] = {0};
+ int NumRotations = 1;
+ for (int i = 1; i < ARRAYCOUNT(Rotations); i++)
+ {
+ if (StartingPiece->CanRotateCCW(i))
+ {
+ Rotations[NumRotations] = i;
+ NumRotations += 1;
+ }
+ }
+ int Rotation = Rotations[rnd % NumRotations];
+
+ cPlacedPiece res(NULL, *StartingPiece, Vector3i(a_BlockX, a_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, a_BlockY, a_BlockZ))
+ );
+ }
+
+ return res;
+}
+
+
+
+
+
+bool cPieceGenerator::TryPlacePieceAtConnector(const cPlacedPiece & a_ParentPiece, const cPiece::cConnector & a_Connector, cPlacedPieces & a_OutPieces)
+{
+ // Translation of direction - direction -> number of CCW rotations needed:
+ // You need DirectionRotationTable[rot1][rot2] CCW turns to get from rot1 to rot2
+ static const int DirectionRotationTable[6][6] =
+ {
+ /* YM, YP, ZM, ZP, XM, XP
+ /* YM */ { 0, 0, 0, 0, 0, 0},
+ /* YP */ { 0, 0, 0, 0, 0, 0},
+ /* ZM */ { 0, 0, 0, 2, 1, 3},
+ /* ZP */ { 0, 0, 2, 0, 3, 1},
+ /* XM */ { 0, 0, 3, 1, 0, 2},
+ /* XP */ { 0, 0, 1, 3, 2, 0},
+ };
+
+ // Get a list of available connections:
+ const int * RotTable = DirectionRotationTable[a_Connector.m_Direction];
+ cConnections Connections;
+ cPieces AvailablePieces = m_PiecePool.GetPiecesWithConnector(a_Connector.m_Type);
+ Connections.reserve(AvailablePieces.size());
+ for (cPieces::iterator itrP = AvailablePieces.begin(), endP = AvailablePieces.end(); itrP != endP; ++itrP)
+ {
+ cPiece::cConnectors Connectors = (*itrP)->GetConnectors();
+ for (cPiece::cConnectors::iterator itrC = Connectors.begin(), endC = Connectors.end(); itrC != endC; ++itrC)
+ {
+ if (itrC->m_Type != a_Connector.m_Type)
+ {
+ continue;
+ }
+ // This is a same-type connector, find out how to rotate to it:
+ int NumCCWRotations = RotTable[itrC->m_Direction];
+ if (!(*itrP)->CanRotateCCW(NumCCWRotations))
+ {
+ // Doesn't support this rotation
+ continue;
+ }
+ if (!CheckConnection(a_Connector, **itrP, *itrC, NumCCWRotations, a_OutPieces))
+ {
+ // Doesn't fit in this rotation
+ continue;
+ }
+ Connections.push_back(cConnection(**itrP, *itrC, NumCCWRotations));
+ } // for itrC - Connectors[]
+ } // for itrP - AvailablePieces[]
+ if (Connections.empty())
+ {
+ // No available connections, bail out
+ return false;
+ }
+
+ // Choose a random connection from the list:
+ int rnd = m_Noise.IntNoise3DInt(a_Connector.m_X, a_Connector.m_Y, a_Connector.m_Z) / 7;
+ cConnection & Conn = Connections[rnd % Connections.size()];
+
+ // Place the piece:
+ cPiece::cConnector NewConnector = Conn.m_Piece->RotateMoveConnector(*(Conn.m_Connector), Conn.m_NumCCWRotations, 0, 0, 0);
+ Vector3i Coords = a_ParentPiece.GetCoords();
+ Coords.x -= NewConnector.m_X;
+ Coords.y -= NewConnector.m_Y;
+ Coords.z -= NewConnector.m_Z;
+ a_OutPieces.push_back(cPlacedPiece(&a_ParentPiece, *(Conn.m_Piece), Coords, Conn.m_NumCCWRotations));
+ return false;
+}
+
+
+
+
+
+bool cPieceGenerator::CheckConnection(
+ const cPiece::cConnector & a_ExistingConnector,
+ 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_ExistingConnector, a_NumCCWRotations);
+ for (cPlacedPieces::const_iterator itr = a_OutPieces.begin(), end = a_OutPieces.end(); itr != end; ++itr)
+ {
+ if (itr->GetHitBox().DoesIntersect(RotatedHitBox))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cPieceGenerator::cConnection:
+
+cPieceGenerator::cConnection::cConnection(cPiece & a_Piece, cPiece::cConnector & a_Connector, int a_NumCCWRotations) :
+ m_Piece(&a_Piece),
+ m_Connector(&a_Connector),
+ m_NumCCWRotations(a_NumCCWRotations)
+{
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 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_BlockY, int a_BlockZ, cPlacedPieces & a_OutPieces)
+{
+ a_OutPieces.clear();
+ cFreeConnectors ConnectorPool;
+
+ // Place the starting piece:
+ a_OutPieces.push_back(PlaceStartingPiece(a_BlockX, a_BlockY, a_BlockZ, ConnectorPool));
+
+ // 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];
+ TryPlacePieceAtConnector(*Conn.m_Piece, Conn.m_Connector, a_OutPieces);
+ NumProcessed++;
+ if (NumProcessed > 1000)
+ {
+ ConnectorPool.erase(ConnectorPool.begin(), ConnectorPool.begin() + NumProcessed);
+ NumProcessed = 0;
+ }
+ }
+}
+
+
+
+
diff --git a/src/Generating/PieceGenerator.h b/src/Generating/PieceGenerator.h
new file mode 100644
index 000000000..3bc71a0ab
--- /dev/null
+++ b/src/Generating/PieceGenerator.h
@@ -0,0 +1,213 @@
+
+// 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 "../Defines.h"
+#include "../Cuboid.h"
+#include "../Noise.h"
+
+
+
+
+
+/** Represents a single piece. Can have multiple connectors of different types where other pieces can connect. */
+class cPiece
+{
+public:
+ struct cConnector
+ {
+ int m_X;
+ int m_Y;
+ int m_Z;
+ int m_Type;
+ eBlockFace m_Direction;
+
+ cConnector(int a_X, int a_Y, int a_Z, int a_Type, eBlockFace m_Direction);
+ };
+
+ typedef std::vector<cConnector> cConnectors;
+
+ /** 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 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_ToConnector*/
+ cCuboid RotateHitBoxToConnector(const cConnector & a_MyConnector, const cConnector & a_ToConnector, int a_NumCCWRotations) const;
+};
+
+typedef std::vector<cPiece *> cPieces;
+
+
+
+
+
+class cPiecePool
+{
+public:
+ /** 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;
+
+ /** 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 cPiece & GetPiece (void) const { return *m_Piece; }
+ const Vector3i & GetCoords (void) const { return m_Coords; }
+ const int GetNumCCWRotations(void) const { return m_NumCCWRotations; }
+ const cCuboid & GetHitBox (void) const { return m_HitBox; }
+ const int GetDepth (void) const { return m_Depth; }
+
+protected:
+ const cPlacedPiece * m_Parent;
+ const cPiece * m_Piece;
+ Vector3i m_Coords;
+ int m_NumCCWRotations;
+ cCuboid m_HitBox;
+ int m_Depth;
+};
+
+typedef std::vector<cPlacedPiece> cPlacedPieces;
+
+
+
+
+
+class cPieceGenerator
+{
+public:
+ cPieceGenerator(cPiecePool & a_PiecePool, int a_Seed);
+
+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
+ int m_NumCCWRotations; // Number of rotations necessary to match the two connectors
+
+ cConnection(cPiece & a_Piece, cPiece::cConnector & a_Connector, int a_NumCCWRotations);
+ };
+ typedef std::vector<cConnection> 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<cFreeConnector> cFreeConnectors;
+
+
+ cPiecePool & m_PiecePool;
+ cNoise m_Noise;
+ int m_Seed;
+
+
+ /** Selects a starting piece and places it, including the rotations.
+ Also puts the piece's connectors in a_OutConnectors. */
+ cPlacedPiece PlaceStartingPiece(int a_BlockX, int a_BlockY, 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,
+ const cPiece::cConnector & a_Connector,
+ cPlacedPieces & a_OutPieces
+ );
+
+ /** Checks if the specified piece would fit with the already-placed pieces, using the specified connector
+ and number of CCW rotations.
+ Returns true if the piece fits, false if not. */
+ bool CheckConnection(
+ const cPiece::cConnector & a_ExistingConnector, // The existing connector
+ 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
+ );
+} ;
+
+
+
+
+
+class cBFSPieceGenerator :
+ public cPieceGenerator
+{
+ typedef cPieceGenerator super;
+
+public:
+ cBFSPieceGenerator(cPiecePool & a_PiecePool, int a_Seed);
+
+ /** Generates a placement for pieces at the specified coords. */
+ void PlacePieces(int a_BlockX, int a_BlockY, int a_BlockZ, cPlacedPieces & a_OutPieces);
+};
+
+
+
+
+
+class cDFSPieceGenerator :
+ public cPieceGenerator
+{
+public:
+ cDFSPieceGenerator(cPiecePool & a_PiecePool, int a_Seed);
+
+ /** Generates a placement for pieces at the specified coords. */
+ void PlacePieces(int a_BlockX, int a_BlockY, int a_BlockZ, cPlacedPieces & a_OutPieces);
+};
+
+
+
+