177 lines
4.6 KiB
C++
177 lines
4.6 KiB
C++
#ifndef DIGGLER_WORLD_HPP
|
|
#define DIGGLER_WORLD_HPP
|
|
|
|
#include <condition_variable>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <queue>
|
|
#include <thread>
|
|
|
|
#include <glm/vec3.hpp>
|
|
|
|
#include "Chunk.hpp"
|
|
#include "io/Stream.hpp"
|
|
#include "platform/Math.hpp"
|
|
#include "Particles.hpp"
|
|
|
|
namespace diggler {
|
|
|
|
class Game;
|
|
namespace net {
|
|
class InMessage;
|
|
class OutMessage;
|
|
}
|
|
|
|
namespace world {
|
|
class Emerger;
|
|
}
|
|
|
|
using WorldId = int;
|
|
struct WorldChunkMapSorter {
|
|
constexpr bool operator()(const glm::ivec3 &lhs, const glm::ivec3 &rhs) const {
|
|
if (lhs.x == rhs.x) {
|
|
if (lhs.y == rhs.y) {
|
|
if (lhs.z == rhs.z) {
|
|
return false; // lhs == rhs isn't lhs < rhs
|
|
} else {
|
|
return (lhs.z < rhs.z);
|
|
}
|
|
} else {
|
|
return (lhs.y < rhs.y);
|
|
}
|
|
}
|
|
return (lhs.x < rhs.x);
|
|
}
|
|
};
|
|
using WorldChunkMap = std::map<glm::ivec3, ChunkWeakRef, WorldChunkMapSorter>;
|
|
|
|
class World final : private WorldChunkMap {
|
|
private:
|
|
friend class Chunk;
|
|
friend class CaveGenerator;
|
|
|
|
Game *G;
|
|
|
|
std::unique_ptr<world::Emerger> emerger;
|
|
|
|
public:
|
|
using WorldChunkMap::size;
|
|
using WorldChunkMap::begin;
|
|
using WorldChunkMap::end;
|
|
|
|
std::vector<ParticleEmitter> emitters;
|
|
|
|
const WorldId id;
|
|
const bool isRemote;
|
|
|
|
World(Game *G, WorldId id, bool remote);
|
|
~World();
|
|
|
|
/* ============ Getters ============ */
|
|
|
|
ChunkRef getNewEmptyChunk(int cx, int cy, int cz);
|
|
|
|
/**
|
|
* @brief Gets a chunk.
|
|
* Gets the chunk at chunk-coordinates @p cx, @p cy, @p cz.
|
|
* Returns an empty reference if the chunk isn't ready and @p load is `false`. In such cases,
|
|
* the chunk is added to the load queue.
|
|
* @param x Chunk coordinate X axis.
|
|
* @param y Chunk coordinate Y axis.
|
|
* @param z Chunk coordinate Z axis.
|
|
* @param load If the chunk must be loaded.
|
|
* @returns `ChunkRef` to the chunk.
|
|
*/
|
|
ChunkRef getChunk(int cx, int cy, int cz, bool load = false);
|
|
inline ChunkRef getChunkAtCoords(int x, int y, int z, bool load = false) {
|
|
return getChunk(divrd(x, Chunk::CX), divrd(y, Chunk::CY), divrd(z, Chunk::CZ), load);
|
|
}
|
|
inline ChunkRef getChunkAtCoords(const glm::ivec3 &v, bool load = false) {
|
|
return getChunk(divrd(v.x, Chunk::CX), divrd(v.y, Chunk::CY), divrd(v.z, Chunk::CZ), load);
|
|
}
|
|
|
|
/**
|
|
* @returns The block ID at specified location.
|
|
*/
|
|
BlockId getBlockId(int x, int y, int z);
|
|
|
|
/**
|
|
* @returns Block's data integer.
|
|
* @note If the block has extdata, 0 is returned.
|
|
*/
|
|
BlockData getBlockData(int x, int y, int z);
|
|
|
|
/**
|
|
* @returns `true` if block has extdata, `false` otherwise.
|
|
*/
|
|
bool blockHasExtdata(int x, int y, int z);
|
|
|
|
/**
|
|
* @brief Gets a block's extdata.
|
|
* Gets a block's extdata store, used to save advanced state values.
|
|
* @returns Block's extdata.
|
|
*/
|
|
// TODO msgpack::object&& getBlockExtdata(int x, int y, int z);
|
|
|
|
/* ============ Setters ============ */
|
|
|
|
/**
|
|
* @brief Sets the block at specified location, replacing its ID and data.
|
|
*/
|
|
bool setBlock(int x, int y, int z, BlockId id, BlockData data = 0);
|
|
|
|
/**
|
|
* @brief Sets the block ID at specified location, keeping its (meta)data.read()
|
|
*/
|
|
bool setBlockId(int x, int y, int z, BlockId id);
|
|
|
|
/**
|
|
* @brief Sets the block data at specified location, keeping its ID.
|
|
*/
|
|
bool setBlockData(int x, int y, int z, BlockData data);
|
|
|
|
// Copies extdata tree
|
|
// TODO void setExtdata(const msgpack::object& &meta);
|
|
|
|
void onRenderPropertiesChanged();
|
|
void refresh();
|
|
|
|
/* ============ Ray tracing ============ */
|
|
|
|
using RayCallback = std::function<bool /*continue*/ (
|
|
glm::ivec3 blockPos,
|
|
BlockId blockId,
|
|
glm::vec3 hitPoint,
|
|
glm::vec3 hitNormal)>;
|
|
|
|
/**
|
|
* @brief Fires a ray through the voxels and determines what block and facing block is hit.
|
|
* @todo Documentation
|
|
*/
|
|
bool raytrace(glm::vec3 pos, glm::vec3 dir, float range, glm::ivec3 *pointed, glm::ivec3 *norm);
|
|
bool raytrace(glm::vec3 from, glm::vec3 to, glm::ivec3 *pointed, glm::ivec3 *facing) {
|
|
return raytrace(from, to-from, glm::length(to-from), pointed, facing);
|
|
}
|
|
|
|
bool raytrace(glm::vec3 pos, glm::vec3 dir, float range, const RayCallback &callback);
|
|
bool raytrace(glm::vec3 from, glm::vec3 to, const RayCallback &callback) {
|
|
return raytrace(from, to-from, glm::length(to-from), callback);
|
|
}
|
|
|
|
/* ============ Serialization ============ */
|
|
|
|
void write(io::OutStream&) const;
|
|
void read(io::InStream&);
|
|
void send(net::OutMessage&) const;
|
|
void recv(net::InMessage&);
|
|
};
|
|
|
|
using WorldRef = std::shared_ptr<World>;
|
|
using WorldWeakRef = std::weak_ptr<World>;
|
|
|
|
}
|
|
|
|
#endif /* DIGGLER_WORLD_HPP */
|