1
0
Fork 0

PrefabPiecePool: Added loading from cubeset file.

This commit is contained in:
Mattes D 2015-06-17 15:22:43 +02:00
parent c6012a95bd
commit ae16689420
4 changed files with 589 additions and 3 deletions

View File

@ -166,6 +166,45 @@ cPrefab::cPrefab(const cBlockArea & a_Image, int a_AllowedRotations) :
cPrefab::cPrefab(const cBlockArea & a_Image) :
m_Size(a_Image.GetSize()),
m_AllowedRotations(0),
m_MergeStrategy(cBlockArea::msOverwrite),
m_ShouldExtendFloor(false),
m_DefaultWeight(1),
m_AddWeightIfSame(0),
m_MoveToGround(false)
{
m_HitBox.p1.Set(0, 0, 0);
m_HitBox.p2.Set(m_Size.x - 1, m_Size.y - 1, m_Size.z - 1);
m_BlockArea[0].CopyFrom(a_Image);
}
cPrefab::cPrefab(const AString & a_BlockDefinitions, const AString & a_BlockData, int a_SizeX, int a_SizeY, int a_SizeZ) :
m_Size(a_SizeX, a_SizeY, a_SizeZ),
m_AllowedRotations(0),
m_MergeStrategy(cBlockArea::msOverwrite),
m_ShouldExtendFloor(false),
m_DefaultWeight(1),
m_AddWeightIfSame(0),
m_MoveToGround(false)
{
m_HitBox.p1.Set(0, 0, 0);
m_HitBox.p2.Set(m_Size.x - 1, m_Size.y - 1, m_Size.z - 1);
m_BlockArea[0].Create(m_Size);
CharMap cm;
ParseCharMap(cm, a_BlockDefinitions.c_str());
ParseBlockImage(cm, a_BlockData.c_str());
}
void cPrefab::AddRotatedBlockAreas(void)
{
// 1 CCW rotation:
@ -326,6 +365,16 @@ void cPrefab::AddConnector(int a_RelX, int a_RelY, int a_RelZ, eBlockFace a_Dire
void cPrefab::SetAllowedRotations(int a_AllowedRotations)
{
m_AllowedRotations = a_AllowedRotations;
AddRotatedBlockAreas();
}
void cPrefab::ParseCharMap(CharMap & a_CharMapOut, const char * a_CharMapDef)
{
ASSERT(a_CharMapDef != nullptr);

View File

@ -95,6 +95,13 @@ public:
/** Creates a prefab based on the given BlockArea and allowed rotations. */
cPrefab(const cBlockArea & a_Image, int a_AllowedRotations);
/** Creates a prefab based on the given BlockArea. Allowed rotations can be added later on using SetAllowedRotations(). */
cPrefab(const cBlockArea & a_Image);
/** Creates a prefab based on the specified block data, using the char-to-block map in a_BlockDefinitions.
Allowed rotations can be added later on using SetAllowedRotations(). */
cPrefab(const AString & a_BlockDefinitions, const AString & a_BlockData, int a_SizeX, int a_SizeY, int a_SizeZ);
/** Draws the prefab into the specified chunk, according to the placement stored in the PlacedPiece. */
void Draw(cChunkDesc & a_Dest, const cPlacedPiece * a_Placement) const;
@ -124,6 +131,21 @@ public:
at the coords governed by the connectors. */
bool ShouldMoveToGround(void) const { return m_MoveToGround; }
/** Sets the m_AllowedRotations bitmask and fills the m_BlockArea[] with rotated versions of m_BlockArea[0]. */
void SetAllowedRotations(int a_AllowedRotations);
/** Parses the per-depth weight into m_DepthWeight member. */
void ParseDepthWeight(const char * a_DepthWeightDef);
/** Sets the merge strategy to be used when drawing the piece. */
void SetMergeStrategy(cBlockArea::eMergeStrategy a_MergeStrategy) { m_MergeStrategy = a_MergeStrategy; }
/** Sets the flag whether the prefab should be moved to ground level before being drawn. */
void SetMoveToGround(bool a_MoveToGround) { m_MoveToGround = a_MoveToGround; }
/** Sets the flag whether the lowest layer of the prefab should be repeated downwards until it hits a solid block. */
void SetExtendFloor(bool a_ShouldExtendFloor) { m_ShouldExtendFloor = a_ShouldExtendFloor; }
protected:
/** Packs complete definition of a single block, for per-letter assignment. */
struct sBlockTypeDef
@ -201,9 +223,6 @@ protected:
/** Parses the connectors definition text into m_Connectors member. */
void ParseConnectors(const char * a_ConnectorsDef);
/** Parses the per-depth weight into m_DepthWeight member. */
void ParseDepthWeight(const char * a_DepthWeightDef);
};

View File

@ -5,6 +5,81 @@
#include "Globals.h"
#include "PrefabPiecePool.h"
#include "../Bindings/LuaState.h"
#include "SelfTests.h"
#include "WorldStorage/SchematicFileSerializer.h"
// Conditionally log a warning
#define CONDWARNING(ShouldLog, ...) \
if (ShouldLog) \
{ \
LOGWARNING(__VA_ARGS__); \
}
#if SELF_TEST
static class cPrefabPiecePoolTest
{
public:
cPrefabPiecePoolTest(void)
{
cSelfTests::Get().Register(cSelfTests::SelfTestFunction(cPrefabPiecePoolTest::TestLoading), "PrefabPiecePool loading test");
}
static void TestLoading(void)
{
cPrefabPiecePool test;
auto res = test.LoadFromFile("test.cubeset", true);
if (!res)
{
LOGWARNING("Loading from file \"test.cubeset\" failed.");
return;
}
LOG("Loaded %u pieces and %u starting pieces", static_cast<unsigned>(test.GetAllPiecesCount()), static_cast<unsigned>(test.GetStartingPiecesCount()));
}
} g_Test;
#endif
/** Returns the map of string => eMergeStrategy used when translating cubeset file merge strategies. */
static std::map<AString, cBlockArea::eMergeStrategy> & GetMergeStrategyMap(void)
{
static std::map<AString, cBlockArea::eMergeStrategy> msmap;
if (msmap.empty())
{
// This is the first use, initialize the map:
msmap["msOverwrite"] = cBlockArea::msOverwrite;
msmap["msFillAir"] = cBlockArea::msFillAir;
msmap["msImprint"] = cBlockArea::msImprint;
msmap["msLake"] = cBlockArea::msLake;
msmap["msSpongePrint"] = cBlockArea::msSpongePrint;
msmap["msDifference"] = cBlockArea::msDifference;
msmap["msSimpleCompare"] = cBlockArea::msSimpleCompare;
msmap["msMask"] = cBlockArea::msMask;
}
return msmap;
}
////////////////////////////////////////////////////////////////////////////////
// cPrefabPiecePool:
cPrefabPiecePool::cPrefabPiecePool(void)
{
}
@ -26,6 +101,15 @@ cPrefabPiecePool::cPrefabPiecePool(
cPrefabPiecePool::cPrefabPiecePool(const AString & a_FileName, bool a_LogWarnings)
{
LoadFromFile(a_FileName, a_LogWarnings);
}
cPrefabPiecePool::~cPrefabPiecePool()
{
Clear();
@ -83,6 +167,64 @@ void cPrefabPiecePool::AddStartingPieceDefs(const cPrefab::sDef * a_StartingPiec
bool cPrefabPiecePool::LoadFromFile(const AString & a_FileName, bool a_LogWarnings)
{
// Read the first 4 KiB of the file in order to auto-detect format:
cFile f;
if (!f.Open(a_FileName, cFile::fmRead))
{
return false;
}
char buf[4096];
auto len = f.Read(buf, sizeof(buf));
f.Close();
AString Header(buf, static_cast<size_t>(len));
if (Header.find("CubesetFormatVersion =") != AString::npos)
{
return LoadFromCubesetFile(a_FileName, a_LogWarnings);
}
return false;
}
bool cPrefabPiecePool::LoadFromCubesetFile(const AString & a_FileName, bool a_LogWarnings)
{
// Load the file in the Lua interpreter:
cLuaState Lua(Printf("LoadablePiecePool %s", a_FileName.c_str()));
Lua.Create();
if (!Lua.LoadFile(a_FileName, a_LogWarnings))
{
// Reason for failure has already been logged in LoadFile()
return false;
}
// Check the version:
int Version = 0;
if (!Lua.GetNamedGlobal("Cubeset.Metadata.CubesetFormatVersion", Version))
{
CONDWARNING(a_LogWarnings, "Cannot load cubeset %s, it doesn't contain version information.", a_FileName.c_str());
return false;
}
// Load the data, using the correct version loader:
if (Version == 1)
{
return LoadFromCubesetFileVer1(a_FileName, Lua, a_LogWarnings);
}
// Unknown version:
CONDWARNING(a_LogWarnings, "Cannot load cubeset %s, version (%d) not supported.", a_FileName.c_str(), Version);
return false;
}
void cPrefabPiecePool::AddToPerConnectorMap(cPrefab * a_Prefab)
{
cPiece::cConnectors Connectors = (static_cast<const cPiece *>(a_Prefab))->GetConnectors();
@ -95,6 +237,303 @@ void cPrefabPiecePool::AddToPerConnectorMap(cPrefab * a_Prefab)
bool cPrefabPiecePool::LoadFromCubesetFileVer1(const AString & a_FileName, cLuaState & a_LuaState, bool a_LogWarnings)
{
// Push the Cubeset.Pieces global value on the stack:
lua_getglobal(a_LuaState, "_G");
cLuaState::cStackValue stk(a_LuaState);
auto pieces = a_LuaState.WalkToValue("Cubeset.Pieces");
if (!pieces.IsValid() || !lua_istable(a_LuaState, -1))
{
CONDWARNING(a_LogWarnings, "The cubeset file %s doesn't contain any pieces", a_FileName.c_str());
return false;
}
// Iterate over all items in the Cubeset.Pieces value:
int idx = 1;
bool res = true;
while (true)
{
lua_pushinteger(a_LuaState, idx); // stk: [Pieces] [idx]
lua_gettable(a_LuaState, -2); // stk: [Pieces] [PieceItem]
if (!lua_istable(a_LuaState, -1))
{
// The PieceItem is not present, we've iterated over all items
lua_pop(a_LuaState, 1); // stk: [Pieces]
break;
}
if (!LoadCubesetPieceVer1(a_FileName, a_LuaState, idx, a_LogWarnings))
{
res = false;
}
lua_pop(a_LuaState, 1); // stk: [Pieces]
idx += 1;
}
return res;
}
bool cPrefabPiecePool::LoadCubesetPieceVer1(const AString & a_FileName, cLuaState & a_LuaState, int a_PieceIndex, bool a_LogWarnings)
{
ASSERT(lua_istable(a_LuaState, -1));
// The piece name is optional, but useful for debugging messages:
AString PieceName;
if (!a_LuaState.GetNamedValue("OriginData.ExportName", PieceName))
{
Printf(PieceName, "Piece #%d", a_PieceIndex);
}
// Read the hitbox dimensions:
cCuboid Hitbox;
if (
!a_LuaState.GetNamedValue("Hitbox.MinX", Hitbox.p1.x) ||
!a_LuaState.GetNamedValue("Hitbox.MinY", Hitbox.p1.y) ||
!a_LuaState.GetNamedValue("Hitbox.MinZ", Hitbox.p1.z) ||
!a_LuaState.GetNamedValue("Hitbox.MaxX", Hitbox.p2.x) ||
!a_LuaState.GetNamedValue("Hitbox.MaxY", Hitbox.p2.y) ||
!a_LuaState.GetNamedValue("Hitbox.MaxZ", Hitbox.p2.z)
)
{
CONDWARNING(a_LogWarnings, "Cannot load piece %s from file %s, it's missing hitbox information", PieceName.c_str(), a_FileName.c_str());
return false;
}
// Load the prefab data:
auto prefab = LoadPrefabFromCubesetVer1(a_FileName, a_LuaState, PieceName, a_LogWarnings);
if (prefab == nullptr)
{
return false;
}
// Read the connectors
if (!ReadConnectorsCubesetVer1(a_FileName, a_LuaState, PieceName, prefab.get(), a_LogWarnings))
{
return false;
}
// Read the allowed rotations. It is an optional metadata value, default to 0:
int AllowedRotations = 0;
a_LuaState.GetNamedValue("Metadata.AllowedRotations", AllowedRotations);
prefab->SetAllowedRotations(AllowedRotations);
// Apply the relevant metadata:
if (!ApplyMetadataCubesetVer1(a_FileName, a_LuaState, PieceName, prefab.get(), a_LogWarnings))
{
return false;
}
// Add the prefab into the list of pieces:
int IsStartingPiece = 0;
a_LuaState.GetNamedValue("Metadata.IsStarting", IsStartingPiece);
if (IsStartingPiece != 0)
{
m_StartingPieces.push_back(prefab.release());
}
else
{
auto p = prefab.release();
m_AllPieces.push_back(p);
AddToPerConnectorMap(p);
}
return true;
}
UniquePtr<cPrefab> cPrefabPiecePool::LoadPrefabFromCubesetVer1(
const AString & a_FileName,
cLuaState & a_LuaState,
const AString & a_PieceName,
bool a_LogWarnings
)
{
// First try loading a referenced schematic file, if any:
AString SchematicFileName;
if (a_LuaState.GetNamedValue("SchematicFileName", SchematicFileName))
{
auto PathEnd = a_FileName.find_last_of("/\\"); // Find the last path separator
if (PathEnd != AString::npos)
{
SchematicFileName = a_FileName.substr(0, PathEnd) + SchematicFileName;
}
cBlockArea area;
if (!cSchematicFileSerializer::LoadFromSchematicFile(area, SchematicFileName))
{
CONDWARNING(a_LogWarnings, "Cannot load schematic file \"%s\" for piece %s in cubeset %s.",
SchematicFileName.c_str(), a_PieceName.c_str(), a_FileName.c_str()
);
return nullptr;
}
return cpp14::make_unique<cPrefab>(area);
} // if (SchematicFileName)
// There's no referenced schematic file, load from BlockDefinitions / BlockData.
// Get references to the data and the table.concat function:
cLuaState::cRef TableConcat, BlockDefinitions, BlockData;
if (
!a_LuaState.GetNamedGlobal("table.concat", TableConcat) ||
!a_LuaState.GetNamedValue("BlockDefinitions", BlockDefinitions) ||
!a_LuaState.GetNamedValue("BlockData", BlockData)
)
{
CONDWARNING(a_LogWarnings, "Cannot parse block data for piece %s in cubeset %s", a_PieceName.c_str(), a_FileName.c_str());
return nullptr;
}
// Call table.concat() on the BlockDefinitions:
AString BlockDefStr;
if (!a_LuaState.Call(TableConcat, BlockDefinitions, "\n", cLuaState::Return, BlockDefStr))
{
CONDWARNING(a_LogWarnings, "Cannot concat block definitions for piece %s in cubeset %s", a_PieceName.c_str(), a_FileName.c_str());
return nullptr;
}
// Call table.concat() on the BlockData:
AString BlockDataStr;
if (!a_LuaState.Call(TableConcat, BlockData, "", cLuaState::Return, BlockDataStr))
{
CONDWARNING(a_LogWarnings, "Cannot concat block data for piece %s in cubeset %s", a_PieceName.c_str(), a_FileName.c_str());
return nullptr;
}
// Read the size:
int SizeX = 0, SizeY = 0, SizeZ = 0;
if (
!a_LuaState.GetNamedValue("Size.x", SizeX) ||
!a_LuaState.GetNamedValue("Size.y", SizeY) ||
!a_LuaState.GetNamedValue("Size.z", SizeZ)
)
{
CONDWARNING(a_LogWarnings, "Cannot load piece %s from file %s, its size information is missing", a_PieceName.c_str(), a_FileName.c_str());
return nullptr;
}
// Check that the size matches the data length:
if (static_cast<size_t>(SizeX * SizeY * SizeZ) != BlockDataStr.size())
{
CONDWARNING(a_LogWarnings, "Cannot create piece %s from file %s, its size (%d) doesn't match the blockdata length (%u)",
a_PieceName.c_str(), a_FileName.c_str(),
SizeX * SizeY * SizeZ, static_cast<unsigned>(BlockDataStr.size())
);
return nullptr;
}
return cpp14::make_unique<cPrefab>(BlockDefStr, BlockDataStr, SizeX, SizeY, SizeZ);
}
bool cPrefabPiecePool::ReadConnectorsCubesetVer1(
const AString & a_FileName,
cLuaState & a_LuaState,
const AString & a_PieceName,
cPrefab * a_Prefab,
bool a_LogWarnings
)
{
// Get the Connectors subtable:
auto conns = a_LuaState.WalkToValue("Connectors");
if (!conns.IsValid())
{
CONDWARNING(a_LogWarnings, "Cannot load piece %s from file %s, it has no connectors definition.", a_PieceName.c_str(), a_FileName.c_str());
return false;
}
// Iterate over all items in the Connectors table:
int idx = 1;
bool res = true;
while (true)
{
lua_pushinteger(a_LuaState, idx); // stk: [Connectors] [idx]
lua_gettable(a_LuaState, -2); // stk: [Connectors] [conn]
if (!lua_istable(a_LuaState, -1))
{
// The connector is not present, we've iterated over all items
lua_pop(a_LuaState, 1); // stk: [Connectors]
break;
}
int Type = 0, RelX = 0, RelY = 0, RelZ = 0;
eBlockFace Direction = BLOCK_FACE_NONE;
if (
!a_LuaState.GetNamedValue("Type", Type) ||
!a_LuaState.GetNamedValue("RelX", RelX) ||
!a_LuaState.GetNamedValue("RelY", RelY) ||
!a_LuaState.GetNamedValue("RelZ", RelZ) ||
!a_LuaState.GetNamedValue("Direction", Direction)
)
{
CONDWARNING(a_LogWarnings, "Piece %s in file %s has a malformed Connector at index %d. Skipping the connector.", a_PieceName.c_str(), a_FileName.c_str(), idx);
res = false;
continue;
}
a_Prefab->AddConnector(RelX, RelY, RelZ, Direction, Type);
lua_pop(a_LuaState, 1); // stk: [Connectors]
idx += 1;
}
return res;
}
bool cPrefabPiecePool::ApplyMetadataCubesetVer1(
const AString & a_FileName,
cLuaState & a_LuaState,
const AString & a_PieceName,
cPrefab * a_Prefab,
bool a_LogWarnings
)
{
// Push the Metadata table on top of the Lua stack:
auto md = a_LuaState.WalkToValue("Metadata");
if (!md.IsValid())
{
return false;
}
// Get the values:
int AddWeightIfSame = 0, DefaultWeight = 100, MoveToGround = 0, ShouldExpandFloor = 0;
AString DepthWeight, MergeStrategy;
a_LuaState.GetNamedValue("AddWeightIfSame", AddWeightIfSame);
a_LuaState.GetNamedValue("DefaultWeight", DefaultWeight);
a_LuaState.GetNamedValue("DepthWeight", DepthWeight);
a_LuaState.GetNamedValue("MergeStrategy", MergeStrategy);
a_LuaState.GetNamedValue("MoveToGround", MoveToGround);
a_LuaState.GetNamedValue("ShouldExpandFloor", ShouldExpandFloor);
// Apply the values:
a_Prefab->SetAddWeightIfSame(AddWeightIfSame);
a_Prefab->SetDefaultWeight(DefaultWeight);
a_Prefab->ParseDepthWeight(DepthWeight.c_str());
auto msmap = GetMergeStrategyMap();
auto strategy = msmap.find(MergeStrategy);
if (strategy == msmap.end())
{
CONDWARNING(a_LogWarnings, "Unknown merge strategy (\"%s\") specified for piece %s in file %s. Using msSpongePrint instead.",
MergeStrategy.c_str(), a_PieceName.c_str(), a_FileName.c_str()
);
a_Prefab->SetMergeStrategy(cBlockArea::msSpongePrint);
}
a_Prefab->SetMoveToGround(MoveToGround != 0);
a_Prefab->SetExtendFloor(ShouldExpandFloor != 0);
return true;
}
cPieces cPrefabPiecePool::GetPiecesWithConnector(int a_ConnectorType)
{
return m_PiecesByConnector[a_ConnectorType];

View File

@ -16,6 +16,13 @@
// fwd:
class cLuaState;
class cPrefabPiecePool :
public cPiecePool
{
@ -34,6 +41,10 @@ public:
const cPrefab::sDef * a_StartingPieceDefs, size_t a_NumStartingPieceDefs
);
/** Creates a pool and loads the contents of the specified file into it.
If a_LogWarnings is true, logs a warning to console when loading fails. */
cPrefabPiecePool(const AString & a_FileName, bool a_LogWarnings);
/** Destroys the pool, freeing all pieces. */
~cPrefabPiecePool();
@ -50,6 +61,20 @@ public:
May be called multiple times with different PieceDefs, will add all such pieces. */
void AddStartingPieceDefs(const cPrefab::sDef * a_StartingPieceDefs, size_t a_NumStartingPieceDefs);
/** Loads the pieces from the specified file. Returns true if successful, false on error.
If a_LogWarnings is true, logs a warning to console when loading fails. */
bool LoadFromFile(const AString & a_FileName, bool a_LogWarnings);
/** Loads the pieces from the specified Cubeset file. Returns true if successful, false on error.
If a_LogWarnings is true, logs a warning to console when loading fails. */
bool LoadFromCubesetFile(const AString & a_FileName, bool a_LogWarnings);
/** Returns the number of regular (non-starting) pieces. */
size_t GetAllPiecesCount(void) const { return m_AllPieces.size(); }
/** Returns the number of starting pieces. */
size_t GetStartingPiecesCount(void) const { return m_StartingPieces.size(); }
protected:
/** The type used to map a connector type to the list of pieces with that connector */
@ -70,7 +95,61 @@ protected:
/** Adds the prefab to the m_PiecesByConnector map for all its connectors. */
void AddToPerConnectorMap(cPrefab * a_Prefab);
/** Loads the pieces from the cubeset file parsed into the specified Lua state.
Returns true on success, false on error.
If a_LogWarnings is true, logs a warning to console when loading fails. */
bool LoadFromCubesetFileVer1(const AString & a_FileName, cLuaState & a_LuaState, bool a_LogWarnings);
/** Loads a single piece from the cubeset file parsed into the specified Lua state.
The piece's definition table is expected to be at the top of the Lua stack.
Returns true on success, false on error.
a_PieceIndex is the index of the piece, in the Pieces table. It is used for logging only.
If a_LogWarnings is true, logs a warning to console when loading fails. */
bool LoadCubesetPieceVer1(const AString & a_FileName, cLuaState & a_LuaState, int a_PieceIndex, bool a_LogWarnings);
/** Loads a single piece's prefab from the cubeset file parsed into the specified Lua state.
The piece's definition table is expected to be at the top of the Lua stack.
Returns the prefab on success, nullptr on failure.
a_PieceName is the identification of the piece, used for logging only.
If a_LogWarnings is true, logs a warning to console when loading fails. */
UniquePtr<cPrefab> LoadPrefabFromCubesetVer1(
const AString & a_FileName,
cLuaState & a_LuaState,
const AString & a_PieceName,
bool a_LogWarnings
);
/** Reads a single piece's connectors from the cubeset file parsed into the specified Lua state.
The piece's definition table is expected to be at the top of the Lua stack.
Returns true on success, false on failure.
The connectors are added into the a_Prefab object.
No Connectors table is considered a failure, empty Connectors table is considered a success.
If any of the connectors are malformed, it is considered a failure, although the rest of the connectors will still load.
a_PieceName is the identification of the piece, used for logging only.
If a_LogWarnings is true, logs a warning to console when loading fails. */
bool ReadConnectorsCubesetVer1(
const AString & a_FileName,
cLuaState & a_LuaState,
const AString & a_PieceName,
cPrefab * a_Prefab,
bool a_LogWarnings
);
/** Reads a single piece's metadata from the cubeset file parsed into the specified Lua state.
The piece's definition table is expected to be at the top of the Lua stack.
Returns true on success, false on failure.
The metadata is applied into the a_Prefab object.
a_PieceName is the identification of the piece, used for logging only.
If a_LogWarnings is true, logs a warning to console when loading fails. */
bool ApplyMetadataCubesetVer1(
const AString & a_FileName,
cLuaState & a_LuaState,
const AString & a_PieceName,
cPrefab * a_Prefab,
bool a_LogWarnings
);
// cPiecePool overrides:
virtual cPieces GetPiecesWithConnector(int a_ConnectorType) override;
virtual cPieces GetStartingPieces(void) override;