#pragma once #include "util/Vector3int32.h" #include 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(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 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(x) < static_cast(sizeX) && static_cast(y) < static_cast(sizeY) && static_cast(z) < static_cast(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(x) < static_cast(sizeX) && static_cast(y) < static_cast(sizeY) && static_cast(z) < static_cast(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(x) < static_cast(sizeX) && static_cast(y) < static_cast(sizeY) && static_cast(z) < static_cast(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(x) < static_cast(sizeX) && static_cast(y) < static_cast(sizeY) && static_cast(z) < static_cast(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 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 getNonEmptyRegions() const; std::vector 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 chunks; unsigned int chunksVolume; std::vector listeners; }; } }