commit
80d7c88e00
|
@ -62,16 +62,12 @@ typedef unsigned char HEIGHTTYPE;
|
|||
class cChunkDef
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
// Chunk dimensions:
|
||||
Width = 16U,
|
||||
Height = 256U,
|
||||
NumBlocks = Width * Height * Width,
|
||||
|
||||
/// If the data is collected into a single buffer, how large it needs to be:
|
||||
BlockDataSize = cChunkDef::NumBlocks * 2 + (cChunkDef::NumBlocks / 2), // 2.5 * numblocks
|
||||
} ;
|
||||
// Chunk dimensions:
|
||||
static const int Width = 16;
|
||||
static const int Height = 256;
|
||||
static const int NumBlocks = Width * Height * Width;
|
||||
/// If the data is collected into a single buffer, how large it needs to be:
|
||||
static const int BlockDataSize = cChunkDef::NumBlocks * 2 + (cChunkDef::NumBlocks / 2); // 2.5 * numblocks
|
||||
|
||||
/// The type used for any heightmap operations and storage; idx = x + Width * z; Height points to the highest non-air block in the column
|
||||
typedef HEIGHTTYPE HeightMap[Width * Width];
|
||||
|
@ -132,13 +128,13 @@ public:
|
|||
}
|
||||
|
||||
|
||||
inline static unsigned int MakeIndexNoCheck(int x, int y, int z)
|
||||
inline static int MakeIndexNoCheck(int x, int y, int z)
|
||||
{
|
||||
#if AXIS_ORDER == AXIS_ORDER_XZY
|
||||
// For some reason, NOT using the Horner schema is faster. Weird.
|
||||
return static_cast<unsigned int>(x + (z * cChunkDef::Width) + (y * cChunkDef::Width * cChunkDef::Width)); // 1.2 is XZY
|
||||
return x + (z * cChunkDef::Width) + (y * cChunkDef::Width * cChunkDef::Width); // 1.2 is XZY
|
||||
#elif AXIS_ORDER == AXIS_ORDER_YZX
|
||||
return static_cast<unsigned int>(y + (z * cChunkDef::Width) + (x * cChunkDef::Height * cChunkDef::Width)); // 1.1 is YZX
|
||||
return y + (z * cChunkDef::Width) + (x * cChunkDef::Height * cChunkDef::Width); // 1.1 is YZX
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -240,7 +236,7 @@ public:
|
|||
{
|
||||
if ((x < Width) && (x > -1) && (y < Height) && (y > -1) && (z < Width) && (z > -1))
|
||||
{
|
||||
unsigned int Index = MakeIndexNoCheck(x, y, z);
|
||||
int Index = MakeIndexNoCheck(x, y, z);
|
||||
return (a_Buffer[Index / 2] >> ((Index & 1) * 4)) & 0x0f;
|
||||
}
|
||||
ASSERT(!"cChunkDef::GetNibble(): coords out of chunk range!");
|
||||
|
@ -274,8 +270,8 @@ public:
|
|||
return;
|
||||
}
|
||||
|
||||
unsigned int Index = MakeIndexNoCheck(x, y, z);
|
||||
a_Buffer[Index / 2] = static_cast<NIBBLETYPE>(
|
||||
int Index = MakeIndexNoCheck(x, y, z);
|
||||
a_Buffer[Index / 2] = (
|
||||
(a_Buffer[Index / 2] & (0xf0 >> ((Index & 1) * 4))) | // The untouched nibble
|
||||
((a_Nibble & 0x0f) << ((Index & 1) * 4)) // The nibble being set
|
||||
);
|
||||
|
|
|
@ -72,6 +72,9 @@ int cCuboid::GetVolume(void) const
|
|||
|
||||
bool cCuboid::DoesIntersect(const cCuboid & a_Other) const
|
||||
{
|
||||
ASSERT(IsSorted());
|
||||
ASSERT(a_Other.IsSorted());
|
||||
|
||||
// In order for cuboids to intersect, each of their coord intervals need to intersect
|
||||
return (
|
||||
DoIntervalsIntersect(p1.x, p2.x, a_Other.p1.x, a_Other.p2.x) &&
|
||||
|
@ -86,6 +89,9 @@ bool cCuboid::DoesIntersect(const cCuboid & a_Other) const
|
|||
|
||||
bool cCuboid::IsCompletelyInside(const cCuboid & a_Outer) const
|
||||
{
|
||||
ASSERT(IsSorted());
|
||||
ASSERT(a_Outer.IsSorted());
|
||||
|
||||
return (
|
||||
(p1.x >= a_Outer.p1.x) &&
|
||||
(p2.x <= a_Outer.p2.x) &&
|
||||
|
@ -197,3 +203,37 @@ bool cCuboid::IsSorted(void) const
|
|||
|
||||
|
||||
|
||||
|
||||
void cCuboid::Engulf(const Vector3i & a_Point)
|
||||
{
|
||||
if (a_Point.x < p1.x)
|
||||
{
|
||||
p1.x = a_Point.x;
|
||||
}
|
||||
else if (a_Point.x > p2.x)
|
||||
{
|
||||
p2.x = a_Point.x;
|
||||
}
|
||||
|
||||
if (a_Point.y < p1.y)
|
||||
{
|
||||
p1.y = a_Point.y;
|
||||
}
|
||||
else if (a_Point.y > p2.y)
|
||||
{
|
||||
p2.y = a_Point.y;
|
||||
}
|
||||
|
||||
if (a_Point.z < p1.z)
|
||||
{
|
||||
p1.z = a_Point.z;
|
||||
}
|
||||
else if (a_Point.z > p2.z)
|
||||
{
|
||||
p2.z = a_Point.z;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
11
src/Cuboid.h
11
src/Cuboid.h
|
@ -34,7 +34,8 @@ public:
|
|||
Works on unsorted cuboids, too. */
|
||||
int GetVolume(void) const;
|
||||
|
||||
/** Returns true if the cuboids have at least one voxel in common. Both coords are considered inclusive. */
|
||||
/** Returns true if the cuboids have at least one voxel in common. Both coords are considered inclusive.
|
||||
Assumes both cuboids are sorted. */
|
||||
bool DoesIntersect(const cCuboid & a_Other) const;
|
||||
|
||||
bool IsInside(const Vector3i & v) const
|
||||
|
@ -64,7 +65,8 @@ public:
|
|||
);
|
||||
}
|
||||
|
||||
/** Returns true if this cuboid is completely inside the specifie cuboid (in all 6 coords) */
|
||||
/** Returns true if this cuboid is completely inside the specifie cuboid (in all 6 coords).
|
||||
Assumes both cuboids are sorted. */
|
||||
bool IsCompletelyInside(const cCuboid & a_Outer) const;
|
||||
|
||||
/** Moves the cuboid by the specified offsets in each direction */
|
||||
|
@ -72,7 +74,7 @@ public:
|
|||
|
||||
/** Expands the cuboid by the specified amount in each direction.
|
||||
Works on unsorted cuboids as well.
|
||||
Note that this function doesn't check for underflows. */
|
||||
Note that this function doesn't check for underflows when using negative amounts. */
|
||||
void Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ);
|
||||
|
||||
/** Clamps both X coords to the specified range. Works on unsorted cuboids, too. */
|
||||
|
@ -86,6 +88,9 @@ public:
|
|||
|
||||
/** Returns true if the coords are properly sorted (lesser in p1, greater in p2) */
|
||||
bool IsSorted(void) const;
|
||||
|
||||
/** If needed, expands the cuboid so that it contains the specified point. Assumes sorted. Doesn't contract. */
|
||||
void Engulf(const Vector3i & a_Point);
|
||||
} ;
|
||||
// tolua_end
|
||||
|
||||
|
|
|
@ -277,6 +277,26 @@ inline eBlockFace RotateBlockFaceCW(eBlockFace a_BlockFace)
|
|||
|
||||
|
||||
|
||||
/** Returns the textual representation of the BlockFace constant. */
|
||||
inline AString BlockFaceToString(eBlockFace a_BlockFace)
|
||||
{
|
||||
switch (a_BlockFace)
|
||||
{
|
||||
case BLOCK_FACE_XM: return "BLOCK_FACE_XM";
|
||||
case BLOCK_FACE_XP: return "BLOCK_FACE_XP";
|
||||
case BLOCK_FACE_YM: return "BLOCK_FACE_YM";
|
||||
case BLOCK_FACE_YP: return "BLOCK_FACE_YP";
|
||||
case BLOCK_FACE_ZM: return "BLOCK_FACE_ZM";
|
||||
case BLOCK_FACE_ZP: return "BLOCK_FACE_ZP";
|
||||
case BLOCK_FACE_NONE: return "BLOCK_FACE_NONE";
|
||||
}
|
||||
return Printf("Unknown BLOCK_FACE: %d", a_BlockFace);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
inline bool IsValidBlock(int a_BlockType)
|
||||
{
|
||||
if (
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "EndGen.h"
|
||||
#include "MineShafts.h"
|
||||
#include "Noise3DGenerator.h"
|
||||
#include "POCPieceGenerator.h"
|
||||
#include "Ravines.h"
|
||||
|
||||
|
||||
|
@ -364,6 +365,10 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
|
|||
{
|
||||
m_FinishGens.push_back(new cStructGenOreNests(Seed));
|
||||
}
|
||||
else if (NoCaseCompare(*itr, "POCPieces") == 0)
|
||||
{
|
||||
m_FinishGens.push_back(new cPOCPieceGenerator(Seed));
|
||||
}
|
||||
else if (NoCaseCompare(*itr, "PreSimulator") == 0)
|
||||
{
|
||||
m_FinishGens.push_back(new cFinishGenPreSimulator);
|
||||
|
|
|
@ -0,0 +1,270 @@
|
|||
|
||||
// POCPieceGenerator.cpp
|
||||
|
||||
// Implements the cPOCPieceGenerator class representing a Proof-Of_Concept structure generator using the cPieceGenerator technique
|
||||
// The generator generates a maze of rooms at {0, 50, 0}
|
||||
|
||||
#include "Globals.h"
|
||||
#include "POCPieceGenerator.h"
|
||||
#include "ChunkDesc.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** POC pieces are simple boxes that have connectors in the middle of their walls.
|
||||
Each wall has one connector, there are 3 connector types that get assigned semi-randomly.
|
||||
The piece also knows how to imprint itself in a cChunkDesc, each piece has a different color glass
|
||||
and each connector is uses a different color wool frame. */
|
||||
class cPOCPiece :
|
||||
public cPiece
|
||||
{
|
||||
public:
|
||||
cPOCPiece(int a_SizeXZ, int a_Height) :
|
||||
m_SizeXZ(a_SizeXZ),
|
||||
m_Height(a_Height)
|
||||
{
|
||||
m_Connectors.push_back(cConnector(m_SizeXZ / 2, a_Height / 2, 0, 0, BLOCK_FACE_ZM));
|
||||
m_Connectors.push_back(cConnector(m_SizeXZ / 2, a_Height / 2, m_SizeXZ - 1, 1, BLOCK_FACE_ZP));
|
||||
m_Connectors.push_back(cConnector(0, a_Height / 2, m_SizeXZ / 2, 2, BLOCK_FACE_XM));
|
||||
m_Connectors.push_back(cConnector(m_SizeXZ - 1, a_Height - 1, m_SizeXZ / 2, m_SizeXZ % 3, BLOCK_FACE_XP));
|
||||
}
|
||||
|
||||
|
||||
/** Imprints the piece in the specified chunk. Assumes they intersect. */
|
||||
void ImprintInChunk(cChunkDesc & a_ChunkDesc, const Vector3i & a_Pos, int a_NumCCWRotations)
|
||||
{
|
||||
int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
|
||||
int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
|
||||
Vector3i Min = a_Pos;
|
||||
Min.Move(-BlockX, 0, -BlockZ);
|
||||
Vector3i Max = Min;
|
||||
Max.Move(m_SizeXZ - 1, m_Height - 1, m_SizeXZ - 1);
|
||||
ASSERT(Min.x < cChunkDef::Width);
|
||||
ASSERT(Min.z < cChunkDef::Width);
|
||||
ASSERT(Max.x >= 0);
|
||||
ASSERT(Max.z >= 0);
|
||||
if (Min.x >= 0)
|
||||
{
|
||||
// Draw the XM wall:
|
||||
a_ChunkDesc.FillRelCuboid(Min.x, Min.x, Min.y, Max.y, Min.z, Max.z, E_BLOCK_STAINED_GLASS, m_SizeXZ % 16);
|
||||
}
|
||||
if (Min.z >= 0)
|
||||
{
|
||||
// Draw the ZM wall:
|
||||
a_ChunkDesc.FillRelCuboid(Min.x, Max.x, Min.y, Max.y, Min.z, Min.z, E_BLOCK_STAINED_GLASS, m_SizeXZ % 16);
|
||||
}
|
||||
if (Max.x < cChunkDef::Width)
|
||||
{
|
||||
// Draw the XP wall:
|
||||
a_ChunkDesc.FillRelCuboid(Max.x, Max.x, Min.y, Max.y, Min.z, Max.z, E_BLOCK_STAINED_GLASS, m_SizeXZ % 16);
|
||||
}
|
||||
if (Max.z < cChunkDef::Width)
|
||||
{
|
||||
// Draw the ZP wall:
|
||||
a_ChunkDesc.FillRelCuboid(Min.x, Max.x, Min.y, Max.y, Max.z, Max.z, E_BLOCK_STAINED_GLASS, m_SizeXZ % 16);
|
||||
}
|
||||
|
||||
// Draw all the connectors:
|
||||
for (cConnectors::const_iterator itr = m_Connectors.begin(), end = m_Connectors.end(); itr != end; ++itr)
|
||||
{
|
||||
cConnector Conn = cPiece::RotateMoveConnector(*itr, a_NumCCWRotations, a_Pos.x, a_Pos.y, a_Pos.z);
|
||||
Conn.m_Pos.Move(-BlockX, 0, -BlockZ);
|
||||
if (
|
||||
(Conn.m_Pos.x >= 0) && (Conn.m_Pos.x < cChunkDef::Width) &&
|
||||
(Conn.m_Pos.z >= 0) && (Conn.m_Pos.z < cChunkDef::Width)
|
||||
)
|
||||
{
|
||||
a_ChunkDesc.SetBlockTypeMeta(Conn.m_Pos.x, Conn.m_Pos.y, Conn.m_Pos.z, E_BLOCK_WOOL, itr->m_Type % 16);
|
||||
}
|
||||
|
||||
/*
|
||||
// TODO: Frame the connectors
|
||||
switch (itr->m_Direction)
|
||||
{
|
||||
case BLOCK_FACE_XM:
|
||||
case BLOCK_FACE_XP:
|
||||
{
|
||||
// TODO
|
||||
break;
|
||||
}
|
||||
|
||||
case BLOCK_FACE_ZM:
|
||||
case BLOCK_FACE_ZP:
|
||||
{
|
||||
// TODO
|
||||
break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
} // for itr - m_Connectors[]
|
||||
}
|
||||
|
||||
protected:
|
||||
int m_SizeXZ;
|
||||
int m_Height;
|
||||
cConnectors m_Connectors;
|
||||
|
||||
// cPiece overrides:
|
||||
virtual cConnectors GetConnectors(void) const override
|
||||
{
|
||||
return m_Connectors;
|
||||
}
|
||||
|
||||
virtual Vector3i GetSize(void) const override
|
||||
{
|
||||
return Vector3i(m_SizeXZ, m_Height, m_SizeXZ);
|
||||
}
|
||||
|
||||
virtual cCuboid GetHitBox(void) const override
|
||||
{
|
||||
return cCuboid(0, 0, 0, m_SizeXZ - 1, m_Height - 1, m_SizeXZ - 1);
|
||||
}
|
||||
|
||||
virtual bool CanRotateCCW(int a_NumRotations) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
static void DebugPieces(const cPlacedPieces & a_Pieces)
|
||||
{
|
||||
size_t idx = 0;
|
||||
for (cPlacedPieces::const_iterator itr = a_Pieces.begin(), end = a_Pieces.end(); itr != end; ++itr, ++idx)
|
||||
{
|
||||
const cCuboid & HitBox = (*itr)->GetHitBox();
|
||||
printf(" %u: %d rotations, {%d - %d, %d - %d}\n",
|
||||
idx, (*itr)->GetNumCCWRotations(),
|
||||
HitBox.p1.x, HitBox.p2.x, HitBox.p1.z, HitBox.p2.z
|
||||
);
|
||||
} // for itr - a_Pieces[]
|
||||
}
|
||||
//*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cPOCPieceGenerator:
|
||||
|
||||
cPOCPieceGenerator::cPOCPieceGenerator(int a_Seed) :
|
||||
m_Seed(a_Seed)
|
||||
{
|
||||
// Prepare a vector of available pieces:
|
||||
m_AvailPieces.push_back(new cPOCPiece(5, 3));
|
||||
m_AvailPieces.push_back(new cPOCPiece(7, 5));
|
||||
m_AvailPieces.push_back(new cPOCPiece(9, 5));
|
||||
m_AvailPieces.push_back(new cPOCPiece(5, 7));
|
||||
|
||||
// Generate the structure:
|
||||
cBFSPieceGenerator Gen(*this, a_Seed);
|
||||
Gen.PlacePieces(0, 50, 0, 6, m_Pieces);
|
||||
|
||||
// DebugPieces(m_Pieces);
|
||||
|
||||
// Get the smallest cuboid encompassing the entire generated structure:
|
||||
cCuboid Bounds(0, 50, 0, 0, 50, 0);
|
||||
for (cPlacedPieces::const_iterator itr = m_Pieces.begin(), end = m_Pieces.end(); itr != end; ++itr)
|
||||
{
|
||||
Vector3i MinCoords = (*itr)->GetCoords();
|
||||
Bounds.Engulf(MinCoords);
|
||||
Bounds.Engulf(MinCoords + (*itr)->GetPiece().GetSize());
|
||||
} // for itr - m_Pieces[]
|
||||
m_Bounds = Bounds;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cPOCPieceGenerator::~cPOCPieceGenerator()
|
||||
{
|
||||
cPieceGenerator::FreePieces(m_Pieces);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPOCPieceGenerator::GenFinish(cChunkDesc & a_ChunkDesc)
|
||||
{
|
||||
int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
|
||||
int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
|
||||
if (
|
||||
(BlockX + 16 < m_Bounds.p1.x) || (BlockX > m_Bounds.p2.x) || // X coords out of bounds of the generated structure
|
||||
(BlockZ + 16 < m_Bounds.p1.z) || (BlockZ > m_Bounds.p2.z) // Z coords out of bounds of the generated structure
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Imprint each piece in the chunk:
|
||||
for (cPlacedPieces::const_iterator itr = m_Pieces.begin(), end = m_Pieces.end(); itr != end; ++itr)
|
||||
{
|
||||
const Vector3i & Pos = (*itr)->GetCoords();
|
||||
Vector3i Size = (*itr)->GetPiece().GetSize();
|
||||
if (((*itr)->GetNumCCWRotations() % 2) == 1)
|
||||
{
|
||||
std::swap(Size.x, Size.z);
|
||||
}
|
||||
if (
|
||||
(Pos.x >= BlockX + 16) || (Pos.x + Size.x - 1 < BlockX) ||
|
||||
(Pos.z >= BlockZ + 16) || (Pos.z + Size.z - 1 < BlockZ)
|
||||
)
|
||||
{
|
||||
// This piece doesn't intersect the chunk
|
||||
continue;
|
||||
}
|
||||
|
||||
((cPOCPiece &)(*itr)->GetPiece()).ImprintInChunk(a_ChunkDesc, Pos, (*itr)->GetNumCCWRotations());
|
||||
} // for itr - m_Pieces[]
|
||||
a_ChunkDesc.UpdateHeightmap();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cPieces cPOCPieceGenerator::GetPiecesWithConnector(int a_ConnectorType)
|
||||
{
|
||||
// Each piece has each connector
|
||||
return m_AvailPieces;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cPieces cPOCPieceGenerator::GetStartingPieces(void)
|
||||
{
|
||||
// Any piece can be a starting piece
|
||||
return m_AvailPieces;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPOCPieceGenerator::PiecePlaced(const cPiece & a_Piece)
|
||||
{
|
||||
UNUSED(a_Piece);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPOCPieceGenerator::Reset(void)
|
||||
{
|
||||
// Nothing needed
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
|
||||
// POCPieceGenerator.h
|
||||
|
||||
// Declares the cPOCPieceGenerator class representing a Proof-Of_Concept structure generator using the cPieceGenerator technique
|
||||
// The generator generates a maze of rooms at {0, 100, 0}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "PieceGenerator.h"
|
||||
#include "ComposableGenerator.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cPOCPieceGenerator :
|
||||
public cFinishGen,
|
||||
protected cPiecePool
|
||||
{
|
||||
public:
|
||||
cPOCPieceGenerator(int a_Seed);
|
||||
~cPOCPieceGenerator();
|
||||
|
||||
protected:
|
||||
int m_Seed;
|
||||
|
||||
/** The pieces from which the generated structure is built. */
|
||||
cPieces m_AvailPieces;
|
||||
|
||||
/** The placed pieces of the generated structure. */
|
||||
cPlacedPieces m_Pieces;
|
||||
|
||||
/** Bounds of the complete structure, to save on processing outside chunks. */
|
||||
cCuboid m_Bounds;
|
||||
|
||||
|
||||
// cFinishGen overrides:
|
||||
virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
|
||||
|
||||
// cPiecePool overrides:
|
||||
virtual cPieces GetPiecesWithConnector(int a_ConnectorType) override;
|
||||
virtual cPieces GetStartingPieces(void) override;
|
||||
virtual void PiecePlaced(const cPiece & a_Piece) override;
|
||||
virtual void Reset(void) override;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,626 @@
|
|||
|
||||
// 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"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef SELF_TEST
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Self-test:
|
||||
|
||||
static class cPieceGeneratorSelfTest :
|
||||
public cPiecePool
|
||||
{
|
||||
public:
|
||||
cPieceGeneratorSelfTest(void)
|
||||
{
|
||||
// Prepare the internal state:
|
||||
InitializePieces();
|
||||
|
||||
// Generate:
|
||||
cBFSPieceGenerator Gen(*this, 0);
|
||||
cPlacedPieces OutPieces;
|
||||
Gen.PlacePieces(500, 50, 500, 3, OutPieces);
|
||||
|
||||
// Print out the pieces:
|
||||
printf("OutPieces.size() = %u\n", OutPieces.size());
|
||||
size_t idx = 0;
|
||||
for (cPlacedPieces::const_iterator itr = OutPieces.begin(), end = OutPieces.end(); itr != end; ++itr, ++idx)
|
||||
{
|
||||
const Vector3i & Coords = (*itr)->GetCoords();
|
||||
cCuboid Hitbox = (*itr)->GetHitBox();
|
||||
Hitbox.Sort();
|
||||
printf("%u: {%d, %d, %d}, rot %d, hitbox {%d, %d, %d} - {%d, %d, %d} (%d * %d * %d)\n", idx,
|
||||
Coords.x, Coords.y, Coords.z,
|
||||
(*itr)->GetNumCCWRotations(),
|
||||
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
|
||||
);
|
||||
} // itr - OutPieces[]
|
||||
printf("Done.\n");
|
||||
|
||||
// Free the placed pieces properly:
|
||||
Gen.FreePieces(OutPieces);
|
||||
}
|
||||
|
||||
~cPieceGeneratorSelfTest()
|
||||
{
|
||||
// Dealloc all the pieces:
|
||||
for (cPieces::iterator itr = m_Pieces.begin(), end = m_Pieces.end(); itr != end; ++itr)
|
||||
{
|
||||
delete *itr;
|
||||
}
|
||||
m_Pieces.clear();
|
||||
}
|
||||
|
||||
protected:
|
||||
class cTestPiece :
|
||||
public cPiece
|
||||
{
|
||||
int m_Size;
|
||||
public:
|
||||
cTestPiece(int a_Size) :
|
||||
m_Size(a_Size)
|
||||
{
|
||||
}
|
||||
|
||||
virtual cConnectors GetConnectors(void) const override
|
||||
{
|
||||
// Each piece has 4 connectors, one of each type, plus one extra, at the center of its walls:
|
||||
cConnectors res;
|
||||
res.push_back(cConnector(m_Size / 2, 1, 0, 0, BLOCK_FACE_ZM));
|
||||
res.push_back(cConnector(m_Size / 2, 1, m_Size - 1, 1, BLOCK_FACE_ZP));
|
||||
res.push_back(cConnector(0, 1, m_Size / 2, 2, BLOCK_FACE_XM));
|
||||
res.push_back(cConnector(m_Size - 1, 1, m_Size / 2, m_Size % 3, BLOCK_FACE_XP));
|
||||
return res;
|
||||
}
|
||||
|
||||
virtual Vector3i GetSize(void) const override
|
||||
{
|
||||
return Vector3i(m_Size, 5, m_Size);
|
||||
}
|
||||
|
||||
virtual cCuboid GetHitBox(void) const override
|
||||
{
|
||||
return cCuboid(0, 0, 0, m_Size - 1, 4, m_Size - 1);
|
||||
}
|
||||
|
||||
virtual bool CanRotateCCW(int a_NumCCWRotations) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
cPieces m_Pieces;
|
||||
|
||||
virtual cPieces GetPiecesWithConnector(int a_ConnectorType) override
|
||||
{
|
||||
// Each piece contains each connector
|
||||
return m_Pieces;
|
||||
}
|
||||
|
||||
|
||||
virtual cPieces GetStartingPieces(void) override
|
||||
{
|
||||
return m_Pieces;
|
||||
}
|
||||
|
||||
|
||||
virtual void PiecePlaced(const cPiece & a_Piece) override
|
||||
{
|
||||
UNUSED(a_Piece);
|
||||
}
|
||||
|
||||
|
||||
virtual void Reset(void) override
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void InitializePieces(void)
|
||||
{
|
||||
m_Pieces.push_back(new cTestPiece(5));
|
||||
m_Pieces.push_back(new cTestPiece(7));
|
||||
m_Pieces.push_back(new cTestPiece(9));
|
||||
}
|
||||
} g_Test;
|
||||
|
||||
#endif // SELF_TEST
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cPiece:
|
||||
|
||||
|
||||
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:
|
||||
Vector3i Size = GetSize();
|
||||
switch (a_NumCCWRotations)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// No rotation needed
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
// 1 CCW rotation:
|
||||
res.m_Direction = RotateBlockFaceCCW(res.m_Direction);
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
// 2 rotations ( = axis flip):
|
||||
res.m_Direction = MirrorBlockFaceY(res.m_Direction);
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
// 1 CW rotation:
|
||||
res.m_Direction = RotateBlockFaceCW(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, eBlockFace 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, eBlockFace a_Direction) :
|
||||
m_Pos(a_Pos),
|
||||
m_Type(a_Type),
|
||||
m_Direction(a_Direction)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// 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);
|
||||
m_HitBox = a_Piece.RotateMoveHitBox(a_NumCCWRotations, a_Coords.x, a_Coords.y, a_Coords.z);
|
||||
m_HitBox.Sort();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// 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_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 = new cPlacedPiece(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,
|
||||
cPieceGenerator::cFreeConnectors & a_OutConnectors
|
||||
)
|
||||
{
|
||||
// Translation of direction - direction -> number of CCW rotations needed:
|
||||
// You need DirectionRotationTable[rot1][rot2] CCW turns to connect rot1 to rot2 (they are opposite)
|
||||
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, 2, 0, 1, 3},
|
||||
/* ZP */ { 0, 0, 0, 2, 3, 1},
|
||||
/* XM */ { 0, 0, 3, 1, 2, 0},
|
||||
/* XP */ { 0, 0, 1, 3, 0, 2},
|
||||
};
|
||||
|
||||
// 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());
|
||||
Vector3i ConnPos = a_Connector.m_Pos; // The position at which the new connector should be placed - 1 block away from the connector
|
||||
AddFaceDirection(ConnPos.x, ConnPos.y, ConnPos.z, a_Connector.m_Direction);
|
||||
|
||||
/*
|
||||
// DEBUG:
|
||||
printf("Placing piece at connector pos {%d, %d, %d}, direction %s\n", ConnPos.x, ConnPos.y, ConnPos.z, BlockFaceToString(a_Connector.m_Direction).c_str());
|
||||
//*/
|
||||
|
||||
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, ConnPos, **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_Pos.x, a_Connector.m_Pos.y, a_Connector.m_Pos.z) / 7;
|
||||
cConnection & Conn = Connections[rnd % Connections.size()];
|
||||
|
||||
// Place the piece:
|
||||
/*
|
||||
// DEBUG
|
||||
printf("Chosen connector at {%d, %d, %d}, direction %s, needs %d rotations\n",
|
||||
Conn.m_Connector.m_Pos.x, Conn.m_Connector.m_Pos.y, Conn.m_Connector.m_Pos.z,
|
||||
BlockFaceToString(Conn.m_Connector.m_Direction).c_str(),
|
||||
Conn.m_NumCCWRotations
|
||||
);
|
||||
//*/
|
||||
|
||||
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();
|
||||
|
||||
/*
|
||||
// DEBUG:
|
||||
printf("Adding %u connectors to the pool\n", Connectors.size() - 1);
|
||||
//*/
|
||||
|
||||
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: %u items\n", a_ConnectorPool.size() - a_NumProcessed);
|
||||
size_t idx = 0;
|
||||
for (cPieceGenerator::cFreeConnectors::const_iterator itr = a_ConnectorPool.begin() + a_NumProcessed, end = a_ConnectorPool.end(); itr != end; ++itr, ++idx)
|
||||
{
|
||||
printf(" %u: {%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,
|
||||
BlockFaceToString(itr->m_Connector.m_Direction).c_str(),
|
||||
itr->m_Piece->GetDepth()
|
||||
);
|
||||
} // for itr - a_ConnectorPool[]
|
||||
}
|
||||
//*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// 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, int a_MaxDepth, cPlacedPieces & a_OutPieces)
|
||||
{
|
||||
a_OutPieces.clear();
|
||||
cFreeConnectors ConnectorPool;
|
||||
|
||||
// Place the starting piece:
|
||||
a_OutPieces.push_back(PlaceStartingPiece(a_BlockX, a_BlockY, 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)
|
||||
{
|
||||
ConnectorPool.erase(ConnectorPool.begin(), ConnectorPool.begin() + NumProcessed);
|
||||
NumProcessed = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,247 @@
|
|||
|
||||
// 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:
|
||||
// Force a virtual destructor in all descendants
|
||||
virtual ~cPiece() {}
|
||||
|
||||
struct cConnector
|
||||
{
|
||||
/** Position relative to the piece */
|
||||
Vector3i m_Pos;
|
||||
|
||||
/** Type of the connector. Any arbitrary number; the generator connects only connectors of the same type. */
|
||||
int m_Type;
|
||||
|
||||
/** Direction in which the connector is facing.
|
||||
Will be matched by the opposite direction for the connecting connector. */
|
||||
eBlockFace m_Direction;
|
||||
|
||||
cConnector(int a_X, int a_Y, int a_Z, int a_Type, eBlockFace a_Direction);
|
||||
cConnector(const Vector3i & a_Pos, int a_Type, eBlockFace a_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 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<cPiece *> cPieces;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** 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;
|
||||
|
||||
/** 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);
|
||||
|
||||
/** 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
|
||||
|
||||
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, // 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.
|
||||
Caller must free each individual cPlacedPiece in a_OutPieces. */
|
||||
void PlacePieces(int a_BlockX, int a_BlockY, 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.
|
||||
Caller must free each individual cPlacedPiece in a_OutPieces. */
|
||||
void PlacePieces(int a_BlockX, int a_BlockY, int a_BlockZ, cPlacedPieces & a_OutPieces);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,6 +1,11 @@
|
|||
|
||||
// Vector3i.cpp
|
||||
|
||||
// Implements the Vector3i class representing an int-based 3D vector
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "math.h"
|
||||
#include "Vector3i.h"
|
||||
#include "Vector3d.h"
|
||||
|
||||
|
@ -8,9 +13,46 @@
|
|||
|
||||
|
||||
|
||||
Vector3i::Vector3i( const Vector3d & v )
|
||||
: x( (int)v.x )
|
||||
, y( (int)v.y )
|
||||
, z( (int)v.z )
|
||||
Vector3i::Vector3i(const Vector3d & v) :
|
||||
x((int)v.x),
|
||||
y((int)v.y),
|
||||
z((int)v.z)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Vector3i::Vector3i(void) :
|
||||
x(0),
|
||||
y(0),
|
||||
z(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Vector3i::Vector3i(int a_x, int a_y, int a_z) :
|
||||
x(a_x),
|
||||
y(a_y),
|
||||
z(a_z)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void Vector3i::Move(int a_MoveX, int a_MoveY, int a_MoveZ)
|
||||
{
|
||||
x += a_MoveX;
|
||||
y += a_MoveY;
|
||||
z += a_MoveZ;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,22 +1,45 @@
|
|||
|
||||
// Vector3i.h
|
||||
|
||||
// Declares the Vector3i class representing an int-based 3D vector
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <math.h>
|
||||
|
||||
|
||||
|
||||
|
||||
// fwd:
|
||||
class Vector3d;
|
||||
class Vector3i // tolua_export
|
||||
{ // tolua_export
|
||||
public: // tolua_export
|
||||
Vector3i( const Vector3d & v ); // tolua_export
|
||||
|
||||
Vector3i() : x(0), y(0), z(0) {} // tolua_export
|
||||
Vector3i(int a_x, int a_y, int a_z) : x(a_x), y(a_y), z(a_z) {} // tolua_export
|
||||
|
||||
inline void Set(int a_x, int a_y, int a_z) { x = a_x, y = a_y, z = a_z; } // tolua_export
|
||||
inline float Length() const { return sqrtf( (float)( x * x + y * y + z * z) ); } // tolua_export
|
||||
inline int SqrLength() const { return x * x + y * y + z * z; } // tolua_export
|
||||
|
||||
inline bool Equals( const Vector3i & v ) const { return (x == v.x && y == v.y && z == v.z ); } // tolua_export
|
||||
inline bool Equals( const Vector3i * v ) const { return (x == v->x && y == v->y && z == v->z ); } // tolua_export
|
||||
|
||||
|
||||
// tolua_begin
|
||||
class Vector3i
|
||||
{
|
||||
public:
|
||||
/** Creates an int vector based on the floor()-ed coords of a double vector. */
|
||||
Vector3i(const Vector3d & v);
|
||||
|
||||
Vector3i(void);
|
||||
Vector3i(int a_x, int a_y, int a_z);
|
||||
|
||||
inline void Set(int a_x, int a_y, int a_z) { x = a_x, y = a_y, z = a_z; }
|
||||
inline float Length() const { return sqrtf( (float)( x * x + y * y + z * z) ); }
|
||||
inline int SqrLength() const { return x * x + y * y + z * z; }
|
||||
|
||||
inline bool Equals( const Vector3i & v ) const { return (x == v.x && y == v.y && z == v.z ); }
|
||||
inline bool Equals( const Vector3i * v ) const { return (x == v->x && y == v->y && z == v->z ); }
|
||||
|
||||
void Move(int a_MoveX, int a_MoveY, int a_MoveZ);
|
||||
|
||||
// tolua_end
|
||||
|
||||
void operator += ( const Vector3i& a_V ) { x += a_V.x; y += a_V.y; z += a_V.z; }
|
||||
void operator += ( Vector3i* a_V ) { x += a_V->x; y += a_V->y; z += a_V->z; }
|
||||
|
|
Loading…
Reference in New Issue