mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-05 13:17:49 +00:00
fahhh
This commit is contained in:
@@ -0,0 +1,275 @@
|
||||
#pragma once
|
||||
|
||||
#include "voxel2/Grid.h"
|
||||
|
||||
namespace RBX { namespace Voxel2 {
|
||||
|
||||
template <typename BitStream> class BitSerializer
|
||||
{
|
||||
public:
|
||||
void encodeIndex(const Vector3int32& index, BitStream& stream)
|
||||
{
|
||||
encodeChunkIndex(index - lastIndex, stream);
|
||||
|
||||
lastIndex = index;
|
||||
}
|
||||
|
||||
void encodeContent(const Box& box, BitStream& stream)
|
||||
{
|
||||
encodeChunkData(box, stream);
|
||||
}
|
||||
|
||||
void decodeIndex(Vector3int32& index, BitStream& stream)
|
||||
{
|
||||
Vector3int32 diff;
|
||||
decodeChunkIndex(diff, stream);
|
||||
|
||||
index = lastIndex + diff;
|
||||
lastIndex = index;
|
||||
}
|
||||
|
||||
void decodeContent(Box& box, BitStream& stream)
|
||||
{
|
||||
decodeChunkData(box, stream);
|
||||
}
|
||||
|
||||
private:
|
||||
Vector3int32 lastIndex;
|
||||
std::vector<Cell> cells;
|
||||
|
||||
void encodeChunkIndex(const Vector3int32& diff, BitStream& stream)
|
||||
{
|
||||
if (char(diff.x) == diff.x && char(diff.y) == diff.y && char(diff.z) == diff.z)
|
||||
{
|
||||
// Single-byte diffs: tag "1"
|
||||
stream << true;
|
||||
stream << char(diff.x);
|
||||
stream << char(diff.y);
|
||||
stream << char(diff.z);
|
||||
}
|
||||
else if (short(diff.x) == diff.x && short(diff.y) == diff.y && short(diff.z) == diff.z)
|
||||
{
|
||||
// Two-byte diffs: tag "01"
|
||||
stream << false;
|
||||
stream << true;
|
||||
stream << short(diff.x);
|
||||
stream << short(diff.y);
|
||||
stream << short(diff.z);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Four-byte diffs: tag "00"
|
||||
stream << false;
|
||||
stream << false;
|
||||
stream << diff.x;
|
||||
stream << diff.y;
|
||||
stream << diff.z;
|
||||
}
|
||||
}
|
||||
|
||||
void decodeChunkIndex(Vector3int32& diff, BitStream& stream)
|
||||
{
|
||||
bool size1;
|
||||
stream >> size1;
|
||||
|
||||
if (size1)
|
||||
{
|
||||
// Single-byte diffs: tag "1"
|
||||
char x, y, z;
|
||||
stream >> x;
|
||||
stream >> y;
|
||||
stream >> z;
|
||||
|
||||
diff = Vector3int32(x, y, z);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool size2;
|
||||
stream >> size2;
|
||||
|
||||
if (size2)
|
||||
{
|
||||
// Two-byte diffs: tag "01"
|
||||
short x, y, z;
|
||||
stream >> x;
|
||||
stream >> y;
|
||||
stream >> z;
|
||||
|
||||
diff = Vector3int32(x, y, z);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Four-byte diffs: tag "00"
|
||||
int x, y, z;
|
||||
stream >> x;
|
||||
stream >> y;
|
||||
stream >> z;
|
||||
|
||||
diff = Vector3int32(x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void encodeChunkData(const Box& box, BitStream& stream)
|
||||
{
|
||||
bool empty = box.isEmpty();
|
||||
|
||||
stream << empty;
|
||||
|
||||
if (empty)
|
||||
return;
|
||||
|
||||
Vector3int32 size = box.getSize();
|
||||
|
||||
cells.resize(size.x * size.y * size.z);
|
||||
|
||||
unsigned int cellOffset = 0;
|
||||
|
||||
for (int y = 0; y < size.y; ++y)
|
||||
for (int z = 0; z < size.z; ++z)
|
||||
{
|
||||
memcpy(&cells[cellOffset], box.readRow(0, y, z), size.x * sizeof(Cell));
|
||||
cellOffset += size.x;
|
||||
}
|
||||
|
||||
int lastMaterial = 0;
|
||||
|
||||
for (unsigned int offset = 0; offset < cells.size(); )
|
||||
{
|
||||
// identify run length
|
||||
Cell cell = cells[offset];
|
||||
unsigned int count = 0;
|
||||
|
||||
do offset++, count++;
|
||||
while (offset < cells.size() && cells[offset] == cell && count < 512);
|
||||
|
||||
// serialize run length
|
||||
// 00 = single cell
|
||||
// xx = x groups of 3-bit values (max is 3 groups of 3-bit values = 9 bit = 512)
|
||||
unsigned char groups = (count == 1) ? 0 : (count <= 8) ? 1 : (count <= 64) ? 2 : 3;
|
||||
unsigned int temp = count - 1;
|
||||
|
||||
stream.WriteBits(&groups, 2);
|
||||
stream.WriteBits((const unsigned char*)&temp, groups * 3);
|
||||
|
||||
// serialize material/occupancy combo
|
||||
// 0 = air
|
||||
// 10 = full (occupancy is assumed to be max)
|
||||
// 11 = custom (followed by 8 bits with occupancy data)
|
||||
|
||||
// material (only if it's not air)
|
||||
// 0 = last
|
||||
// 1 = new (followed by 6 bits with material data)
|
||||
if (cell.getMaterial() == Cell::Material_Air)
|
||||
stream << false;
|
||||
else
|
||||
{
|
||||
// solid
|
||||
stream << true;
|
||||
|
||||
// customOccupancy
|
||||
if (cell.getOccupancy() == Cell::Occupancy_Max)
|
||||
stream << false;
|
||||
else
|
||||
{
|
||||
stream << true;
|
||||
|
||||
unsigned char occupancy = cell.getOccupancy();
|
||||
stream.WriteBits(&occupancy, Cell::Occupancy_Bits);
|
||||
}
|
||||
|
||||
// newMaterial
|
||||
if (cell.getMaterial() == lastMaterial)
|
||||
stream << false;
|
||||
else
|
||||
{
|
||||
stream << true;
|
||||
|
||||
unsigned char material = cell.getMaterial();
|
||||
stream.WriteBits(&material, Cell::Material_Bits);
|
||||
}
|
||||
|
||||
lastMaterial = cell.getMaterial();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void decodeChunkData(Box& box, BitStream& stream)
|
||||
{
|
||||
RBXASSERT(box.isEmpty());
|
||||
|
||||
bool empty;
|
||||
stream >> empty;
|
||||
|
||||
if (empty)
|
||||
return;
|
||||
|
||||
Vector3int32 size = box.getSize();
|
||||
|
||||
cells.resize(size.x * size.y * size.z);
|
||||
|
||||
int lastMaterial = 0;
|
||||
|
||||
for (unsigned int offset = 0; offset < cells.size(); )
|
||||
{
|
||||
// deserialize run length
|
||||
unsigned char groups = 0;
|
||||
stream.ReadBits(&groups, 2);
|
||||
|
||||
unsigned int temp = 0;
|
||||
stream.ReadBits((unsigned char*)&temp, groups * 3);
|
||||
|
||||
unsigned int count = temp + 1;
|
||||
|
||||
if (offset + count > cells.size())
|
||||
throw RBX::runtime_error("Error while decoding data: chunk overflow at %u cells", offset + count);
|
||||
|
||||
// deserialize material/occupancy
|
||||
unsigned char material = Cell::Material_Air;
|
||||
unsigned char occupancy = 0;
|
||||
|
||||
bool solid;
|
||||
stream >> solid;
|
||||
|
||||
if (solid)
|
||||
{
|
||||
bool customOccupancy;
|
||||
stream >> customOccupancy;
|
||||
|
||||
if (!customOccupancy)
|
||||
occupancy = Cell::Occupancy_Max;
|
||||
else
|
||||
stream.ReadBits(&occupancy, Cell::Occupancy_Bits);
|
||||
|
||||
bool newMaterial;
|
||||
stream >> newMaterial;
|
||||
|
||||
if (!newMaterial)
|
||||
material = lastMaterial;
|
||||
else
|
||||
stream.ReadBits(&material, Cell::Material_Bits);
|
||||
|
||||
lastMaterial = material;
|
||||
}
|
||||
|
||||
// fill cells
|
||||
Cell cell(material, occupancy);
|
||||
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
cells[offset + i] = cell;
|
||||
|
||||
offset += count;
|
||||
}
|
||||
|
||||
unsigned int cellOffset = 0;
|
||||
|
||||
for (int y = 0; y < size.y; ++y)
|
||||
for (int z = 0; z < size.z; ++z)
|
||||
{
|
||||
memcpy(box.writeRow(0, y, z), &cells[cellOffset], size.x * sizeof(Cell));
|
||||
cellOffset += size.x;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} }
|
||||
@@ -0,0 +1,192 @@
|
||||
#pragma once
|
||||
|
||||
#include "v8datamodel/PartInstance.h"
|
||||
|
||||
#include "voxel/Grid.h"
|
||||
#include "voxel2/Grid.h"
|
||||
|
||||
namespace RBX { namespace Voxel2 { namespace Conversion {
|
||||
|
||||
static const int kOccupancySolid = Cell::Occupancy_Max;
|
||||
static const int kOccupancyWedge = Cell::Occupancy_Max / 2;
|
||||
static const int kOccupancyCorner = Cell::Occupancy_Max / 3;
|
||||
static const int kOccupancyInverseCorner = Cell::Occupancy_Max * 2 / 3;
|
||||
|
||||
static const PartMaterial kMaterialTable[] =
|
||||
{
|
||||
AIR_MATERIAL,
|
||||
WATER_MATERIAL,
|
||||
GRASS_MATERIAL,
|
||||
SLATE_MATERIAL,
|
||||
CONCRETE_MATERIAL,
|
||||
BRICK_MATERIAL,
|
||||
SAND_MATERIAL,
|
||||
WOODPLANKS_MATERIAL,
|
||||
ROCK_MATERIAL,
|
||||
GLACIER_MATERIAL,
|
||||
SNOW_MATERIAL,
|
||||
SANDSTONE_MATERIAL,
|
||||
MUD_MATERIAL,
|
||||
BASALT_MATERIAL,
|
||||
GROUND_MATERIAL,
|
||||
CRACKED_LAVA_MATERIAL,
|
||||
};
|
||||
|
||||
static const int kMaterialDefault = 2;
|
||||
|
||||
inline unsigned char getOccupancyFromSolidBlock(Voxel::CellBlock block)
|
||||
{
|
||||
switch (block)
|
||||
{
|
||||
case Voxel::CELL_BLOCK_Solid:
|
||||
return kOccupancySolid;
|
||||
|
||||
case Voxel::CELL_BLOCK_VerticalWedge:
|
||||
case Voxel::CELL_BLOCK_HorizontalWedge:
|
||||
return kOccupancyWedge;
|
||||
|
||||
case Voxel::CELL_BLOCK_CornerWedge:
|
||||
return kOccupancyCorner;
|
||||
|
||||
case Voxel::CELL_BLOCK_InverseCornerWedge:
|
||||
return kOccupancyInverseCorner;
|
||||
|
||||
default:
|
||||
RBXASSERT(false);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline Voxel::CellBlock getCellBlockFromCell(const Cell& cell)
|
||||
{
|
||||
static const int kOccupancyRounder = Cell::Occupancy_Max / 6;
|
||||
|
||||
if (cell.getMaterial() == Cell::Material_Air)
|
||||
return Voxel::CELL_BLOCK_Empty;
|
||||
else if (cell.getOccupancy() < kOccupancyCorner - kOccupancyRounder)
|
||||
return Voxel::CELL_BLOCK_Empty;
|
||||
else if (cell.getOccupancy() < kOccupancyWedge - kOccupancyRounder)
|
||||
return Voxel::CELL_BLOCK_CornerWedge;
|
||||
else if (cell.getOccupancy() < kOccupancyInverseCorner - kOccupancyRounder)
|
||||
return Voxel::CELL_BLOCK_VerticalWedge;
|
||||
else if (cell.getOccupancy() < kOccupancySolid - kOccupancyRounder)
|
||||
return Voxel::CELL_BLOCK_InverseCornerWedge;
|
||||
else
|
||||
return Voxel::CELL_BLOCK_Solid;
|
||||
}
|
||||
|
||||
inline PartMaterial getMaterialFromVoxelMaterial(unsigned char material)
|
||||
{
|
||||
if (static_cast<size_t>(material) < sizeof(kMaterialTable) / sizeof(kMaterialTable[0]))
|
||||
return kMaterialTable[material];
|
||||
else
|
||||
return kMaterialTable[kMaterialDefault];
|
||||
}
|
||||
|
||||
inline unsigned char getVoxelMaterialFromMaterial(PartMaterial material)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(kMaterialTable) / sizeof(kMaterialTable[0]); ++i)
|
||||
if (kMaterialTable[i] == material)
|
||||
return i;
|
||||
|
||||
return kMaterialDefault;
|
||||
}
|
||||
|
||||
inline PartMaterial getMaterialFromCellMaterial(Voxel::CellMaterial material)
|
||||
{
|
||||
switch (material)
|
||||
{
|
||||
case Voxel::CELL_MATERIAL_Deprecated_Empty:
|
||||
return AIR_MATERIAL;
|
||||
case Voxel::CELL_MATERIAL_Grass:
|
||||
return GRASS_MATERIAL;
|
||||
case Voxel::CELL_MATERIAL_Sand:
|
||||
return SAND_MATERIAL;
|
||||
case Voxel::CELL_MATERIAL_Brick:
|
||||
return BRICK_MATERIAL;
|
||||
case Voxel::CELL_MATERIAL_Granite:
|
||||
return SLATE_MATERIAL;
|
||||
case Voxel::CELL_MATERIAL_Asphalt:
|
||||
return CONCRETE_MATERIAL;
|
||||
case Voxel::CELL_MATERIAL_Wood_Plank:
|
||||
case Voxel::CELL_MATERIAL_Wood_Log:
|
||||
return WOODPLANKS_MATERIAL;
|
||||
case Voxel::CELL_MATERIAL_Gravel:
|
||||
return SLATE_MATERIAL;
|
||||
case Voxel::CELL_MATERIAL_Cinder_Block:
|
||||
return CONCRETE_MATERIAL;
|
||||
case Voxel::CELL_MATERIAL_Stone_Block:
|
||||
return SLATE_MATERIAL;
|
||||
case Voxel::CELL_MATERIAL_Cement:
|
||||
return CONCRETE_MATERIAL;
|
||||
case Voxel::CELL_MATERIAL_Water:
|
||||
return WATER_MATERIAL;
|
||||
default:
|
||||
return kMaterialTable[kMaterialDefault];
|
||||
}
|
||||
}
|
||||
|
||||
inline Voxel::CellMaterial getCellMaterialFromMaterial(PartMaterial material)
|
||||
{
|
||||
switch (material)
|
||||
{
|
||||
case AIR_MATERIAL:
|
||||
return Voxel::CELL_MATERIAL_Deprecated_Empty;
|
||||
case WATER_MATERIAL:
|
||||
return Voxel::CELL_MATERIAL_Water;
|
||||
case GRASS_MATERIAL:
|
||||
return Voxel::CELL_MATERIAL_Grass;
|
||||
case SLATE_MATERIAL:
|
||||
return Voxel::CELL_MATERIAL_Stone_Block;
|
||||
case CONCRETE_MATERIAL:
|
||||
return Voxel::CELL_MATERIAL_Cement;
|
||||
case BRICK_MATERIAL:
|
||||
return Voxel::CELL_MATERIAL_Brick;
|
||||
case SAND_MATERIAL:
|
||||
return Voxel::CELL_MATERIAL_Sand;
|
||||
case WOODPLANKS_MATERIAL:
|
||||
return Voxel::CELL_MATERIAL_Wood_Plank;
|
||||
default:
|
||||
return Voxel::CELL_MATERIAL_Grass;
|
||||
}
|
||||
}
|
||||
|
||||
inline void convertToSmooth(const Voxel::Grid& oldGrid, Voxel2::Grid& grid)
|
||||
{
|
||||
std::vector<SpatialRegion::Id> chunks = oldGrid.getNonEmptyChunks();
|
||||
|
||||
for (size_t i = 0; i < chunks.size(); ++i)
|
||||
{
|
||||
Region3int16 extents = SpatialRegion::inclusiveVoxelExtentsOfRegion(chunks[i]);
|
||||
Voxel::Grid::Region region = oldGrid.getRegion(extents.getMinPos(), extents.getMaxPos());
|
||||
|
||||
Voxel2::Box box(Voxel::kXZ_CHUNK_SIZE, Voxel::kY_CHUNK_SIZE, Voxel::kXZ_CHUNK_SIZE);
|
||||
|
||||
for (int y = 0; y < Voxel::kY_CHUNK_SIZE; ++y)
|
||||
for (int z = 0; z < Voxel::kXZ_CHUNK_SIZE; ++z)
|
||||
for (int x = 0; x < Voxel::kXZ_CHUNK_SIZE; ++x)
|
||||
{
|
||||
Vector3int16 cpos = extents.getMinPos() + Vector3int16(x, y, z);
|
||||
const Voxel::Cell& oldCell = region.voxelAt(cpos);
|
||||
const Voxel::CellMaterial& oldMaterial = region.materialAt(cpos);
|
||||
|
||||
if (!oldCell.isEmpty())
|
||||
{
|
||||
using namespace Voxel2::Conversion;
|
||||
|
||||
Voxel2::Cell cell;
|
||||
|
||||
if (oldCell.isExplicitWaterCell())
|
||||
cell = Voxel2::Cell(Voxel2::Cell::Material_Water, Voxel2::Cell::Occupancy_Max);
|
||||
else
|
||||
cell = Voxel2::Cell(getVoxelMaterialFromMaterial(getMaterialFromCellMaterial(oldMaterial)), getOccupancyFromSolidBlock(oldCell.solid.getBlock()));
|
||||
|
||||
box.set(x, y, z, cell);
|
||||
}
|
||||
}
|
||||
|
||||
grid.write(Voxel2::Region(Vector3int32(extents.getMinPos()), Vector3int32(extents.getMaxPos() + Vector3int16(1, 1, 1))), box);
|
||||
}
|
||||
}
|
||||
|
||||
} } }
|
||||
@@ -0,0 +1,205 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/Vector3int32.h"
|
||||
#include <vector>
|
||||
|
||||
namespace RBX { namespace Voxel2 {
|
||||
|
||||
class Cell
|
||||
{
|
||||
public:
|
||||
enum Material
|
||||
{
|
||||
Material_Air = 0,
|
||||
Material_Water = 1,
|
||||
|
||||
Material_Bits = 6,
|
||||
Material_Max = (1 << Material_Bits) - 1
|
||||
};
|
||||
|
||||
enum Occupancy
|
||||
{
|
||||
Occupancy_Bits = 8,
|
||||
Occupancy_Max = (1 << Occupancy_Bits) - 1
|
||||
};
|
||||
|
||||
Cell()
|
||||
{
|
||||
// we rely on Air being 0 since we use memset elsewhere
|
||||
BOOST_STATIC_ASSERT(Material_Air == 0);
|
||||
|
||||
this->material = 0;
|
||||
this->occupancy = 0;
|
||||
}
|
||||
|
||||
Cell(unsigned char material, unsigned char occupancy)
|
||||
{
|
||||
RBXASSERT_VERY_FAST(material <= Material_Max && occupancy <= Occupancy_Max);
|
||||
|
||||
// make sure occupancy is always 0 for Air material (0)
|
||||
this->material = material;
|
||||
this->occupancy = occupancy & (-static_cast<int>(material) >> 31);
|
||||
}
|
||||
|
||||
unsigned char getMaterial() const { return material; }
|
||||
unsigned char getOccupancy() const { return occupancy; }
|
||||
|
||||
bool operator==(const Cell& other) const { return material == other.material && occupancy == other.occupancy; }
|
||||
bool operator!=(const Cell& other) const { return !(*this == other); }
|
||||
|
||||
private:
|
||||
unsigned char material;
|
||||
unsigned char occupancy;
|
||||
};
|
||||
|
||||
class Region
|
||||
{
|
||||
public:
|
||||
Region(const Vector3int32& begin, const Vector3int32& end)
|
||||
: begin_(begin)
|
||||
, end_(end)
|
||||
{
|
||||
RBXASSERT_VERY_FAST(begin.x <= end.x && begin.y <= end.y && begin.z <= end.z);
|
||||
}
|
||||
|
||||
Region(const Vector3int32& begin, unsigned int size)
|
||||
: begin_(begin)
|
||||
, end_(begin + Vector3int32(size, size, size))
|
||||
{
|
||||
}
|
||||
|
||||
static Region fromChunk(const Vector3int32& id, unsigned int chunkSizeLog2)
|
||||
{
|
||||
return Region(id << chunkSizeLog2, 1 << chunkSizeLog2);
|
||||
}
|
||||
|
||||
static Region fromExtents(const Vector3& min, const Vector3& max);
|
||||
|
||||
const Vector3int32& begin() const { return begin_; }
|
||||
const Vector3int32& end() const { return end_; }
|
||||
|
||||
Vector3int32 size() const { return end_ - begin_; }
|
||||
|
||||
bool empty() const { return begin_.x == end_.x || begin_.y == end_.y || begin_.z == end_.z; }
|
||||
|
||||
bool operator==(const Region& other) const { return begin_ == other.begin_ && end_ == other.end_; }
|
||||
bool operator!=(const Region& other) const { return begin_ != other.begin_ || end_ != other.end_; }
|
||||
|
||||
bool aligned(unsigned int size) const;
|
||||
bool inside(const Region& other) const;
|
||||
|
||||
Region intersect(const Region& other) const;
|
||||
Region expand(unsigned int size) const;
|
||||
Region expandToGrid(unsigned int size) const;
|
||||
Region offset(const Vector3int32& offset) const;
|
||||
Region downsample(unsigned int lod) const;
|
||||
|
||||
std::vector<Vector3int32> getChunkIds(unsigned int chunkSizeLog2) const;
|
||||
unsigned long long getChunkCount(unsigned int chunkSizeLog2) const;
|
||||
|
||||
private:
|
||||
Vector3int32 begin_;
|
||||
Vector3int32 end_;
|
||||
};
|
||||
|
||||
class Box
|
||||
{
|
||||
public:
|
||||
Box();
|
||||
Box(int sizeX, int sizeY, int sizeZ);
|
||||
|
||||
const Cell& get(int x, int y, int z) const
|
||||
{
|
||||
RBXASSERT_VERY_FAST(static_cast<unsigned>(x) < static_cast<unsigned>(sizeX) && static_cast<unsigned>(y) < static_cast<unsigned>(sizeY) && static_cast<unsigned>(z) < static_cast<unsigned>(sizeZ));
|
||||
return data.get() ? data[x + sizeX * z + sliceXZ * y] : emptyCell;
|
||||
}
|
||||
|
||||
void set(int x, int y, int z, const Cell& cell)
|
||||
{
|
||||
RBXASSERT_VERY_FAST(static_cast<unsigned>(x) < static_cast<unsigned>(sizeX) && static_cast<unsigned>(y) < static_cast<unsigned>(sizeY) && static_cast<unsigned>(z) < static_cast<unsigned>(sizeZ));
|
||||
if (!data) allocate();
|
||||
data[x + sizeX * z + sliceXZ * y] = cell;
|
||||
}
|
||||
|
||||
const Cell* readRow(int x, int y, int z) const
|
||||
{
|
||||
RBXASSERT_VERY_FAST(static_cast<unsigned>(x) < static_cast<unsigned>(sizeX) && static_cast<unsigned>(y) < static_cast<unsigned>(sizeY) && static_cast<unsigned>(z) < static_cast<unsigned>(sizeZ));
|
||||
RBXASSERT_VERY_FAST(data.get());
|
||||
return &data[x + sizeX * z + sliceXZ * y];
|
||||
}
|
||||
|
||||
Cell* writeRow(int x, int y, int z)
|
||||
{
|
||||
RBXASSERT_VERY_FAST(static_cast<unsigned>(x) < static_cast<unsigned>(sizeX) && static_cast<unsigned>(y) < static_cast<unsigned>(sizeY) && static_cast<unsigned>(z) < static_cast<unsigned>(sizeZ));
|
||||
if (!data) allocate();
|
||||
return &data[x + sizeX * z + sliceXZ * y];
|
||||
}
|
||||
|
||||
int getSizeX() const { return sizeX; }
|
||||
int getSizeY() const { return sizeY; }
|
||||
int getSizeZ() const { return sizeZ; }
|
||||
|
||||
Vector3int32 getSize() const { return Vector3int32(sizeX, sizeY, sizeZ); }
|
||||
|
||||
bool isEmpty() const { return !data; }
|
||||
|
||||
Box clone() const;
|
||||
|
||||
private:
|
||||
int sizeX;
|
||||
int sizeY;
|
||||
int sizeZ;
|
||||
int sliceXZ;
|
||||
|
||||
boost::shared_ptr<Cell[]> data;
|
||||
|
||||
static const Cell emptyCell;
|
||||
|
||||
void allocate();
|
||||
};
|
||||
|
||||
class GridListener;
|
||||
|
||||
class Grid
|
||||
{
|
||||
public:
|
||||
Grid();
|
||||
|
||||
void connectListener(GridListener* listener);
|
||||
void disconnectListener(GridListener* listener);
|
||||
|
||||
Box read(const Region& region, int lod = 0) const;
|
||||
void write(const Region& region, const Box& box);
|
||||
|
||||
Cell getCell(int x, int y, int z) const;
|
||||
|
||||
std::vector<Region> getNonEmptyRegions() const;
|
||||
std::vector<Region> getNonEmptyRegionsInside(const Region& region) const;
|
||||
|
||||
unsigned int getNonEmptyCellCountApprox() const;
|
||||
|
||||
bool isAllocated() const { return !chunks.empty(); }
|
||||
|
||||
void serialize(std::string& result) const;
|
||||
void deserialize(const std::string& result);
|
||||
|
||||
private:
|
||||
enum { kChunkMips = 4 };
|
||||
|
||||
struct Chunk
|
||||
{
|
||||
Box data[kChunkMips];
|
||||
unsigned int volume;
|
||||
|
||||
Chunk();
|
||||
|
||||
bool isEmpty() const;
|
||||
};
|
||||
|
||||
boost::unordered_map<Vector3int32, Chunk> chunks;
|
||||
unsigned int chunksVolume;
|
||||
|
||||
std::vector<GridListener*> listeners;
|
||||
};
|
||||
|
||||
} }
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
namespace RBX { namespace Voxel2 {
|
||||
|
||||
class Region;
|
||||
|
||||
class GridListener
|
||||
{
|
||||
public:
|
||||
virtual ~GridListener() {}
|
||||
|
||||
virtual void onTerrainRegionChanged(const Region& region) = 0;
|
||||
};
|
||||
|
||||
} }
|
||||
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
namespace RBX { namespace Voxel2 {
|
||||
|
||||
class MaterialTable
|
||||
{
|
||||
public:
|
||||
enum Type
|
||||
{
|
||||
Type_Soft,
|
||||
Type_Hard,
|
||||
Type_HardSoft,
|
||||
};
|
||||
|
||||
enum Deformation
|
||||
{
|
||||
Deformation_None,
|
||||
Deformation_Shift,
|
||||
Deformation_Cubify,
|
||||
Deformation_Quantize,
|
||||
Deformation_Barrel,
|
||||
Deformation_Water,
|
||||
};
|
||||
|
||||
enum Mapping
|
||||
{
|
||||
Mapping_Default,
|
||||
Mapping_Cube,
|
||||
};
|
||||
|
||||
struct Material
|
||||
{
|
||||
std::string name;
|
||||
|
||||
int topLayer;
|
||||
int sideLayer;
|
||||
int bottomLayer;
|
||||
|
||||
Type type;
|
||||
Mapping mapping;
|
||||
|
||||
Deformation deformation;
|
||||
float parameter;
|
||||
};
|
||||
|
||||
struct Layer
|
||||
{
|
||||
float tiling;
|
||||
float detiling;
|
||||
};
|
||||
|
||||
struct Atlas
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
int tileSize;
|
||||
int tileCount;
|
||||
int borderSize;
|
||||
};
|
||||
|
||||
MaterialTable(const std::string& file, unsigned int materialCount);
|
||||
~MaterialTable();
|
||||
|
||||
const Material& getMaterial(unsigned int index) const { return materials[index]; }
|
||||
unsigned int getMaterialCount() const { return materials.size(); }
|
||||
|
||||
const Layer& getLayer(unsigned int index) const { return layers[index]; }
|
||||
unsigned int getLayerCount() const { return layers.size(); }
|
||||
|
||||
const Atlas& getAtlas() const { return atlas; }
|
||||
|
||||
private:
|
||||
Atlas atlas;
|
||||
std::vector<Material> materials;
|
||||
std::vector<Layer> layers;
|
||||
|
||||
void load(const std::string& file);
|
||||
};
|
||||
|
||||
} }
|
||||
@@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
|
||||
#include "Util/G3DCore.h"
|
||||
#include "Util/Vector3int32.h"
|
||||
|
||||
#include "voxel2/Grid.h"
|
||||
|
||||
namespace RBX { namespace Voxel2 {
|
||||
|
||||
class MaterialTable;
|
||||
|
||||
namespace Mesher
|
||||
{
|
||||
struct Vertex
|
||||
{
|
||||
Vector3 position;
|
||||
|
||||
unsigned int border: 1;
|
||||
unsigned int reserved: 7;
|
||||
unsigned int material: 8;
|
||||
unsigned int seed: 16;
|
||||
};
|
||||
|
||||
struct GraphicsVertex
|
||||
{
|
||||
Vector3 position;
|
||||
Color4uint8 normal; // xyz = normal, w = vertex index (0-2)
|
||||
Color4uint8 material[3]; // x = layer index, y = normal segment (0-17), zw = random seed
|
||||
};
|
||||
|
||||
struct GraphicsVertexPacked
|
||||
{
|
||||
Vector3int16 position;
|
||||
short id; // vertex index (0-2)
|
||||
Color4uint8 normal; // xyz = normal, w = random seed 0
|
||||
Color4uint8 material0; // xyz = layer index (0-?), w = random seed 1
|
||||
Color4uint8 material1; // xyz = normal segment (0-17), w = random seed 2
|
||||
};
|
||||
|
||||
struct Options
|
||||
{
|
||||
const MaterialTable* materials;
|
||||
bool generateWater;
|
||||
};
|
||||
|
||||
struct BasicMesh
|
||||
{
|
||||
std::vector<Vertex> vertices;
|
||||
std::vector<unsigned int> indices;
|
||||
|
||||
static bool isWater(const Vertex& v0, const Vertex& v1, const Vertex& v2)
|
||||
{
|
||||
return (v0.material == Cell::Material_Water || v1.material == Cell::Material_Water || v2.material == Cell::Material_Water);
|
||||
}
|
||||
};
|
||||
|
||||
struct GraphicsMesh
|
||||
{
|
||||
std::vector<GraphicsVertex> vertices;
|
||||
std::vector<unsigned int> solidIndices;
|
||||
std::vector<unsigned int> waterIndices;
|
||||
};
|
||||
|
||||
struct GraphicsMeshPacked
|
||||
{
|
||||
std::vector<GraphicsVertexPacked> vertices;
|
||||
std::vector<unsigned int> solidIndices;
|
||||
std::vector<unsigned int> waterIndices;
|
||||
};
|
||||
|
||||
void prepareTables();
|
||||
|
||||
BasicMesh generateGeometry(const Box& box, const Vector3int32& offset, int lod, const Options& options);
|
||||
GraphicsMesh generateGraphicsGeometry(const BasicMesh& mesh, const Options& options);
|
||||
|
||||
GraphicsMeshPacked generateGraphicsGeometryPacked(const BasicMesh& mesh, const Vector4& packInfo, const Options& options);
|
||||
|
||||
struct TriangleAdjacency
|
||||
{
|
||||
enum
|
||||
{
|
||||
None = -1,
|
||||
Multiple = -2
|
||||
};
|
||||
|
||||
int neighbor[3];
|
||||
};
|
||||
|
||||
void generateAdjacency(std::vector<TriangleAdjacency>& result, const BasicMesh& mesh);
|
||||
void generateEdgeFlags(std::vector<unsigned char>& result, const BasicMesh& mesh, float cutoff);
|
||||
|
||||
typedef const Vector3 TextureBasis[18];
|
||||
|
||||
const TextureBasis& getTextureBasisU();
|
||||
const TextureBasis& getTextureBasisV();
|
||||
};
|
||||
|
||||
} }
|
||||
Reference in New Issue
Block a user