2014-10-12 10:06:07 +03:00
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
// Copyright 2014 Perttu Ahola <celeron55@gmail.com>
|
|
|
|
#include "voxelworld/api.h"
|
|
|
|
#include "network/api.h"
|
|
|
|
#include "client_file/api.h"
|
2014-10-17 11:49:47 +03:00
|
|
|
#include "replicate/api.h"
|
2014-10-12 10:06:07 +03:00
|
|
|
#include "core/log.h"
|
|
|
|
#include "interface/module.h"
|
|
|
|
#include "interface/server.h"
|
|
|
|
#include "interface/event.h"
|
|
|
|
#include "interface/mesh.h"
|
|
|
|
#include "interface/voxel.h"
|
|
|
|
#include "interface/block.h"
|
2014-10-12 14:49:11 +03:00
|
|
|
#include "interface/voxel_volume.h"
|
2014-10-20 14:05:58 +03:00
|
|
|
#include "interface/polyvox_numeric.h"
|
|
|
|
#include "interface/polyvox_cereal.h"
|
|
|
|
#include "interface/polyvox_std.h"
|
2014-10-23 19:29:40 +03:00
|
|
|
#include "interface/os.h"
|
2014-10-12 14:49:11 +03:00
|
|
|
#include <PolyVoxCore/RawVolume.h>
|
2014-10-12 10:11:36 +03:00
|
|
|
#include <cereal/archives/portable_binary.hpp>
|
2014-10-19 19:59:04 +03:00
|
|
|
#include <cereal/types/string.hpp>
|
2014-10-12 10:06:07 +03:00
|
|
|
#include <Node.h>
|
|
|
|
#include <Scene.h>
|
2014-10-12 11:26:33 +03:00
|
|
|
#include <Model.h>
|
|
|
|
#include <RigidBody.h>
|
|
|
|
#include <CollisionShape.h>
|
|
|
|
#include <Context.h>
|
|
|
|
#include <ResourceCache.h>
|
2014-10-12 13:28:24 +03:00
|
|
|
#include <Light.h>
|
2014-10-12 16:54:04 +03:00
|
|
|
#include <Geometry.h>
|
2014-10-21 12:57:57 +03:00
|
|
|
#include <Zone.h>
|
2014-10-12 10:06:07 +03:00
|
|
|
#include <deque>
|
2014-10-12 21:17:49 +03:00
|
|
|
#include <algorithm>
|
2014-10-12 10:06:07 +03:00
|
|
|
|
|
|
|
using interface::Event;
|
|
|
|
namespace magic = Urho3D;
|
|
|
|
namespace pv = PolyVox;
|
|
|
|
using namespace Urho3D;
|
2014-10-12 14:49:11 +03:00
|
|
|
using interface::VoxelInstance;
|
2014-10-20 14:05:58 +03:00
|
|
|
using interface::container_coord;
|
|
|
|
using interface::container_coord16;
|
2014-10-12 14:49:11 +03:00
|
|
|
|
2014-10-12 10:06:07 +03:00
|
|
|
namespace voxelworld {
|
|
|
|
|
2014-10-15 14:44:25 +03:00
|
|
|
struct ChunkBuffer
|
2014-10-12 16:54:04 +03:00
|
|
|
{
|
2014-10-23 19:29:40 +03:00
|
|
|
static constexpr const char *MODULE = "ChunkBuffer";
|
|
|
|
pv::Vector3DInt32 chunk_p; // For logging
|
|
|
|
up_<pv::RawVolume<VoxelInstance>> volume;
|
2014-10-15 14:44:25 +03:00
|
|
|
bool dirty = false; // If false, buffer has only been read from so far
|
2014-10-23 19:29:40 +03:00
|
|
|
int64_t last_accessed_us = 0;
|
|
|
|
|
|
|
|
ChunkBuffer(){}
|
|
|
|
void timer_reset(const pv::Vector3DInt32 &chunk_p_){
|
|
|
|
chunk_p = chunk_p_;
|
|
|
|
last_accessed_us = interface::os::get_timeofday_us();
|
|
|
|
}
|
|
|
|
bool unload_if_old(int64_t timeout_us){ // True if not loaded
|
|
|
|
if(!volume)
|
|
|
|
return true;
|
|
|
|
if(interface::os::get_timeofday_us() < last_accessed_us + timeout_us)
|
|
|
|
return false;
|
2014-10-23 23:52:07 +03:00
|
|
|
log_t(MODULE, "Unloading chunk " PV3I_FORMAT, PV3I_PARAMS(chunk_p));
|
2014-10-23 19:29:40 +03:00
|
|
|
volume.reset();
|
2014-10-24 10:41:00 +03:00
|
|
|
dirty = false;
|
2014-10-23 19:29:40 +03:00
|
|
|
return true;
|
|
|
|
}
|
2014-10-12 16:54:04 +03:00
|
|
|
};
|
|
|
|
|
2014-10-12 10:06:07 +03:00
|
|
|
struct Section
|
|
|
|
{
|
2014-10-23 19:29:40 +03:00
|
|
|
static constexpr const char *MODULE = "Section";
|
2014-10-17 13:49:55 +03:00
|
|
|
pv::Vector3DInt16 section_p; // Position in sections
|
2014-10-12 10:06:07 +03:00
|
|
|
pv::Vector3DInt16 chunk_size;
|
2014-10-17 13:49:55 +03:00
|
|
|
pv::Region contained_chunks; // Position and size in chunks
|
2014-10-12 10:06:07 +03:00
|
|
|
// Static voxel nodes (each contains one chunk); Initialized to 0.
|
2014-10-15 15:15:49 +03:00
|
|
|
sp_<pv::RawVolume<uint32_t>> node_ids;
|
2014-10-12 21:17:49 +03:00
|
|
|
size_t num_chunks = 0;
|
2014-10-15 14:44:25 +03:00
|
|
|
// Cache these for speed
|
|
|
|
int w_chunks = 0;
|
|
|
|
int h_chunks = 0;
|
|
|
|
int d_chunks = 0;
|
2014-10-12 10:06:07 +03:00
|
|
|
|
2014-10-15 14:44:25 +03:00
|
|
|
// Chunk buffers (index using get_chunk_i())
|
|
|
|
sv_<ChunkBuffer> chunk_buffers;
|
2014-10-12 16:54:04 +03:00
|
|
|
|
2014-10-12 13:28:24 +03:00
|
|
|
// TODO: Specify what exactly do these mean and how they are used
|
|
|
|
bool loaded = false;
|
2014-10-12 10:06:07 +03:00
|
|
|
bool save_enabled = false;
|
|
|
|
bool generated = false;
|
|
|
|
|
2014-10-12 13:28:24 +03:00
|
|
|
Section(): // Needed for containers
|
2014-10-12 16:54:04 +03:00
|
|
|
chunk_size(0, 0, 0) // This is used to detect uninitialized instance
|
2014-10-12 13:28:24 +03:00
|
|
|
{}
|
|
|
|
Section(pv::Vector3DInt16 section_p,
|
|
|
|
pv::Vector3DInt16 chunk_size,
|
|
|
|
pv::Region contained_chunks):
|
|
|
|
section_p(section_p),
|
2014-10-12 10:06:07 +03:00
|
|
|
chunk_size(chunk_size),
|
|
|
|
contained_chunks(contained_chunks),
|
2014-10-15 15:15:49 +03:00
|
|
|
node_ids(new pv::RawVolume<uint32_t>(contained_chunks)),
|
2014-10-12 21:17:49 +03:00
|
|
|
num_chunks(contained_chunks.getWidthInVoxels() *
|
2014-10-17 13:49:55 +03:00
|
|
|
contained_chunks.getHeightInVoxels() *
|
|
|
|
contained_chunks.getDepthInVoxels())
|
2014-10-12 21:17:49 +03:00
|
|
|
{
|
2014-10-15 14:44:25 +03:00
|
|
|
chunk_buffers.resize(num_chunks);
|
|
|
|
// Cache these for speed
|
|
|
|
w_chunks = contained_chunks.getWidthInVoxels();
|
|
|
|
h_chunks = contained_chunks.getHeightInVoxels();
|
|
|
|
d_chunks = contained_chunks.getDepthInVoxels();
|
2014-10-12 16:54:04 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t get_chunk_i(const pv::Vector3DInt32 &chunk_p); // global chunk_p
|
|
|
|
pv::Vector3DInt32 get_chunk_p(size_t chunk_p);
|
2014-10-15 14:44:25 +03:00
|
|
|
|
|
|
|
ChunkBuffer& get_buffer(const pv::Vector3DInt32 &chunk_p,
|
2014-10-23 19:29:40 +03:00
|
|
|
interface::Server *server, size_t *total_buffers_loaded);
|
2014-10-12 10:06:07 +03:00
|
|
|
};
|
|
|
|
|
2014-10-12 16:54:04 +03:00
|
|
|
size_t Section::get_chunk_i(const pv::Vector3DInt32 &chunk_p) // global chunk_p
|
|
|
|
{
|
2014-10-12 21:17:49 +03:00
|
|
|
auto &lc = contained_chunks.getLowerCorner();
|
2014-10-15 14:44:25 +03:00
|
|
|
// NOTE: pv::Vector3DInt32 operators and getters are too slow
|
|
|
|
int local_x = chunk_p.getX() - lc.getX();
|
|
|
|
int local_y = chunk_p.getY() - lc.getY();
|
|
|
|
int local_z = chunk_p.getZ() - lc.getZ();
|
|
|
|
const int &w = w_chunks;
|
|
|
|
const int &h = h_chunks;
|
|
|
|
size_t i = local_z * h * w + local_y * w + local_x;
|
|
|
|
if(i >= num_chunks) // NOTE: This is not accurate but it is safe and fast
|
2014-10-12 21:17:49 +03:00
|
|
|
throw Exception(ss_()+"get_chunk_i: Section "+cs(section_p)+
|
2014-10-17 13:49:55 +03:00
|
|
|
" does not contain chunk"+cs(chunk_p));
|
2014-10-12 21:17:49 +03:00
|
|
|
return i;
|
2014-10-12 16:54:04 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
pv::Vector3DInt32 Section::get_chunk_p(size_t chunk_i)
|
|
|
|
{
|
2014-10-15 14:44:25 +03:00
|
|
|
const int &w = w_chunks;
|
|
|
|
const int &h = h_chunks;
|
2014-10-12 16:54:04 +03:00
|
|
|
pv::Vector3DInt32 p;
|
|
|
|
p.setZ(chunk_i / h / w);
|
|
|
|
p.setY(chunk_i / w - p.getZ() * h);
|
|
|
|
p.setX(chunk_i - p.getZ() * h * w - p.getY() * w);
|
|
|
|
return contained_chunks.getLowerCorner() + p;
|
|
|
|
}
|
|
|
|
|
2014-10-15 14:44:25 +03:00
|
|
|
ChunkBuffer& Section::get_buffer(const pv::Vector3DInt32 &chunk_p,
|
2014-10-23 19:29:40 +03:00
|
|
|
interface::Server *server, size_t *total_buffers_loaded)
|
2014-10-15 14:44:25 +03:00
|
|
|
{
|
|
|
|
size_t chunk_i = get_chunk_i(chunk_p);
|
|
|
|
ChunkBuffer &buf = chunk_buffers[chunk_i];
|
2014-10-23 19:29:40 +03:00
|
|
|
buf.timer_reset(chunk_p);
|
2014-10-15 14:44:25 +03:00
|
|
|
// If loaded, return right away
|
|
|
|
if(buf.volume)
|
|
|
|
return buf;
|
|
|
|
// Not loaded.
|
|
|
|
// Get the static voxel node from the scene and read the volume from it
|
|
|
|
int32_t node_id = node_ids->getVoxelAt(chunk_p);
|
|
|
|
if(node_id == 0){
|
|
|
|
log_w("voxelworld", "Section::get_buffer(): No node found for chunk "
|
|
|
|
PV3I_FORMAT " in section " PV3I_FORMAT,
|
|
|
|
PV3I_PARAMS(chunk_p), PV3I_PARAMS(section_p));
|
|
|
|
return buf;
|
|
|
|
}
|
2014-10-23 23:52:07 +03:00
|
|
|
log_t(MODULE, "Loading chunk " PV3I_FORMAT " (node %i)",
|
|
|
|
PV3I_PARAMS(chunk_p), node_id);
|
2014-10-15 14:44:25 +03:00
|
|
|
server->access_scene([&](Scene *scene)
|
|
|
|
{
|
|
|
|
Node *n = scene->GetNode(node_id);
|
|
|
|
if(!n){
|
2014-10-17 13:49:55 +03:00
|
|
|
log_w("voxelworld",
|
|
|
|
"Section::get_buffer(): Node %i not found in scene "
|
2014-10-15 14:44:25 +03:00
|
|
|
"for chunk " PV3I_FORMAT " in section " PV3I_FORMAT,
|
|
|
|
node_id, PV3I_PARAMS(chunk_p), PV3I_PARAMS(section_p));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const Variant &var = n->GetVar(StringHash("buildat_voxel_data"));
|
|
|
|
const PODVector<unsigned char> &rawbuf = var.GetBuffer();
|
|
|
|
ss_ data((const char*)&rawbuf[0], rawbuf.Size());
|
2014-10-23 19:29:40 +03:00
|
|
|
buf.volume = interface::deserialize_volume(data);
|
2014-10-15 14:44:25 +03:00
|
|
|
if(!buf.volume){
|
2014-10-17 13:49:55 +03:00
|
|
|
log_w("voxelworld",
|
|
|
|
"Section::get_buffer(): Voxel volume could not be "
|
2014-10-15 14:44:25 +03:00
|
|
|
"loaded from node %i for chunk "
|
|
|
|
PV3I_FORMAT " in section " PV3I_FORMAT,
|
|
|
|
node_id, PV3I_PARAMS(chunk_p), PV3I_PARAMS(section_p));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
});
|
2014-10-23 19:29:40 +03:00
|
|
|
(*total_buffers_loaded)++;
|
2014-10-15 14:44:25 +03:00
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2014-10-15 15:15:49 +03:00
|
|
|
struct QueuedNodePhysicsUpdate
|
|
|
|
{
|
|
|
|
uint node_id = 0;
|
2014-10-23 19:29:40 +03:00
|
|
|
// TODO bool is_static_chunk = false;
|
|
|
|
// TODO pv::Vector3DInt32 chunk_p; // Only set if is_static_chunk == true
|
2014-10-15 15:15:49 +03:00
|
|
|
|
2014-10-23 19:29:40 +03:00
|
|
|
QueuedNodePhysicsUpdate(const uint &node_id):
|
|
|
|
node_id(node_id){}
|
2014-10-15 15:15:49 +03:00
|
|
|
bool operator>(const QueuedNodePhysicsUpdate &other) const {
|
|
|
|
return node_id > other.node_id;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-10-12 10:06:07 +03:00
|
|
|
struct Module: public interface::Module, public voxelworld::Interface
|
|
|
|
{
|
|
|
|
interface::Server *m_server;
|
|
|
|
|
|
|
|
// Accessing any of these outside of Server::access_scene is disallowed
|
2014-10-19 14:40:43 +03:00
|
|
|
sp_<interface::AtlasRegistry> m_atlas_reg;
|
2014-10-12 10:06:07 +03:00
|
|
|
sp_<interface::VoxelRegistry> m_voxel_reg;
|
|
|
|
sp_<interface::BlockRegistry> m_block_reg;
|
|
|
|
|
2014-10-20 11:12:30 +03:00
|
|
|
sv_<up_<CommitHook>> m_commit_hooks;
|
|
|
|
|
2014-10-13 18:47:41 +03:00
|
|
|
// One node holds one chunk of voxels (eg. 24x24x24)
|
2014-10-14 12:23:51 +03:00
|
|
|
pv::Vector3DInt16 m_chunk_size_voxels = pv::Vector3DInt16(32, 32, 32);
|
|
|
|
//pv::Vector3DInt16 m_chunk_size_voxels = pv::Vector3DInt16(24, 24, 24);
|
|
|
|
//pv::Vector3DInt16 m_chunk_size_voxels = pv::Vector3DInt16(16, 16, 16);
|
|
|
|
//pv::Vector3DInt16 m_chunk_size_voxels = pv::Vector3DInt16(8, 8, 8);
|
|
|
|
|
2014-10-12 23:32:54 +03:00
|
|
|
// The world is loaded and unloaded by sections (eg. 2x2x2)
|
|
|
|
pv::Vector3DInt16 m_section_size_chunks = pv::Vector3DInt16(2, 2, 2);
|
2014-10-12 10:06:07 +03:00
|
|
|
|
2014-10-23 19:29:40 +03:00
|
|
|
int64_t m_buffer_unload_timeout = 5000000;
|
|
|
|
size_t m_max_buffers_loaded = 50;
|
|
|
|
|
2014-10-12 13:28:24 +03:00
|
|
|
// Sections (this(y,z)=sector, sector(x)=section)
|
2014-10-12 10:11:36 +03:00
|
|
|
sm_<pv::Vector<2, int16_t>, sm_<int16_t, Section>> m_sections;
|
2014-10-12 10:06:07 +03:00
|
|
|
// Cache of last used sections (add to end, remove from beginning)
|
2014-10-12 21:17:49 +03:00
|
|
|
std::deque<Section*> m_last_used_sections;
|
2014-10-12 10:06:07 +03:00
|
|
|
|
2014-10-15 14:44:25 +03:00
|
|
|
// Set of sections that have buffers allocated
|
2014-10-12 21:17:49 +03:00
|
|
|
// (as a sorted array in descending order)
|
2014-10-15 14:44:25 +03:00
|
|
|
std::vector<Section*> m_sections_with_loaded_buffers;
|
2014-10-23 19:29:40 +03:00
|
|
|
size_t m_total_buffers_loaded = 0;
|
2014-10-24 10:41:00 +03:00
|
|
|
size_t m_total_buffers_dirty = 0;
|
2014-10-12 16:54:04 +03:00
|
|
|
|
2014-10-15 15:15:49 +03:00
|
|
|
// Set of nodes by node_id that need set_voxel_physics_boxes()
|
|
|
|
// (as a sorted array in descending node_id order)
|
|
|
|
std::vector<QueuedNodePhysicsUpdate> m_nodes_needing_physics_update;
|
|
|
|
|
2014-10-12 10:06:07 +03:00
|
|
|
Module(interface::Server *server):
|
|
|
|
interface::Module("voxelworld"),
|
|
|
|
m_server(server)
|
|
|
|
{
|
2014-10-13 19:48:00 +03:00
|
|
|
m_voxel_reg.reset(interface::createVoxelRegistry());
|
|
|
|
m_block_reg.reset(interface::createBlockRegistry(m_voxel_reg.get()));
|
2014-10-12 10:06:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
~Module()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void init()
|
|
|
|
{
|
|
|
|
m_server->sub_event(this, Event::t("core:start"));
|
|
|
|
m_server->sub_event(this, Event::t("core:unload"));
|
|
|
|
m_server->sub_event(this, Event::t("core:continue"));
|
|
|
|
m_server->sub_event(this, Event::t("network:client_connected"));
|
|
|
|
m_server->sub_event(this, Event::t("core:tick"));
|
|
|
|
m_server->sub_event(this, Event::t("client_file:files_transmitted"));
|
2014-10-12 10:11:36 +03:00
|
|
|
m_server->sub_event(this, Event::t(
|
2014-10-12 11:26:33 +03:00
|
|
|
"network:packet_received/voxelworld:get_section"));
|
2014-10-12 10:06:07 +03:00
|
|
|
|
|
|
|
m_server->access_scene([&](Scene *scene)
|
|
|
|
{
|
|
|
|
Context *context = scene->GetContext();
|
2014-10-13 19:48:00 +03:00
|
|
|
|
2014-10-19 14:40:43 +03:00
|
|
|
m_atlas_reg.reset(interface::createAtlasRegistry(context));
|
2014-10-13 20:06:03 +03:00
|
|
|
});
|
2014-10-12 10:06:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void event(const Event::Type &type, const Event::Private *p)
|
|
|
|
{
|
|
|
|
EVENT_VOIDN("core:start", on_start)
|
|
|
|
EVENT_VOIDN("core:unload", on_unload)
|
|
|
|
EVENT_VOIDN("core:continue", on_continue)
|
|
|
|
EVENT_TYPEN("network:client_connected", on_client_connected,
|
|
|
|
network::NewClient)
|
|
|
|
EVENT_TYPEN("core:tick", on_tick, interface::TickEvent)
|
|
|
|
EVENT_TYPEN("client_file:files_transmitted", on_files_transmitted,
|
|
|
|
client_file::FilesTransmitted)
|
2014-10-12 11:26:33 +03:00
|
|
|
EVENT_TYPEN("network:packet_received/voxelworld:get_section",
|
|
|
|
on_get_section, network::Packet)
|
2014-10-12 10:06:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void on_start()
|
|
|
|
{
|
2014-10-15 17:14:15 +03:00
|
|
|
// TODO: Load from disk or something
|
|
|
|
|
2014-10-24 10:41:00 +03:00
|
|
|
//pv::Region region(0, 0, 0, 0, 0, 0); // Use this for valgrind
|
2014-10-15 14:44:25 +03:00
|
|
|
//pv::Region region(-1, 0, -1, 1, 0, 1);
|
2014-10-19 23:39:35 +03:00
|
|
|
//pv::Region region(-1, -1, -1, 1, 1, 1);
|
2014-10-15 04:14:10 +03:00
|
|
|
//pv::Region region(-2, -1, -2, 2, 1, 2);
|
2014-10-14 14:23:55 +03:00
|
|
|
//pv::Region region(-3, -1, -3, 3, 1, 3);
|
2014-10-19 23:39:35 +03:00
|
|
|
pv::Region region(-5, -1, 0, 0, 1, 5);
|
2014-10-18 10:10:15 +03:00
|
|
|
//pv::Region region(-5, -1, -5, 5, 1, 5);
|
|
|
|
//pv::Region region(-6, -1, -6, 6, 1, 6);
|
2014-10-14 12:23:51 +03:00
|
|
|
//pv::Region region(-8, -1, -8, 8, 1, 8);
|
2014-10-12 16:54:04 +03:00
|
|
|
auto lc = region.getLowerCorner();
|
|
|
|
auto uc = region.getUpperCorner();
|
|
|
|
for(int z = lc.getZ(); z <= uc.getZ(); z++){
|
|
|
|
for(int y = lc.getY(); y <= uc.getY(); y++){
|
|
|
|
for(int x = lc.getX(); x <= uc.getX(); x++){
|
|
|
|
load_or_generate_section(pv::Vector3DInt16(x, y, z));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-10-12 10:06:07 +03:00
|
|
|
}
|
|
|
|
|
2014-10-15 17:14:15 +03:00
|
|
|
void unload_node(Scene *scene, uint node_id)
|
|
|
|
{
|
2014-10-20 11:12:30 +03:00
|
|
|
log_d(MODULE, "Unloading node %i", node_id);
|
2014-10-15 17:14:15 +03:00
|
|
|
Node *n = scene->GetNode(node_id);
|
|
|
|
if(!n){
|
|
|
|
log_w(MODULE, "Cannot unload node %i: Not found in scene", node_id);
|
|
|
|
return;
|
|
|
|
}
|
2014-10-20 11:12:30 +03:00
|
|
|
// Remove RigidBody first to speed up removal of CollisionShapes
|
|
|
|
RigidBody *body = n->GetComponent<RigidBody>();
|
|
|
|
if(body)
|
|
|
|
n->RemoveComponent(body);
|
|
|
|
// Remove everything else
|
2014-10-15 17:14:15 +03:00
|
|
|
n->RemoveAllComponents();
|
|
|
|
n->Remove();
|
|
|
|
}
|
|
|
|
|
2014-10-12 10:06:07 +03:00
|
|
|
void on_unload()
|
|
|
|
{
|
2014-10-15 17:14:15 +03:00
|
|
|
log_v(MODULE, "on_unload()");
|
|
|
|
|
|
|
|
commit();
|
|
|
|
|
|
|
|
// Remove everything managed by us from the scene
|
|
|
|
m_server->access_scene([&](Scene *scene)
|
|
|
|
{
|
2014-10-20 11:12:30 +03:00
|
|
|
size_t progress = 0;
|
2014-10-17 13:49:55 +03:00
|
|
|
for(auto §or_pair: m_sections){
|
2014-10-20 11:12:30 +03:00
|
|
|
log_v(MODULE, "Unloading nodes... %i%%",
|
|
|
|
100 * progress / m_sections.size());
|
|
|
|
progress++;
|
2014-10-17 13:49:55 +03:00
|
|
|
for(auto §ion_pair: sector_pair.second){
|
2014-10-15 17:14:15 +03:00
|
|
|
Section §ion = section_pair.second;
|
|
|
|
|
|
|
|
auto region = section.node_ids->getEnclosingRegion();
|
|
|
|
auto lc = region.getLowerCorner();
|
|
|
|
auto uc = region.getUpperCorner();
|
|
|
|
for(int z = lc.getZ(); z <= uc.getZ(); z++){
|
|
|
|
for(int y = lc.getY(); y <= uc.getY(); y++){
|
|
|
|
for(int x = lc.getX(); x <= uc.getX(); x++){
|
|
|
|
uint id = section.node_ids->getVoxelAt(x, y, z);
|
|
|
|
section.node_ids->setVoxelAt(x, y, z, 0);
|
|
|
|
unload_node(scene, id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-10-20 11:12:30 +03:00
|
|
|
log_v(MODULE, "Unloading nodes... 100%%");
|
2014-10-15 17:14:15 +03:00
|
|
|
});
|
2014-10-19 19:59:04 +03:00
|
|
|
|
|
|
|
// Store voxel registry and stuff
|
|
|
|
std::ostringstream os(std::ios::binary);
|
|
|
|
{
|
|
|
|
cereal::PortableBinaryOutputArchive ar(os);
|
|
|
|
ar(m_voxel_reg->serialize());
|
|
|
|
}
|
|
|
|
m_server->tmp_store_data("voxelworld:restore_info", os.str());
|
2014-10-12 10:06:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void on_continue()
|
|
|
|
{
|
2014-10-19 19:59:04 +03:00
|
|
|
// Restore voxel registry and stuff
|
|
|
|
ss_ data = m_server->tmp_restore_data("voxelworld:restore_info");
|
|
|
|
ss_ voxel_reg_data;
|
|
|
|
{
|
|
|
|
std::istringstream is(data, std::ios::binary);
|
|
|
|
cereal::PortableBinaryInputArchive ar(is);
|
|
|
|
ar(voxel_reg_data);
|
|
|
|
}
|
|
|
|
m_voxel_reg->deserialize(voxel_reg_data);
|
|
|
|
|
|
|
|
// Start up normally
|
2014-10-15 17:14:15 +03:00
|
|
|
on_start();
|
2014-10-12 10:06:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void on_client_connected(const network::NewClient &client_connected)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void on_client_disconnected(const network::OldClient &old_client)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void on_tick(const interface::TickEvent &event)
|
|
|
|
{
|
2014-10-15 15:15:49 +03:00
|
|
|
m_server->access_scene([&](Scene *scene)
|
|
|
|
{
|
|
|
|
Context *context = scene->GetContext();
|
|
|
|
|
|
|
|
// Update node collision boxes
|
|
|
|
if(!m_nodes_needing_physics_update.empty()){
|
2014-10-23 23:52:07 +03:00
|
|
|
log_v(MODULE, "Updating physics of %zu nodes",
|
2014-10-15 15:15:49 +03:00
|
|
|
m_nodes_needing_physics_update.size());
|
|
|
|
}
|
2014-10-17 13:49:55 +03:00
|
|
|
for(QueuedNodePhysicsUpdate &update: m_nodes_needing_physics_update){
|
2014-10-15 15:15:49 +03:00
|
|
|
uint node_id = update.node_id;
|
|
|
|
Node *n = scene->GetNode(node_id);
|
|
|
|
if(!n){
|
|
|
|
log_w(MODULE, "on_tick(): Node physics update: "
|
|
|
|
"Node %i not found", node_id);
|
|
|
|
return;
|
|
|
|
}
|
2014-10-23 19:29:40 +03:00
|
|
|
// Get volume
|
|
|
|
const Variant &var = n->GetVar(StringHash("buildat_voxel_data"));
|
|
|
|
const PODVector<unsigned char> &rawbuf = var.GetBuffer();
|
|
|
|
ss_ data((const char*)&rawbuf[0], rawbuf.Size());
|
|
|
|
up_<pv::RawVolume<VoxelInstance>> volume =
|
|
|
|
interface::deserialize_volume(data);
|
2014-10-15 15:15:49 +03:00
|
|
|
// Update collision shape
|
2014-10-18 10:09:31 +03:00
|
|
|
interface::mesh::set_voxel_physics_boxes(n, context, *volume,
|
2014-10-15 15:15:49 +03:00
|
|
|
m_voxel_reg.get());
|
|
|
|
}
|
|
|
|
m_nodes_needing_physics_update.clear();
|
|
|
|
});
|
2014-10-23 19:29:40 +03:00
|
|
|
|
|
|
|
// Unload stuff if needed
|
|
|
|
maintain_maximum_buffer_limit();
|
2014-10-12 10:06:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void on_files_transmitted(const client_file::FilesTransmitted &event)
|
|
|
|
{
|
2014-10-12 10:11:36 +03:00
|
|
|
int peer = event.recipient;
|
2014-10-12 10:06:07 +03:00
|
|
|
network::access(m_server, [&](network::Interface *inetwork){
|
2014-10-12 10:11:36 +03:00
|
|
|
inetwork->send(peer, "core:run_script",
|
2014-10-12 10:06:07 +03:00
|
|
|
"require(\"buildat/module/voxelworld\").init()");
|
|
|
|
});
|
2014-10-12 10:11:36 +03:00
|
|
|
std::ostringstream os(std::ios::binary);
|
|
|
|
{
|
|
|
|
cereal::PortableBinaryOutputArchive ar(os);
|
|
|
|
ar(m_chunk_size_voxels);
|
|
|
|
ar(m_section_size_chunks);
|
|
|
|
}
|
|
|
|
network::access(m_server, [&](network::Interface *inetwork){
|
|
|
|
inetwork->send(peer, "voxelworld:init", os.str());
|
2014-10-19 17:31:32 +03:00
|
|
|
inetwork->send(peer, "voxelworld:voxel_registry",
|
|
|
|
m_voxel_reg->serialize());
|
2014-10-12 10:11:36 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2014-10-12 11:26:33 +03:00
|
|
|
// TODO: How should nodes be filtered for replication?
|
2014-10-12 13:53:16 +03:00
|
|
|
// TODO: Generally the client wants roughly one section, but isn't
|
|
|
|
// positioned at the middle of a section
|
2014-10-12 11:26:33 +03:00
|
|
|
void on_get_section(const network::Packet &packet)
|
2014-10-12 10:11:36 +03:00
|
|
|
{
|
2014-10-12 11:26:33 +03:00
|
|
|
pv::Vector3DInt16 section_p;
|
2014-10-12 10:11:36 +03:00
|
|
|
{
|
|
|
|
std::istringstream is(packet.data, std::ios::binary);
|
|
|
|
cereal::PortableBinaryInputArchive ar(is);
|
2014-10-12 11:26:33 +03:00
|
|
|
ar(section_p);
|
2014-10-12 10:11:36 +03:00
|
|
|
}
|
2014-10-12 11:26:33 +03:00
|
|
|
log_v(MODULE, "C%i: on_get_section(): " PV3I_FORMAT,
|
|
|
|
packet.sender, PV3I_PARAMS(section_p));
|
2014-10-12 10:06:07 +03:00
|
|
|
}
|
|
|
|
|
2014-10-12 21:17:49 +03:00
|
|
|
// Get section if exists
|
2014-10-12 14:49:11 +03:00
|
|
|
Section* get_section(const pv::Vector3DInt16 §ion_p)
|
|
|
|
{
|
2014-10-12 21:17:49 +03:00
|
|
|
// Check cache
|
|
|
|
for(Section *section : m_last_used_sections){
|
|
|
|
if(section->section_p == section_p)
|
|
|
|
return section;
|
|
|
|
}
|
|
|
|
// Not in cache
|
2014-10-12 14:49:11 +03:00
|
|
|
pv::Vector<2, int16_t> p_yz(section_p.getY(), section_p.getZ());
|
|
|
|
auto sector_it = m_sections.find(p_yz);
|
|
|
|
if(sector_it == m_sections.end())
|
|
|
|
return nullptr;
|
|
|
|
sm_<int16_t, Section> §or = sector_it->second;
|
|
|
|
auto section_it = sector.find(section_p.getX());
|
|
|
|
if(section_it == sector.end())
|
|
|
|
return nullptr;
|
|
|
|
Section §ion = section_it->second;
|
2014-10-12 21:17:49 +03:00
|
|
|
// Add to cache and return
|
|
|
|
m_last_used_sections.push_back(§ion);
|
|
|
|
if(m_last_used_sections.size() > 2) // 2 is maybe optimal-ish
|
|
|
|
m_last_used_sections.pop_front();
|
2014-10-12 14:49:11 +03:00
|
|
|
return §ion;
|
|
|
|
}
|
|
|
|
|
2014-10-12 21:17:49 +03:00
|
|
|
// Get a section; allocate it if it doesn't exist yet
|
2014-10-12 14:49:11 +03:00
|
|
|
Section& force_get_section(const pv::Vector3DInt16 §ion_p)
|
2014-10-12 13:28:24 +03:00
|
|
|
{
|
|
|
|
pv::Vector<2, int16_t> p_yz(section_p.getY(), section_p.getZ());
|
|
|
|
sm_<int16_t, Section> §or = m_sections[p_yz];
|
|
|
|
Section §ion = sector[section_p.getX()];
|
|
|
|
if(section.chunk_size.getX() == 0){
|
|
|
|
// Initialize newly created section properly
|
|
|
|
pv::Region contained_chunks(
|
|
|
|
section_p.getX() * m_section_size_chunks.getX(),
|
|
|
|
section_p.getY() * m_section_size_chunks.getY(),
|
|
|
|
section_p.getZ() * m_section_size_chunks.getZ(),
|
|
|
|
(section_p.getX()+1) * m_section_size_chunks.getX() - 1,
|
|
|
|
(section_p.getY()+1) * m_section_size_chunks.getY() - 1,
|
|
|
|
(section_p.getZ()+1) * m_section_size_chunks.getZ() - 1
|
|
|
|
);
|
|
|
|
section = Section(section_p, m_chunk_size_voxels, contained_chunks);
|
|
|
|
}
|
|
|
|
return section;
|
|
|
|
}
|
|
|
|
|
|
|
|
void create_chunk_node(Scene *scene, Section §ion, int x, int y, int z)
|
|
|
|
{
|
|
|
|
Context *context = scene->GetContext();
|
|
|
|
|
|
|
|
pv::Vector3DInt16 section_p = section.section_p;
|
|
|
|
pv::Vector3DInt32 chunk_p(
|
|
|
|
section_p.getX() * m_section_size_chunks.getX() + x,
|
|
|
|
section_p.getY() * m_section_size_chunks.getY() + y,
|
|
|
|
section_p.getZ() * m_section_size_chunks.getZ() + z
|
|
|
|
);
|
|
|
|
|
|
|
|
Vector3 node_p(
|
|
|
|
chunk_p.getX() * m_chunk_size_voxels.getX() +
|
2014-10-22 01:25:19 +03:00
|
|
|
m_chunk_size_voxels.getX() / 2.0f - 0.5f,
|
2014-10-12 13:28:24 +03:00
|
|
|
chunk_p.getY() * m_chunk_size_voxels.getY() +
|
2014-10-22 01:25:19 +03:00
|
|
|
m_chunk_size_voxels.getY() / 2.0f - 0.5f,
|
2014-10-12 13:28:24 +03:00
|
|
|
chunk_p.getZ() * m_chunk_size_voxels.getZ() +
|
2014-10-22 01:25:19 +03:00
|
|
|
m_chunk_size_voxels.getZ() / 2.0f - 0.5f
|
2014-10-12 13:28:24 +03:00
|
|
|
);
|
|
|
|
|
2014-10-23 23:08:36 +03:00
|
|
|
ss_ name = "static_"+dump(chunk_p);
|
2014-10-22 11:43:37 +03:00
|
|
|
|
|
|
|
log_t(MODULE, "create_chunk_node(): node_p=(%f, %f, %f), name=\"%s\"",
|
|
|
|
node_p.x_, node_p.y_, node_p.z_, cs(name));
|
2014-10-12 14:49:11 +03:00
|
|
|
|
2014-10-12 13:28:24 +03:00
|
|
|
Node *n = scene->CreateChild(name.c_str());
|
2014-10-12 14:49:11 +03:00
|
|
|
if(n->GetID() == 0)
|
|
|
|
throw Exception("Can't handle static node id=0");
|
|
|
|
section.node_ids->setVoxelAt(chunk_p, n->GetID());
|
|
|
|
|
2014-10-21 12:57:57 +03:00
|
|
|
// Distinguish static voxel nodes from others
|
|
|
|
n->SetVar(StringHash("buildat_static"), Variant(true));
|
2014-10-12 13:28:24 +03:00
|
|
|
|
|
|
|
int w = m_chunk_size_voxels.getX();
|
|
|
|
int h = m_chunk_size_voxels.getY();
|
|
|
|
int d = m_chunk_size_voxels.getZ();
|
2014-10-12 14:49:11 +03:00
|
|
|
|
2014-10-21 12:57:57 +03:00
|
|
|
// This makes sure the node will be found when searched from the octree,
|
|
|
|
// both on the server and the client
|
|
|
|
Zone *node_zone = n->CreateComponent<Zone>();
|
|
|
|
node_zone->SetPriority(-1000);
|
|
|
|
node_zone->SetBoundingBox(BoundingBox(
|
2014-10-22 12:30:29 +03:00
|
|
|
Vector3(-w/2, -h/2, -d/2), Vector3(w/2, h/2, d/2)));
|
2014-10-21 12:57:57 +03:00
|
|
|
|
|
|
|
n->SetScale(Vector3(1.0f, 1.0f, 1.0f));
|
|
|
|
n->SetPosition(node_p);
|
|
|
|
|
2014-10-13 01:32:58 +03:00
|
|
|
// NOTE: These volumes have one extra voxel at each edge in order to
|
|
|
|
// make proper meshes without gaps
|
2014-10-20 17:02:21 +03:00
|
|
|
// TODO: Is this needed anymore?
|
2014-10-13 01:32:58 +03:00
|
|
|
pv::Region region(-1, -1, -1, w, h, d);
|
2014-10-20 11:12:30 +03:00
|
|
|
sp_<pv::RawVolume<VoxelInstance>> volume(
|
|
|
|
new pv::RawVolume<VoxelInstance>(region));
|
2014-10-12 14:49:11 +03:00
|
|
|
|
|
|
|
auto lc = region.getLowerCorner();
|
|
|
|
auto uc = region.getUpperCorner();
|
|
|
|
for(int z = lc.getZ(); z <= uc.getZ(); z++){
|
|
|
|
for(int y = lc.getY(); y <= uc.getY(); y++){
|
|
|
|
for(int x = lc.getX(); x <= uc.getX(); x++){
|
2014-10-20 11:12:30 +03:00
|
|
|
volume->setVoxelAt(x, y, z, VoxelInstance(0));
|
2014-10-12 14:49:11 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-10-12 13:28:24 +03:00
|
|
|
|
2014-10-23 19:29:40 +03:00
|
|
|
run_commit_hooks_in_thread(chunk_p, *volume);
|
2014-10-12 13:28:24 +03:00
|
|
|
|
2014-10-20 11:12:30 +03:00
|
|
|
ss_ data = interface::serialize_volume_compressed(*volume);
|
2014-10-12 13:28:24 +03:00
|
|
|
n->SetVar(StringHash("buildat_voxel_data"), Variant(
|
2014-10-17 13:49:55 +03:00
|
|
|
PODVector<uint8_t>((const uint8_t*)data.c_str(), data.size())));
|
2014-10-12 13:28:24 +03:00
|
|
|
|
2014-10-20 11:12:30 +03:00
|
|
|
run_commit_hooks_in_scene(chunk_p, n);
|
2014-10-19 23:39:35 +03:00
|
|
|
|
2014-10-23 19:29:40 +03:00
|
|
|
m_server->emit_event("voxelworld:node_volume_updated",
|
|
|
|
new NodeVolumeUpdated(n->GetID(), true, chunk_p));
|
2014-10-21 01:47:51 +03:00
|
|
|
|
2014-10-13 16:14:29 +03:00
|
|
|
// There are no collision shapes initially, but add the rigid body now
|
2014-10-13 01:32:58 +03:00
|
|
|
RigidBody *body = n->CreateComponent<RigidBody>(LOCAL);
|
2014-10-12 13:28:24 +03:00
|
|
|
body->SetFriction(0.75f);
|
|
|
|
}
|
|
|
|
|
|
|
|
void create_section(Section §ion)
|
|
|
|
{
|
|
|
|
m_server->access_scene([&](Scene *scene)
|
|
|
|
{
|
|
|
|
auto lc = section.contained_chunks.getLowerCorner();
|
|
|
|
auto uc = section.contained_chunks.getUpperCorner();
|
2014-10-12 14:26:19 +03:00
|
|
|
for(int z = 0; z <= uc.getZ() - lc.getZ(); z++){
|
|
|
|
for(int y = 0; y <= uc.getY() - lc.getY(); y++){
|
|
|
|
for(int x = 0; x <= uc.getX() - lc.getX(); x++){
|
2014-10-12 13:28:24 +03:00
|
|
|
create_chunk_node(scene, section, x, y, z);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Somehow get the section's static nodes and possible other nodes, either
|
|
|
|
// by loading from disk or by creating new ones
|
|
|
|
void load_section(Section §ion)
|
|
|
|
{
|
|
|
|
if(section.loaded)
|
|
|
|
return;
|
|
|
|
section.loaded = true;
|
|
|
|
pv::Vector3DInt16 section_p = section.section_p;
|
2014-10-23 23:52:07 +03:00
|
|
|
log_d(MODULE, "Loading section " PV3I_FORMAT, PV3I_PARAMS(section_p));
|
2014-10-12 14:26:19 +03:00
|
|
|
|
2014-10-12 13:28:24 +03:00
|
|
|
// TODO: If found on disk, load nodes from there
|
|
|
|
// TODO: If not found on disk, create new static nodes
|
|
|
|
// Always create new nodes for now
|
|
|
|
create_section(section);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate the section; requires static nodes to already exist
|
|
|
|
void generate_section(Section §ion)
|
|
|
|
{
|
|
|
|
if(section.generated)
|
|
|
|
return;
|
|
|
|
section.generated = true;
|
|
|
|
pv::Vector3DInt16 section_p = section.section_p;
|
2014-10-23 23:52:07 +03:00
|
|
|
log_v(MODULE, "Section will be generated: " PV3I_FORMAT,
|
|
|
|
PV3I_PARAMS(section_p));
|
2014-10-12 14:26:19 +03:00
|
|
|
m_server->emit_event("voxelworld:generation_request",
|
|
|
|
new GenerationRequest(section_p));
|
2014-10-12 13:28:24 +03:00
|
|
|
}
|
|
|
|
|
2014-10-23 19:29:40 +03:00
|
|
|
void mark_node_for_physics_update(uint node_id)
|
2014-10-15 15:15:49 +03:00
|
|
|
{
|
2014-10-23 19:29:40 +03:00
|
|
|
QueuedNodePhysicsUpdate update(node_id);
|
2014-10-15 15:15:49 +03:00
|
|
|
auto it = std::lower_bound(m_nodes_needing_physics_update.begin(),
|
2014-10-17 13:49:55 +03:00
|
|
|
m_nodes_needing_physics_update.end(), update,
|
|
|
|
std::greater<QueuedNodePhysicsUpdate>());
|
2014-10-15 15:15:49 +03:00
|
|
|
if(it == m_nodes_needing_physics_update.end()){
|
|
|
|
m_nodes_needing_physics_update.insert(it, update);
|
2014-10-15 16:24:31 +03:00
|
|
|
} else if(it->node_id != node_id){
|
|
|
|
m_nodes_needing_physics_update.insert(it, update);
|
2014-10-15 15:15:49 +03:00
|
|
|
} else {
|
|
|
|
*it = update;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-20 11:12:30 +03:00
|
|
|
// Should be called before the volume is serialized so that the hook can
|
|
|
|
// modify the volume
|
|
|
|
void run_commit_hooks_in_thread(
|
|
|
|
const pv::Vector3DInt32 &chunk_p,
|
2014-10-23 19:29:40 +03:00
|
|
|
pv::RawVolume<VoxelInstance> &volume)
|
2014-10-20 11:12:30 +03:00
|
|
|
{
|
|
|
|
for(up_<CommitHook> &hook : m_commit_hooks)
|
|
|
|
hook->in_thread(this, chunk_p, volume);
|
|
|
|
}
|
|
|
|
|
|
|
|
void run_commit_hooks_in_scene(
|
|
|
|
const pv::Vector3DInt32 &chunk_p, magic::Node *n)
|
|
|
|
{
|
|
|
|
for(up_<CommitHook> &hook : m_commit_hooks)
|
|
|
|
hook->in_scene(this, chunk_p, n);
|
|
|
|
}
|
|
|
|
|
2014-10-23 19:29:40 +03:00
|
|
|
void unload_old_buffers(int64_t unload_timeout, size_t max_buffers)
|
2014-10-21 01:47:51 +03:00
|
|
|
{
|
2014-10-23 19:29:40 +03:00
|
|
|
// Swap out the current set
|
|
|
|
std::vector<Section*> sections_with_loaded_buffers;
|
|
|
|
sections_with_loaded_buffers.swap(m_sections_with_loaded_buffers);
|
|
|
|
// Allocate a new set to put back stuff that is still loaded
|
|
|
|
m_sections_with_loaded_buffers.reserve(
|
|
|
|
sections_with_loaded_buffers.size());
|
|
|
|
// Go through the swapped set, putting back sections that are still loaded
|
|
|
|
m_total_buffers_loaded = 0;
|
2014-10-24 10:41:00 +03:00
|
|
|
m_total_buffers_dirty = 0;
|
2014-10-23 19:29:40 +03:00
|
|
|
for(Section *section : sections_with_loaded_buffers){
|
|
|
|
size_t num_loaded = 0;
|
|
|
|
for(size_t i = 0; i < section->chunk_buffers.size(); i++){
|
|
|
|
ChunkBuffer &chunk_buffer = section->chunk_buffers[i];
|
|
|
|
bool unloaded = chunk_buffer.unload_if_old(unload_timeout);
|
|
|
|
if(!unloaded){
|
|
|
|
num_loaded++;
|
|
|
|
m_total_buffers_loaded++;
|
2014-10-24 10:41:00 +03:00
|
|
|
if(chunk_buffer.dirty)
|
|
|
|
m_total_buffers_dirty++;
|
2014-10-23 19:29:40 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if(num_loaded > 0)
|
|
|
|
m_sections_with_loaded_buffers.push_back(section);
|
|
|
|
}
|
|
|
|
// Call recursively if too many buffers are still loaded
|
|
|
|
if(unload_timeout > 1000 && m_total_buffers_loaded > max_buffers)
|
|
|
|
unload_old_buffers(unload_timeout / 4, max_buffers);
|
|
|
|
}
|
|
|
|
|
|
|
|
void maintain_maximum_buffer_limit()
|
|
|
|
{
|
|
|
|
if(m_total_buffers_loaded > m_max_buffers_loaded)
|
|
|
|
unload_old_buffers(m_buffer_unload_timeout, m_max_buffers_loaded);
|
2014-10-21 01:47:51 +03:00
|
|
|
}
|
|
|
|
|
2014-10-12 10:06:07 +03:00
|
|
|
// Interface
|
|
|
|
|
2014-10-20 11:12:30 +03:00
|
|
|
interface::VoxelRegistry* get_voxel_reg()
|
2014-10-12 13:28:24 +03:00
|
|
|
{
|
2014-10-20 11:12:30 +03:00
|
|
|
return m_voxel_reg.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
void add_commit_hook(up_<CommitHook> hook)
|
|
|
|
{
|
|
|
|
m_commit_hooks.push_back(std::move(hook));
|
2014-10-12 13:28:24 +03:00
|
|
|
}
|
|
|
|
|
2014-10-20 14:05:58 +03:00
|
|
|
pv::Vector3DInt16 get_section_size_voxels()
|
|
|
|
{
|
|
|
|
return pv::Vector3DInt16(
|
|
|
|
m_section_size_chunks.getX() * m_chunk_size_voxels.getX(),
|
|
|
|
m_section_size_chunks.getY() * m_chunk_size_voxels.getY(),
|
|
|
|
m_section_size_chunks.getZ() * m_chunk_size_voxels.getZ()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2014-10-15 14:44:25 +03:00
|
|
|
pv::Region get_section_region_voxels(const pv::Vector3DInt16 §ion_p)
|
2014-10-12 14:26:19 +03:00
|
|
|
{
|
2014-10-12 16:54:04 +03:00
|
|
|
pv::Vector3DInt32 p0 = pv::Vector3DInt32(
|
2014-10-12 14:26:19 +03:00
|
|
|
section_p.getX() * m_section_size_chunks.getX() *
|
2014-10-17 13:49:55 +03:00
|
|
|
m_chunk_size_voxels.getX(),
|
2014-10-12 14:26:19 +03:00
|
|
|
section_p.getY() * m_section_size_chunks.getY() *
|
2014-10-17 13:49:55 +03:00
|
|
|
m_chunk_size_voxels.getY(),
|
2014-10-12 14:26:19 +03:00
|
|
|
section_p.getZ() * m_section_size_chunks.getZ() *
|
2014-10-17 13:49:55 +03:00
|
|
|
m_chunk_size_voxels.getZ()
|
2014-10-12 14:26:19 +03:00
|
|
|
);
|
2014-10-12 16:54:04 +03:00
|
|
|
pv::Vector3DInt32 p1 = p0 + pv::Vector3DInt32(
|
2014-10-12 14:26:19 +03:00
|
|
|
m_section_size_chunks.getX() * m_chunk_size_voxels.getX() - 1,
|
|
|
|
m_section_size_chunks.getY() * m_chunk_size_voxels.getY() - 1,
|
|
|
|
m_section_size_chunks.getZ() * m_chunk_size_voxels.getZ() - 1
|
|
|
|
);
|
2014-10-12 16:54:04 +03:00
|
|
|
return pv::Region(p0, p1);
|
2014-10-12 14:26:19 +03:00
|
|
|
}
|
|
|
|
|
2014-10-20 11:12:30 +03:00
|
|
|
const pv::Vector3DInt16& get_chunk_size_voxels()
|
|
|
|
{
|
|
|
|
return m_chunk_size_voxels;
|
|
|
|
}
|
|
|
|
|
|
|
|
pv::Region get_chunk_region_voxels(const pv::Vector3DInt32 &chunk_p)
|
|
|
|
{
|
|
|
|
pv::Vector3DInt32 p0 = pv::Vector3DInt32(
|
|
|
|
chunk_p.getX() * m_chunk_size_voxels.getX(),
|
|
|
|
chunk_p.getY() * m_chunk_size_voxels.getY(),
|
|
|
|
chunk_p.getZ() * m_chunk_size_voxels.getZ()
|
|
|
|
);
|
|
|
|
pv::Vector3DInt32 p1 = p0 + pv::Vector3DInt32(
|
|
|
|
m_chunk_size_voxels.getX() - 1,
|
|
|
|
m_chunk_size_voxels.getY() - 1,
|
|
|
|
m_chunk_size_voxels.getZ() - 1
|
|
|
|
);
|
|
|
|
return pv::Region(p0, p1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void load_or_generate_section(const pv::Vector3DInt16 §ion_p)
|
|
|
|
{
|
|
|
|
Section §ion = force_get_section(section_p);
|
|
|
|
if(!section.loaded)
|
|
|
|
load_section(section);
|
|
|
|
if(!section.generated)
|
|
|
|
generate_section(section);
|
|
|
|
}
|
|
|
|
|
2014-10-12 16:54:04 +03:00
|
|
|
void set_voxel_direct(const pv::Vector3DInt32 &p,
|
|
|
|
const interface::VoxelInstance &v)
|
2014-10-12 14:26:19 +03:00
|
|
|
{
|
2014-10-12 16:54:04 +03:00
|
|
|
log_t(MODULE, "set_voxel_direct() p=" PV3I_FORMAT ", v=%i",
|
2014-10-12 14:49:11 +03:00
|
|
|
PV3I_PARAMS(p), v.data);
|
|
|
|
pv::Vector3DInt32 chunk_p = container_coord(p, m_chunk_size_voxels);
|
|
|
|
pv::Vector3DInt16 section_p =
|
|
|
|
container_coord16(chunk_p, m_section_size_chunks);
|
|
|
|
Section *section = get_section(section_p);
|
|
|
|
if(section == nullptr){
|
2014-10-12 16:54:04 +03:00
|
|
|
log_w(MODULE, "set_voxel_direct() p=" PV3I_FORMAT ", v=%i: No section "
|
2014-10-12 14:49:11 +03:00
|
|
|
" " PV3I_FORMAT " for chunk " PV3I_FORMAT,
|
|
|
|
PV3I_PARAMS(p), v.data, PV3I_PARAMS(section_p),
|
|
|
|
PV3I_PARAMS(chunk_p));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int32_t node_id = section->node_ids->getVoxelAt(chunk_p);
|
|
|
|
if(node_id == 0){
|
2014-10-12 16:54:04 +03:00
|
|
|
log_w(MODULE, "set_voxel_direct() p=" PV3I_FORMAT ", v=%i: No node for "
|
2014-10-12 14:49:11 +03:00
|
|
|
"chunk " PV3I_FORMAT " in section " PV3I_FORMAT,
|
|
|
|
PV3I_PARAMS(p), v.data, PV3I_PARAMS(chunk_p),
|
|
|
|
PV3I_PARAMS(section_p));
|
|
|
|
return;
|
|
|
|
}
|
2014-10-12 16:54:04 +03:00
|
|
|
|
|
|
|
// Have to commit first so that this modification doesn't get
|
|
|
|
// overwritten by some older one
|
2014-10-15 15:15:49 +03:00
|
|
|
// TODO: Commit only the current chunk
|
2014-10-12 16:54:04 +03:00
|
|
|
commit();
|
|
|
|
|
|
|
|
m_server->access_scene([&](Scene *scene)
|
|
|
|
{
|
|
|
|
Node *n = scene->GetNode(node_id);
|
|
|
|
const Variant &var = n->GetVar(StringHash("buildat_voxel_data"));
|
|
|
|
const PODVector<unsigned char> &buf = var.GetBuffer();
|
|
|
|
ss_ data((const char*)&buf[0], buf.Size());
|
2014-10-23 19:29:40 +03:00
|
|
|
up_<pv::RawVolume<VoxelInstance>> volume =
|
|
|
|
interface::deserialize_volume(data);
|
2014-10-12 16:54:04 +03:00
|
|
|
|
|
|
|
pv::Vector3DInt32 voxel_p(
|
2014-10-20 17:02:21 +03:00
|
|
|
p.getX() - chunk_p.getX() * m_chunk_size_voxels.getX(),
|
|
|
|
p.getY() - chunk_p.getY() * m_chunk_size_voxels.getY(),
|
|
|
|
p.getZ() - chunk_p.getZ() * m_chunk_size_voxels.getZ()
|
2014-10-17 13:49:55 +03:00
|
|
|
);
|
2014-10-12 16:54:04 +03:00
|
|
|
log_t(MODULE, "set_voxel_direct() p=" PV3I_FORMAT ", v=%i: "
|
|
|
|
"Chunk " PV3I_FORMAT " in section " PV3I_FORMAT
|
|
|
|
"; internal position " PV3I_FORMAT,
|
|
|
|
PV3I_PARAMS(p), v.data, PV3I_PARAMS(chunk_p),
|
|
|
|
PV3I_PARAMS(section_p), PV3I_PARAMS(voxel_p));
|
|
|
|
volume->setVoxelAt(voxel_p, v);
|
|
|
|
|
2014-10-23 19:29:40 +03:00
|
|
|
run_commit_hooks_in_thread(chunk_p, *volume);
|
2014-10-20 11:12:30 +03:00
|
|
|
|
2014-10-12 23:15:21 +03:00
|
|
|
ss_ new_data = interface::serialize_volume_compressed(*volume);
|
2014-10-12 16:54:04 +03:00
|
|
|
|
|
|
|
n->SetVar(StringHash("buildat_voxel_data"), Variant(
|
2014-10-17 13:49:55 +03:00
|
|
|
PODVector<uint8_t>((const uint8_t*)new_data.c_str(),
|
|
|
|
new_data.size())));
|
2014-10-20 11:12:30 +03:00
|
|
|
|
|
|
|
run_commit_hooks_in_scene(chunk_p, n);
|
2014-10-12 16:54:04 +03:00
|
|
|
});
|
2014-10-15 15:15:49 +03:00
|
|
|
|
|
|
|
// Mark node for collision box update
|
2014-10-23 19:29:40 +03:00
|
|
|
mark_node_for_physics_update(node_id);
|
2014-10-21 01:47:51 +03:00
|
|
|
|
2014-10-23 19:29:40 +03:00
|
|
|
m_server->emit_event("voxelworld:node_volume_updated",
|
|
|
|
new NodeVolumeUpdated(node_id, true, chunk_p));
|
2014-10-12 16:54:04 +03:00
|
|
|
}
|
|
|
|
|
2014-10-15 17:14:15 +03:00
|
|
|
void set_voxel(const pv::Vector3DInt32 &p, const interface::VoxelInstance &v,
|
|
|
|
bool disable_warnings)
|
2014-10-12 16:54:04 +03:00
|
|
|
{
|
2014-10-15 14:44:25 +03:00
|
|
|
// Don't log here; this is a too busy place for even ignored log calls
|
|
|
|
/*log_t(MODULE, "set_voxel() p=" PV3I_FORMAT ", v=%i",
|
2014-10-17 13:49:55 +03:00
|
|
|
PV3I_PARAMS(p), v.data);*/
|
2014-10-12 16:54:04 +03:00
|
|
|
pv::Vector3DInt32 chunk_p = container_coord(p, m_chunk_size_voxels);
|
|
|
|
pv::Vector3DInt16 section_p =
|
|
|
|
container_coord16(chunk_p, m_section_size_chunks);
|
|
|
|
Section *section = get_section(section_p);
|
|
|
|
if(section == nullptr){
|
2014-10-24 10:41:00 +03:00
|
|
|
log_(disable_warnings ? LOG_DEBUG : LOG_WARNING,
|
|
|
|
MODULE, "set_voxel() p=" PV3I_FORMAT ", v=%i: No section "
|
|
|
|
PV3I_FORMAT " for chunk " PV3I_FORMAT,
|
|
|
|
PV3I_PARAMS(p), v.data, PV3I_PARAMS(section_p),
|
|
|
|
PV3I_PARAMS(chunk_p));
|
2014-10-12 16:54:04 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-10-23 19:29:40 +03:00
|
|
|
// Unload stuff if needed
|
|
|
|
maintain_maximum_buffer_limit();
|
|
|
|
|
2014-10-15 14:44:25 +03:00
|
|
|
// Set in buffer
|
2014-10-23 19:29:40 +03:00
|
|
|
ChunkBuffer &buf = section->get_buffer(chunk_p, m_server,
|
|
|
|
&m_total_buffers_loaded);
|
2014-10-15 14:44:25 +03:00
|
|
|
if(!buf.volume){
|
2014-10-24 10:41:00 +03:00
|
|
|
log_(disable_warnings ? LOG_DEBUG : LOG_WARNING,
|
|
|
|
MODULE, "set_voxel() p=" PV3I_FORMAT ", v=%i: Couldn't get "
|
|
|
|
"buffer volume for chunk " PV3I_FORMAT " in section "
|
|
|
|
PV3I_FORMAT, PV3I_PARAMS(p), v.data, PV3I_PARAMS(chunk_p),
|
|
|
|
PV3I_PARAMS(section_p));
|
2014-10-15 14:44:25 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
pv::Vector3DInt32 voxel_p(
|
2014-10-20 17:02:21 +03:00
|
|
|
p.getX() - chunk_p.getX() * m_chunk_size_voxels.getX(),
|
|
|
|
p.getY() - chunk_p.getY() * m_chunk_size_voxels.getY(),
|
|
|
|
p.getZ() - chunk_p.getZ() * m_chunk_size_voxels.getZ()
|
2014-10-15 14:44:25 +03:00
|
|
|
);
|
|
|
|
buf.volume->setVoxelAt(voxel_p, v);
|
|
|
|
|
|
|
|
// Set buffer dirty
|
2014-10-24 10:41:00 +03:00
|
|
|
if(!buf.dirty){
|
|
|
|
buf.dirty = true;
|
|
|
|
m_total_buffers_dirty++;
|
|
|
|
}
|
2014-10-12 21:17:49 +03:00
|
|
|
|
2014-10-15 14:44:25 +03:00
|
|
|
// Set section buffer loaded flag
|
|
|
|
auto it = std::lower_bound(m_sections_with_loaded_buffers.begin(),
|
2014-10-17 13:49:55 +03:00
|
|
|
m_sections_with_loaded_buffers.end(), section,
|
|
|
|
std::greater<Section*>()); // position in descending order
|
2014-10-15 14:44:25 +03:00
|
|
|
if(it == m_sections_with_loaded_buffers.end() || *it != section)
|
|
|
|
m_sections_with_loaded_buffers.insert(it, section);
|
2014-10-12 16:54:04 +03:00
|
|
|
}
|
|
|
|
|
2014-10-15 14:44:25 +03:00
|
|
|
// Commit and unload chunk buffer
|
|
|
|
void commit_chunk_buffer(Section *section, size_t chunk_i)
|
2014-10-12 16:54:04 +03:00
|
|
|
{
|
2014-10-15 14:44:25 +03:00
|
|
|
ChunkBuffer &chunk_buffer = section->chunk_buffers[chunk_i];
|
|
|
|
if(!chunk_buffer.dirty){
|
2014-10-23 19:29:40 +03:00
|
|
|
// No changes
|
2014-10-15 14:44:25 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-10-12 16:54:04 +03:00
|
|
|
pv::Vector3DInt32 chunk_p = section->get_chunk_p(chunk_i);
|
2014-10-15 15:15:49 +03:00
|
|
|
uint node_id = section->node_ids->getVoxelAt(chunk_p);
|
2014-10-23 19:29:40 +03:00
|
|
|
|
2014-10-23 23:52:07 +03:00
|
|
|
log_d(MODULE, "Committing chunk " PV3I_FORMAT " (node %i)",
|
|
|
|
PV3I_PARAMS(chunk_p), node_id);
|
2014-10-23 19:29:40 +03:00
|
|
|
|
2014-10-12 16:54:04 +03:00
|
|
|
if(node_id == 0){
|
2014-10-15 14:44:25 +03:00
|
|
|
log_w(MODULE, "commit_chunk_buffer() chunk_i=%zu: "
|
2014-10-12 16:54:04 +03:00
|
|
|
"No node found for chunk " PV3I_FORMAT
|
|
|
|
" in section " PV3I_FORMAT,
|
|
|
|
chunk_i, PV3I_PARAMS(chunk_p),
|
|
|
|
PV3I_PARAMS(section->section_p));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-10-23 19:29:40 +03:00
|
|
|
run_commit_hooks_in_thread(chunk_p, *chunk_buffer.volume);
|
2014-10-20 11:12:30 +03:00
|
|
|
|
2014-10-15 15:15:49 +03:00
|
|
|
ss_ new_data = interface::serialize_volume_compressed(
|
|
|
|
*chunk_buffer.volume);
|
|
|
|
|
2014-10-12 16:54:04 +03:00
|
|
|
m_server->access_scene([&](Scene *scene)
|
|
|
|
{
|
2014-10-13 01:07:09 +03:00
|
|
|
Context *context = scene->GetContext();
|
|
|
|
|
2014-10-12 16:54:04 +03:00
|
|
|
Node *n = scene->GetNode(node_id);
|
|
|
|
if(!n){
|
2014-10-15 14:44:25 +03:00
|
|
|
log_w(MODULE, "commit_chunk_buffer(): Node %i not found",
|
2014-10-12 16:54:04 +03:00
|
|
|
node_id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const Variant &var = n->GetVar(StringHash("buildat_voxel_data"));
|
2014-10-15 14:44:25 +03:00
|
|
|
if(var.GetType() != VAR_BUFFER){
|
|
|
|
log_w(MODULE, "commit_chunk_buffer(): Node %i does not contain "
|
|
|
|
"an existing buffer; assuming some kind of error",
|
|
|
|
node_id);
|
|
|
|
return;
|
|
|
|
}
|
2014-10-12 16:54:04 +03:00
|
|
|
|
|
|
|
n->SetVar(StringHash("buildat_voxel_data"), Variant(
|
2014-10-17 13:49:55 +03:00
|
|
|
PODVector<uint8_t>((const uint8_t*)new_data.c_str(),
|
|
|
|
new_data.size())));
|
2014-10-20 11:12:30 +03:00
|
|
|
|
|
|
|
run_commit_hooks_in_scene(chunk_p, n);
|
2014-10-15 15:15:49 +03:00
|
|
|
});
|
2014-10-13 01:07:09 +03:00
|
|
|
|
2014-10-17 11:49:47 +03:00
|
|
|
// Tell replicate to emit events once it has done its job
|
2014-10-21 17:29:20 +03:00
|
|
|
// TODO: Have some way of invoking replication directly for this node;
|
|
|
|
// this is too slow
|
|
|
|
sv_<replicate::PeerId> peers;
|
2014-10-17 11:49:47 +03:00
|
|
|
replicate::access(m_server, [&](replicate::Interface *ireplicate){
|
2014-10-21 17:29:20 +03:00
|
|
|
ireplicate->sync_node_immediate(node_id);
|
|
|
|
peers = ireplicate->find_peers_that_know_node(node_id);
|
|
|
|
});
|
|
|
|
std::ostringstream os(std::ios::binary);
|
|
|
|
{
|
|
|
|
cereal::PortableBinaryOutputArchive ar(os);
|
|
|
|
ar((int32_t)node_id);
|
|
|
|
}
|
|
|
|
network::access(m_server, [&](network::Interface *inetwork){
|
|
|
|
for(auto &peer_id: peers){
|
2014-10-23 19:29:40 +03:00
|
|
|
inetwork->send(peer_id, "voxelworld:node_volume_updated",
|
2014-10-21 17:29:20 +03:00
|
|
|
os.str());
|
|
|
|
}
|
2014-10-17 11:49:47 +03:00
|
|
|
});
|
|
|
|
|
2014-10-15 15:15:49 +03:00
|
|
|
// Mark node for collision box update
|
2014-10-23 19:29:40 +03:00
|
|
|
mark_node_for_physics_update(node_id);
|
2014-10-13 01:07:09 +03:00
|
|
|
|
2014-10-15 15:15:49 +03:00
|
|
|
// Reset dirty flag
|
|
|
|
chunk_buffer.dirty = false;
|
2014-10-24 10:41:00 +03:00
|
|
|
m_total_buffers_dirty--;
|
2014-10-21 01:47:51 +03:00
|
|
|
|
2014-10-23 19:29:40 +03:00
|
|
|
m_server->emit_event("voxelworld:node_volume_updated",
|
|
|
|
new NodeVolumeUpdated(node_id, true, chunk_p));
|
2014-10-12 14:26:19 +03:00
|
|
|
}
|
|
|
|
|
2014-10-15 14:44:25 +03:00
|
|
|
size_t num_buffers_loaded()
|
|
|
|
{
|
2014-10-23 23:52:07 +03:00
|
|
|
return m_total_buffers_loaded;
|
2014-10-15 14:44:25 +03:00
|
|
|
}
|
|
|
|
|
2014-10-12 14:49:11 +03:00
|
|
|
void commit()
|
|
|
|
{
|
2014-10-15 14:44:25 +03:00
|
|
|
if(m_sections_with_loaded_buffers.empty())
|
|
|
|
return;
|
2014-10-24 10:41:00 +03:00
|
|
|
log_d(MODULE, "Committing %zu dirty buffers in %zu sections",
|
|
|
|
m_total_buffers_dirty,
|
2014-10-15 14:44:25 +03:00
|
|
|
m_sections_with_loaded_buffers.size());
|
|
|
|
for(Section *section : m_sections_with_loaded_buffers){
|
|
|
|
for(size_t i = 0; i < section->chunk_buffers.size(); i++){
|
|
|
|
commit_chunk_buffer(section, i);
|
2014-10-12 16:54:04 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-15 17:14:15 +03:00
|
|
|
VoxelInstance get_voxel(const pv::Vector3DInt32 &p, bool disable_warnings)
|
2014-10-12 16:54:04 +03:00
|
|
|
{
|
2014-10-15 17:14:15 +03:00
|
|
|
pv::Vector3DInt32 chunk_p = container_coord(p, m_chunk_size_voxels);
|
|
|
|
pv::Vector3DInt16 section_p =
|
|
|
|
container_coord16(chunk_p, m_section_size_chunks);
|
|
|
|
Section *section = get_section(section_p);
|
|
|
|
if(section == nullptr){
|
2014-10-24 10:41:00 +03:00
|
|
|
log_(disable_warnings ? LOG_DEBUG : LOG_WARNING,
|
|
|
|
MODULE, "get_voxel() p=" PV3I_FORMAT ": No section "
|
|
|
|
PV3I_FORMAT " for chunk " PV3I_FORMAT,
|
|
|
|
PV3I_PARAMS(p), PV3I_PARAMS(section_p),
|
|
|
|
PV3I_PARAMS(chunk_p));
|
2014-10-15 17:14:15 +03:00
|
|
|
return VoxelInstance(interface::VOXELTYPEID_UNDEFINED);
|
|
|
|
}
|
|
|
|
|
2014-10-23 19:29:40 +03:00
|
|
|
// Unload stuff if needed
|
|
|
|
maintain_maximum_buffer_limit();
|
|
|
|
|
2014-10-15 17:14:15 +03:00
|
|
|
// Get from buffer
|
2014-10-23 19:29:40 +03:00
|
|
|
ChunkBuffer &buf = section->get_buffer(chunk_p, m_server,
|
|
|
|
&m_total_buffers_loaded);
|
2014-10-15 17:14:15 +03:00
|
|
|
if(!buf.volume){
|
2014-10-24 10:41:00 +03:00
|
|
|
log_(disable_warnings ? LOG_DEBUG : LOG_WARNING,
|
|
|
|
MODULE, "get_voxel() p=" PV3I_FORMAT ": Couldn't get "
|
|
|
|
"buffer volume for chunk " PV3I_FORMAT " in section "
|
|
|
|
PV3I_FORMAT, PV3I_PARAMS(p), PV3I_PARAMS(chunk_p),
|
|
|
|
PV3I_PARAMS(section_p));
|
2014-10-15 17:14:15 +03:00
|
|
|
return VoxelInstance(interface::VOXELTYPEID_UNDEFINED);
|
|
|
|
}
|
|
|
|
pv::Vector3DInt32 voxel_p(
|
2014-10-20 17:02:21 +03:00
|
|
|
p.getX() - chunk_p.getX() * m_chunk_size_voxels.getX(),
|
|
|
|
p.getY() - chunk_p.getY() * m_chunk_size_voxels.getY(),
|
|
|
|
p.getZ() - chunk_p.getZ() * m_chunk_size_voxels.getZ()
|
2014-10-15 17:14:15 +03:00
|
|
|
);
|
|
|
|
VoxelInstance v = buf.volume->getVoxelAt(voxel_p);
|
|
|
|
|
|
|
|
// Set section buffer loaded flag
|
|
|
|
auto it = std::lower_bound(m_sections_with_loaded_buffers.begin(),
|
2014-10-17 13:49:55 +03:00
|
|
|
m_sections_with_loaded_buffers.end(), section,
|
|
|
|
std::greater<Section*>()); // position in descending order
|
2014-10-15 17:14:15 +03:00
|
|
|
if(it == m_sections_with_loaded_buffers.end() || *it != section)
|
|
|
|
m_sections_with_loaded_buffers.insert(it, section);
|
|
|
|
|
|
|
|
return v;
|
2014-10-12 14:49:11 +03:00
|
|
|
}
|
|
|
|
|
2014-10-12 10:06:07 +03:00
|
|
|
void* get_interface()
|
|
|
|
{
|
|
|
|
return dynamic_cast<Interface*>(this);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
BUILDAT_EXPORT void* createModule_voxelworld(interface::Server *server){
|
|
|
|
return (void*)(new Module(server));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-12 10:11:36 +03:00
|
|
|
// vim: set noet ts=4 sw=4:
|