WIP
This commit is contained in:
parent
9d22c4a7cb
commit
adbb3290e7
@ -159,6 +159,7 @@ if(BUILD_CLIENT)
|
||||
src/lua_bindings/voxel.cpp
|
||||
src/lua_bindings/atlas.cpp
|
||||
src/lua_bindings/mesh.cpp
|
||||
src/lua_bindings/voxel_volume.cpp
|
||||
src/lua_bindings/spatial_update_queue.cpp
|
||||
src/lua_bindings/misc_urho3d.cpp
|
||||
)
|
||||
|
@ -14,6 +14,7 @@ namespace Urho3D
|
||||
{
|
||||
class Context;
|
||||
class Scene;
|
||||
class Node;
|
||||
}
|
||||
|
||||
namespace voxelworld
|
||||
@ -38,14 +39,35 @@ namespace voxelworld
|
||||
NodeVoxelDataUpdatedEvent(uint node_id): node_id(node_id){}
|
||||
};
|
||||
|
||||
struct Interface;
|
||||
|
||||
struct CommitHook
|
||||
{
|
||||
virtual ~CommitHook(){}
|
||||
virtual void in_thread(voxelworld::Interface *ivoxelworld,
|
||||
const pv::Vector3DInt32 &chunk_p,
|
||||
sp_<pv::RawVolume<VoxelInstance>> volume){}
|
||||
virtual void in_scene(voxelworld::Interface *ivoxelworld,
|
||||
const pv::Vector3DInt32 &chunk_p, magic::Node *n){}
|
||||
};
|
||||
|
||||
struct Interface
|
||||
{
|
||||
virtual void load_or_generate_section(
|
||||
const pv::Vector3DInt16 §ion_p) = 0;
|
||||
virtual interface::VoxelRegistry* get_voxel_reg() = 0;
|
||||
|
||||
virtual void add_commit_hook(up_<CommitHook> hook) = 0;
|
||||
|
||||
virtual pv::Region get_section_region_voxels(
|
||||
const pv::Vector3DInt16 §ion_p) = 0;
|
||||
|
||||
virtual const pv::Vector3DInt16& get_chunk_size_voxels() = 0;
|
||||
|
||||
virtual pv::Region get_chunk_region_voxels(
|
||||
const pv::Vector3DInt32 &chunk_p) = 0;
|
||||
|
||||
virtual void load_or_generate_section(
|
||||
const pv::Vector3DInt16 §ion_p) = 0;
|
||||
|
||||
virtual void set_voxel(const pv::Vector3DInt32 &p,
|
||||
const VoxelInstance &v,
|
||||
bool disable_warnings = false) = 0;
|
||||
@ -57,8 +79,6 @@ namespace voxelworld
|
||||
virtual VoxelInstance get_voxel(const pv::Vector3DInt32 &p,
|
||||
bool disable_warnings = false) = 0;
|
||||
|
||||
virtual interface::VoxelRegistry* get_voxel_reg() = 0;
|
||||
|
||||
// NOTE: There is no interface in here for directly accessing chunk
|
||||
// volumes of static nodes, because it is so much more hassly and was
|
||||
// tested to improve speed only by 53% compared to the current very
|
||||
|
@ -28,9 +28,6 @@ local update_counter = -1
|
||||
|
||||
local camera_p = magic.Vector3(0, 0, 0)
|
||||
local camera_dir = magic.Vector3(0, 0, 0)
|
||||
-- Start higher than any conceivable value because otherwise things will never
|
||||
-- be triggered to reflect this value
|
||||
local camera_far_clip = 1000
|
||||
|
||||
local camera_last_p = camera_p
|
||||
local camera_last_dir = camera_dir
|
||||
@ -43,6 +40,9 @@ local atlas_reg = buildat.createAtlasRegistry()
|
||||
M.chunk_size_voxels = nil
|
||||
M.section_size_chunks = nil
|
||||
M.section_size_voxels = nil
|
||||
-- Start higher than any conceivable value because otherwise things will never
|
||||
-- be triggered to reflect this value
|
||||
M.camera_far_clip = 1000
|
||||
|
||||
-- voxelworld is ready once the init and voxel_registry packets (and in the
|
||||
-- future possibly some other ones) have been received)
|
||||
@ -60,6 +60,8 @@ function on_ready()
|
||||
on_ready_callbacks = nil
|
||||
end
|
||||
|
||||
local geometry_update_cbs = {} -- function(node)
|
||||
|
||||
function M.init()
|
||||
log:info("voxelworld.init()")
|
||||
|
||||
@ -67,7 +69,7 @@ function M.init()
|
||||
|
||||
local function queue_initial_node_update(node)
|
||||
node_update_queue:put(node:GetWorldPosition(),
|
||||
INITIAL_GEOMETRY_NEAR_WEIGHT, camera_far_clip * 1.2,
|
||||
INITIAL_GEOMETRY_NEAR_WEIGHT, M.camera_far_clip * 1.2,
|
||||
nil, nil, {
|
||||
type = "geometry",
|
||||
current_lod = 0,
|
||||
@ -127,29 +129,8 @@ function M.init()
|
||||
", d="..math.floor(d)..", #data="..data:GetSize()..
|
||||
", node="..node:GetID())
|
||||
|
||||
do
|
||||
local zone_node = replicate.main_scene:CreateChild("Zone")
|
||||
local zone = zone_node:CreateComponent("Zone")
|
||||
local cs = M.chunk_size_voxels
|
||||
zone.boundingBox = magic.BoundingBox(
|
||||
node_p - magic.Vector3(cs.x, cs.y, cs.z)/2,
|
||||
node_p + magic.Vector3(cs.x, cs.y, cs.z)/2
|
||||
)
|
||||
local has_sunlight = buildat.voxel_heuristic_has_sunlight(
|
||||
data, voxel_reg)
|
||||
if has_sunlight then
|
||||
zone.ambientColor = magic.Color(0.1, 0.1, 0.1)
|
||||
zone.fogColor = magic.Color(0.6, 0.7, 0.8)
|
||||
else
|
||||
zone.ambientColor = magic.Color(0, 0, 0)
|
||||
zone.fogColor = magic.Color(0, 0, 0)
|
||||
end
|
||||
--zone.ambientColor = magic.Color(
|
||||
-- math.random(), math.random(), math.random())
|
||||
--zone.fogEnd = 10 + math.random() * 50
|
||||
zone.fogStart = 10
|
||||
zone.fogEnd = camera_far_clip * 1.2
|
||||
--zone.ambientGradient = true
|
||||
for _, cb in ipairs(geometry_update_cbs) do
|
||||
cb(node)
|
||||
end
|
||||
|
||||
local near_trigger_d = nil
|
||||
@ -157,13 +138,13 @@ function M.init()
|
||||
local far_trigger_d = nil
|
||||
local far_weight = nil
|
||||
|
||||
if d >= camera_far_clip * 1.4 then
|
||||
if d >= M.camera_far_clip * 1.4 then
|
||||
log:verbose("Clearing voxel geometry outside camera far clip ("
|
||||
..camera_far_clip..")")
|
||||
..M.camera_far_clip..")")
|
||||
buildat.clear_voxel_geometry(node)
|
||||
|
||||
-- clip out -> 4
|
||||
near_trigger_d = 1.2 * camera_far_clip
|
||||
near_trigger_d = 1.2 * M.camera_far_clip
|
||||
near_weight = 0.4
|
||||
elseif lod == 1 then
|
||||
buildat.set_voxel_geometry(
|
||||
@ -196,7 +177,7 @@ function M.init()
|
||||
near_trigger_d = 3 * LOD_DISTANCE * (1.0 - LOD_THRESHOLD)
|
||||
near_weight = 0.5
|
||||
-- 4 -> clip out
|
||||
far_trigger_d = camera_far_clip * 1.4
|
||||
far_trigger_d = M.camera_far_clip * 1.4
|
||||
far_weight = 0.4
|
||||
end
|
||||
end
|
||||
@ -257,7 +238,7 @@ function M.init()
|
||||
if camera_node then
|
||||
camera_dir = camera_node.direction
|
||||
camera_p = camera_node:GetWorldPosition()
|
||||
camera_far_clip = camera_node:GetComponent("Camera").farClip
|
||||
M.camera_far_clip = camera_node:GetComponent("Camera").farClip
|
||||
end
|
||||
|
||||
if camera_node and M.section_size_voxels then
|
||||
@ -360,6 +341,10 @@ function M.sub_ready(cb)
|
||||
end
|
||||
end
|
||||
|
||||
function M.sub_geometry_update(cb)
|
||||
table.insert(geometry_update_cbs, cb)
|
||||
end
|
||||
|
||||
function send_get_section(p)
|
||||
local data = cereal.binary_output({
|
||||
p = {
|
||||
|
@ -269,6 +269,8 @@ struct Module: public interface::Module, public voxelworld::Interface
|
||||
sp_<interface::VoxelRegistry> m_voxel_reg;
|
||||
sp_<interface::BlockRegistry> m_block_reg;
|
||||
|
||||
sv_<up_<CommitHook>> m_commit_hooks;
|
||||
|
||||
// One node holds one chunk of voxels (eg. 24x24x24)
|
||||
pv::Vector3DInt16 m_chunk_size_voxels = pv::Vector3DInt16(32, 32, 32);
|
||||
//pv::Vector3DInt16 m_chunk_size_voxels = pv::Vector3DInt16(24, 24, 24);
|
||||
@ -364,12 +366,17 @@ struct Module: public interface::Module, public voxelworld::Interface
|
||||
|
||||
void unload_node(Scene *scene, uint node_id)
|
||||
{
|
||||
log_v(MODULE, "Unloading node %i", node_id);
|
||||
log_d(MODULE, "Unloading node %i", node_id);
|
||||
Node *n = scene->GetNode(node_id);
|
||||
if(!n){
|
||||
log_w(MODULE, "Cannot unload node %i: Not found in scene", node_id);
|
||||
return;
|
||||
}
|
||||
// Remove RigidBody first to speed up removal of CollisionShapes
|
||||
RigidBody *body = n->GetComponent<RigidBody>();
|
||||
if(body)
|
||||
n->RemoveComponent(body);
|
||||
// Remove everything else
|
||||
n->RemoveAllComponents();
|
||||
n->Remove();
|
||||
}
|
||||
@ -383,7 +390,11 @@ struct Module: public interface::Module, public voxelworld::Interface
|
||||
// Remove everything managed by us from the scene
|
||||
m_server->access_scene([&](Scene *scene)
|
||||
{
|
||||
size_t progress = 0;
|
||||
for(auto §or_pair: m_sections){
|
||||
log_v(MODULE, "Unloading nodes... %i%%",
|
||||
100 * progress / m_sections.size());
|
||||
progress++;
|
||||
for(auto §ion_pair: sector_pair.second){
|
||||
Section §ion = section_pair.second;
|
||||
|
||||
@ -401,6 +412,7 @@ struct Module: public interface::Module, public voxelworld::Interface
|
||||
}
|
||||
}
|
||||
}
|
||||
log_v(MODULE, "Unloading nodes... 100%%");
|
||||
});
|
||||
|
||||
// Store voxel registry and stuff
|
||||
@ -608,41 +620,26 @@ struct Module: public interface::Module, public voxelworld::Interface
|
||||
// NOTE: These volumes have one extra voxel at each edge in order to
|
||||
// make proper meshes without gaps
|
||||
pv::Region region(-1, -1, -1, w, h, d);
|
||||
pv::RawVolume<VoxelInstance> volume(region);
|
||||
sp_<pv::RawVolume<VoxelInstance>> volume(
|
||||
new pv::RawVolume<VoxelInstance>(region));
|
||||
|
||||
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++){
|
||||
volume.setVoxelAt(x, y, z, VoxelInstance(0));
|
||||
volume->setVoxelAt(x, y, z, VoxelInstance(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ss_ data = interface::serialize_volume_compressed(volume);
|
||||
run_commit_hooks_in_thread(chunk_p, volume);
|
||||
|
||||
ss_ data = interface::serialize_volume_compressed(*volume);
|
||||
n->SetVar(StringHash("buildat_voxel_data"), Variant(
|
||||
PODVector<uint8_t>((const uint8_t*)data.c_str(), data.size())));
|
||||
|
||||
{
|
||||
// Y-seethrough (1 = can see, 0 = can't see)
|
||||
pv::Region yst_region(0, 0, 0, w, 0, d);
|
||||
pv::RawVolume<uint8_t> yst_volume(yst_region);
|
||||
auto lc = yst_region.getLowerCorner();
|
||||
auto uc = yst_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++){
|
||||
volume.setVoxelAt(x, y, z, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
ss_ data = interface::serialize_volume_compressed(yst_volume);
|
||||
n->SetVar(StringHash("buildat_voxel_yst_data"), Variant(
|
||||
PODVector<uint8_t>((const uint8_t*)data.c_str(),
|
||||
data.size())));
|
||||
}
|
||||
run_commit_hooks_in_scene(chunk_p, n);
|
||||
|
||||
// There are no collision shapes initially, but add the rigid body now
|
||||
RigidBody *body = n->CreateComponent<RigidBody>(LOCAL);
|
||||
@ -709,15 +706,33 @@ struct Module: public interface::Module, public voxelworld::Interface
|
||||
}
|
||||
}
|
||||
|
||||
// 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,
|
||||
sp_<pv::RawVolume<VoxelInstance>> volume)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
// Interface
|
||||
|
||||
void load_or_generate_section(const pv::Vector3DInt16 §ion_p)
|
||||
interface::VoxelRegistry* get_voxel_reg()
|
||||
{
|
||||
Section §ion = force_get_section(section_p);
|
||||
if(!section.loaded)
|
||||
load_section(section);
|
||||
if(!section.generated)
|
||||
generate_section(section);
|
||||
return m_voxel_reg.get();
|
||||
}
|
||||
|
||||
void add_commit_hook(up_<CommitHook> hook)
|
||||
{
|
||||
m_commit_hooks.push_back(std::move(hook));
|
||||
}
|
||||
|
||||
pv::Region get_section_region_voxels(const pv::Vector3DInt16 §ion_p)
|
||||
@ -738,6 +753,35 @@ struct Module: public interface::Module, public voxelworld::Interface
|
||||
return pv::Region(p0, p1);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void set_voxel_direct(const pv::Vector3DInt32 &p,
|
||||
const interface::VoxelInstance &v)
|
||||
{
|
||||
@ -794,11 +838,15 @@ struct Module: public interface::Module, public voxelworld::Interface
|
||||
PV3I_PARAMS(section_p), PV3I_PARAMS(voxel_p));
|
||||
volume->setVoxelAt(voxel_p, v);
|
||||
|
||||
run_commit_hooks_in_thread(chunk_p, volume);
|
||||
|
||||
ss_ new_data = interface::serialize_volume_compressed(*volume);
|
||||
|
||||
n->SetVar(StringHash("buildat_voxel_data"), Variant(
|
||||
PODVector<uint8_t>((const uint8_t*)new_data.c_str(),
|
||||
new_data.size())));
|
||||
|
||||
run_commit_hooks_in_scene(chunk_p, n);
|
||||
});
|
||||
|
||||
// Mark node for collision box update
|
||||
@ -878,6 +926,8 @@ struct Module: public interface::Module, public voxelworld::Interface
|
||||
return;
|
||||
}
|
||||
|
||||
run_commit_hooks_in_thread(chunk_p, chunk_buffer.volume);
|
||||
|
||||
ss_ new_data = interface::serialize_volume_compressed(
|
||||
*chunk_buffer.volume);
|
||||
|
||||
@ -902,6 +952,8 @@ struct Module: public interface::Module, public voxelworld::Interface
|
||||
n->SetVar(StringHash("buildat_voxel_data"), Variant(
|
||||
PODVector<uint8_t>((const uint8_t*)new_data.c_str(),
|
||||
new_data.size())));
|
||||
|
||||
run_commit_hooks_in_scene(chunk_p, n);
|
||||
});
|
||||
|
||||
// Tell replicate to emit events once it has done its job
|
||||
@ -984,11 +1036,6 @@ struct Module: public interface::Module, public voxelworld::Interface
|
||||
return v;
|
||||
}
|
||||
|
||||
interface::VoxelRegistry* get_voxel_reg()
|
||||
{
|
||||
return m_voxel_reg.get();
|
||||
}
|
||||
|
||||
void* get_interface()
|
||||
{
|
||||
return dynamic_cast<Interface*>(this);
|
||||
|
@ -17,6 +17,10 @@ buildat.safe.AtlasSegmentDefinition = __buildat_AtlasSegmentDefinition
|
||||
buildat.safe.VoxelDefinition = __buildat_VoxelDefinition
|
||||
buildat.safe.createVoxelRegistry = __buildat_createVoxelRegistry
|
||||
buildat.safe.createAtlasRegistry = __buildat_createAtlasRegistry
|
||||
buildat.safe.Region = __buildat_Region
|
||||
buildat.safe.VoxelInstance = __buildat_VoxelInstance
|
||||
buildat.safe.Volume = __buildat_Volume
|
||||
buildat.safe.deserialize_volume_8bit = __buildat_deserialize_volume_8bit
|
||||
|
||||
-- NOTE: Maybe not actually safe
|
||||
--buildat.safe.class_info = class_info -- Luabind class_info()
|
||||
|
11
doc/todo.txt
11
doc/todo.txt
@ -23,8 +23,6 @@ Buildat TODO
|
||||
- "Continuation voxels"
|
||||
- Support Cereal's shared pointer serialization in Lua
|
||||
- Automatically fetch and build and patch Urho3D when building buildat
|
||||
- Handle module load order properly in reloads (unload by popping from top,
|
||||
then reload everything until top)
|
||||
- Automatic buildat root detection so that the server and the client can
|
||||
generally be started with any working directory in either the in-source or
|
||||
windows-installed configuration
|
||||
@ -54,3 +52,12 @@ Buildat TODO
|
||||
- Multiple replicated scens and voxelworlds, with threading, for servers with a
|
||||
lobby and multiple worlds
|
||||
- Don't emit all world generation events at once
|
||||
- Handle module load order properly in reloads (unload by popping from top,
|
||||
then reload everything until top)
|
||||
- When reloading a module, unload all dependants first; then load dependants
|
||||
again
|
||||
- Each module should be able to choose whether they want their dependants
|
||||
unloaded or not; eg. network can just store the socket fds and be reloaded
|
||||
without anyone noticing
|
||||
- Altough, network is very rarely modified so it doesn't really matter
|
||||
- Rename VoxelInstance::getId() to VoxelInstance::get_id()
|
||||
|
@ -519,6 +519,10 @@ function M.define(dst, util)
|
||||
},
|
||||
})
|
||||
|
||||
-- Add properties to Node, using types defined later
|
||||
getmetatable(dst.Node).def.properties["scene"] =
|
||||
util.simple_property(dst.Scene),
|
||||
|
||||
util.wc("ResourceCache", {
|
||||
instance = {
|
||||
GetResource = util.wrap_function({"ResourceCache", "string", "string"},
|
||||
|
@ -41,6 +41,57 @@ do
|
||||
end
|
||||
--]]
|
||||
|
||||
voxelworld.sub_geometry_update(function(node)
|
||||
local yst_data = node:GetVar("main_voxel_yst_data"):GetBuffer()
|
||||
log:verbose("#yst_data="..yst_data:GetSize())
|
||||
local yst_volume = buildat.deserialize_volume_8bit(yst_data)
|
||||
|
||||
local cs = voxelworld.chunk_size_voxels
|
||||
|
||||
local v_min = cs.y + 1
|
||||
|
||||
for x = 0, cs.x - 1 do
|
||||
for z = 0, cs.z - 1 do
|
||||
local v = yst_volume:get_voxel_at(0, 0, 0)
|
||||
--log:verbose("voxel at ("..x..",0,"..z.."): "..v.id)
|
||||
if v.data < v_min then
|
||||
v_min = v.data
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--local v_avg = v_sum / cs.x / cs.z
|
||||
--log:verbose("v_avg: "..v_avg)
|
||||
|
||||
--local has_sunlight = buildat.voxel_heuristic_has_sunlight(
|
||||
-- data, voxel_reg)
|
||||
-- TODO: This is a hack that doesn't even work properly
|
||||
local has_sunlight = (v_min < cs.y + 1)
|
||||
|
||||
local node_p = node:GetWorldPosition()
|
||||
local scene = node.scene
|
||||
|
||||
local zone_node = scene:CreateChild("Zone")
|
||||
local zone = zone_node:CreateComponent("Zone")
|
||||
zone.boundingBox = magic.BoundingBox(
|
||||
node_p - magic.Vector3(cs.x, cs.y, cs.z)/2,
|
||||
node_p + magic.Vector3(cs.x, cs.y, cs.z)/2
|
||||
)
|
||||
if has_sunlight then
|
||||
zone.ambientColor = magic.Color(0.1, 0.1, 0.1)
|
||||
zone.fogColor = magic.Color(0.6, 0.7, 0.8)
|
||||
else
|
||||
zone.ambientColor = magic.Color(0, 0, 0)
|
||||
zone.fogColor = magic.Color(0, 0, 0)
|
||||
end
|
||||
--zone.ambientColor = magic.Color(
|
||||
-- math.random(), math.random(), math.random())
|
||||
--zone.fogEnd = 10 + math.random() * 50
|
||||
zone.fogStart = 10
|
||||
zone.fogEnd = voxelworld.camera_far_clip * 1.2
|
||||
--zone.ambientGradient = true
|
||||
end)
|
||||
|
||||
-- Add lights
|
||||
do
|
||||
--[[
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "interface/mesh.h"
|
||||
#include "interface/voxel.h"
|
||||
#include "interface/noise.h"
|
||||
#include "interface/voxel_volume.h"
|
||||
#include <Scene.h>
|
||||
#include <RigidBody.h>
|
||||
#include <CollisionShape.h>
|
||||
@ -53,6 +54,63 @@ namespace digger {
|
||||
|
||||
using namespace Urho3D;
|
||||
|
||||
// Y-seethrough commit hook
|
||||
struct YstCommitHook: public voxelworld::CommitHook
|
||||
{
|
||||
static constexpr const char *MODULE = "YstCommitHook";
|
||||
ss_ m_yst_data;
|
||||
|
||||
void in_thread(voxelworld::Interface *ivoxelworld,
|
||||
const pv::Vector3DInt32 &chunk_p,
|
||||
sp_<pv::RawVolume<VoxelInstance>> volume)
|
||||
{
|
||||
interface::VoxelRegistry *voxel_reg = ivoxelworld->get_voxel_reg();
|
||||
const auto &chunk_size_voxels = ivoxelworld->get_chunk_size_voxels();
|
||||
int w = chunk_size_voxels.getX();
|
||||
int h = chunk_size_voxels.getY();
|
||||
int d = chunk_size_voxels.getZ();
|
||||
pv::Region yst_region(0, 0, 0, w, 0, d);
|
||||
// Y-seethrough (value: Lowest Y where light can go)
|
||||
// If chunk above does not pass light to this chunk, value=d+1
|
||||
up_<pv::RawVolume<uint8_t>> yst_volume(
|
||||
new pv::RawVolume<uint8_t>(yst_region));
|
||||
auto lc = yst_region.getLowerCorner();
|
||||
auto uc = yst_region.getUpperCorner();
|
||||
for(int z = 0; z < d; z++){
|
||||
for(int x = 0; x <= w; x++){
|
||||
// TODO: Read initial value from the chunk above
|
||||
int y;
|
||||
for(y = d; y >= 0; y--){
|
||||
VoxelInstance v = volume->getVoxelAt(x+1, y+1, z+1);
|
||||
const auto *def = voxel_reg->get_cached(v);
|
||||
if(!def)
|
||||
throw Exception(ss_()+"Undefined voxel: "+itos(v.getId()));
|
||||
bool light_passes = (!def || !def->physically_solid);
|
||||
if(!light_passes)
|
||||
break;
|
||||
}
|
||||
y++;
|
||||
yst_volume->setVoxelAt(x, 0, z, y);
|
||||
}
|
||||
}
|
||||
m_yst_data = interface::serialize_volume_compressed(*yst_volume);
|
||||
}
|
||||
|
||||
void in_scene(voxelworld::Interface *ivoxelworld,
|
||||
const pv::Vector3DInt32 &chunk_p, magic::Node *n)
|
||||
{
|
||||
if(m_yst_data.empty()){
|
||||
// Can happen if in_thread() fails
|
||||
log_w(MODULE, "Commit hook in_scene executed without in_thread");
|
||||
return;
|
||||
}
|
||||
n->SetVar(StringHash("main_voxel_yst_data"), Variant(
|
||||
PODVector<uint8_t>((const uint8_t*)m_yst_data.c_str(),
|
||||
m_yst_data.size())));
|
||||
m_yst_data.clear();
|
||||
}
|
||||
};
|
||||
|
||||
struct Module: public interface::Module
|
||||
{
|
||||
interface::Server *m_server;
|
||||
@ -98,8 +156,10 @@ struct Module: public interface::Module
|
||||
{
|
||||
voxelworld::access(m_server, [&](voxelworld::Interface *ivoxelworld)
|
||||
{
|
||||
interface::VoxelRegistry *voxel_reg =
|
||||
ivoxelworld->get_voxel_reg();
|
||||
ivoxelworld->add_commit_hook(
|
||||
up_<YstCommitHook>(new YstCommitHook()));
|
||||
|
||||
interface::VoxelRegistry *voxel_reg = ivoxelworld->get_voxel_reg();
|
||||
{
|
||||
interface::VoxelDefinition vdef;
|
||||
vdef.name.block_name = "air";
|
||||
|
@ -157,7 +157,7 @@ ss_ serialize_volume_compressed(const pv::RawVolume<uint8_t> &volume)
|
||||
return os.str();
|
||||
}
|
||||
|
||||
up_<pv::RawVolume<uint8_t>> deserialize_volume_uint8(const ss_ &data)
|
||||
up_<pv::RawVolume<uint8_t>> deserialize_volume_8bit(const ss_ &data)
|
||||
{
|
||||
std::istringstream is(data, std::ios::binary);
|
||||
cereal::PortableBinaryInputArchive ar(is);
|
||||
|
@ -14,6 +14,7 @@ extern void init_cereal(lua_State *L);
|
||||
extern void init_voxel(lua_State *L);
|
||||
extern void init_atlas(lua_State *L);
|
||||
extern void init_mesh(lua_State *L);
|
||||
extern void init_voxel_volume(lua_State *L);
|
||||
extern void init_spatial_update_queue(lua_State *L);
|
||||
extern void init_misc_urho3d(lua_State *L);
|
||||
|
||||
@ -27,6 +28,7 @@ void init(lua_State *L)
|
||||
init_voxel(L);
|
||||
init_atlas(L);
|
||||
init_mesh(L);
|
||||
init_voxel_volume(L);
|
||||
init_spatial_update_queue(L);
|
||||
init_misc_urho3d(L);
|
||||
}
|
||||
|
@ -22,9 +22,26 @@
|
||||
} \
|
||||
lua_pop(L, 1); /* type_name */ \
|
||||
lua_getfield(L, -1, "unsafe"); \
|
||||
int top_##name##_L = lua_gettop(L); \
|
||||
GET_TOLUA_STUFF(result_name, top_##name##_L, type); \
|
||||
int top_##result_name##_L = lua_gettop(L); \
|
||||
GET_TOLUA_STUFF(result_name, top_##result_name##_L, type); \
|
||||
lua_pop(L, 2); /* unsafe, metatable */
|
||||
|
||||
#define TRY_GET_SANDBOX_STUFF(result_name, index, type) \
|
||||
lua_getmetatable(L, index); \
|
||||
lua_getfield(L, -1, "type_name"); \
|
||||
type *result_name = nullptr; \
|
||||
if(ss_(lua_tostring(L, -1)) != #type){ \
|
||||
lua_pop(L, 2); /* type_name, metatable */ \
|
||||
throw Exception("Value is not a sandboxed " #type); \
|
||||
} else { \
|
||||
lua_pop(L, 1); /* type_name */ \
|
||||
lua_getfield(L, -1, "unsafe"); \
|
||||
int top_L = lua_gettop(L); \
|
||||
tolua_Error tolua_err; \
|
||||
if(tolua_isusertype(L, top_L, #type, 0, &tolua_err)) \
|
||||
result_name = (type*)tolua_tousertype(L, top_L, 0); \
|
||||
lua_pop(L, 2); /* unsafe, metatable */ \
|
||||
}
|
||||
|
||||
// vim: set noet ts=4 sw=4:
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "interface/voxel.h"
|
||||
#include "lua_bindings/util.h"
|
||||
#include "lua_bindings/luabind_util.h"
|
||||
#include "lua_bindings/sandbox_util.h"
|
||||
#include <luabind/luabind.hpp>
|
||||
#include <luabind/adopt_policy.hpp>
|
||||
#include <luabind/object.hpp>
|
||||
@ -14,15 +15,6 @@
|
||||
#pragma GCC diagnostic pop
|
||||
#define MODULE "lua_bindings"
|
||||
|
||||
#define GET_TOLUA_STUFF(result_name, index, type){ \
|
||||
tolua_Error tolua_err; \
|
||||
if(!tolua_isusertype(L, index, #type, 0, &tolua_err)){ \
|
||||
tolua_error(L, __PRETTY_FUNCTION__, &tolua_err); \
|
||||
throw Exception("Expected \"" #type "\""); \
|
||||
} \
|
||||
} \
|
||||
type *result_name = (type*)tolua_tousertype(L, index, 0);
|
||||
|
||||
using namespace interface;
|
||||
using Urho3D::IntVector2;
|
||||
|
||||
@ -40,28 +32,8 @@ luabind::object asd_get_total_segments(
|
||||
void asd_set_total_segments(
|
||||
AtlasSegmentDefinition &def, luabind::object value_safe, lua_State *L)
|
||||
{
|
||||
// This won't work
|
||||
/*luabind::object value_meta = luabind::getmetatable(value_safe);
|
||||
if(luabind::object_cast<ss_>(value_meta["type_name"]) != "IntVector2")
|
||||
throw Exception("Value is not a sandboxed IntVector2");
|
||||
luabind::object value = value_meta["unsafe"];
|
||||
value.push(L);
|
||||
int top_L = lua_gettop(L);
|
||||
GET_TOLUA_STUFF(v, top_L, IntVector2);*/
|
||||
|
||||
// This works
|
||||
lua_getmetatable(L, 2);
|
||||
lua_getfield(L, -1, "type_name");
|
||||
if(ss_(lua_tostring(L, -1)) != "IntVector2"){
|
||||
lua_pop(L, 2); // type_name, metatable
|
||||
throw Exception("Value is not a sandboxed IntVector2");
|
||||
}
|
||||
lua_pop(L, 1); // type_name
|
||||
lua_getfield(L, -1, "unsafe");
|
||||
int top_L = lua_gettop(L);
|
||||
GET_TOLUA_STUFF(v, top_L, IntVector2);
|
||||
GET_SANDBOX_STUFF(v, 2, IntVector2);
|
||||
def.total_segments = *v;
|
||||
lua_pop(L, 2); // unsafe, metatable
|
||||
}
|
||||
|
||||
luabind::object asd_get_select_segment(
|
||||
@ -74,18 +46,8 @@ luabind::object asd_get_select_segment(
|
||||
void asd_set_select_segment(
|
||||
AtlasSegmentDefinition &def, luabind::object value, lua_State *L)
|
||||
{
|
||||
lua_getmetatable(L, 2);
|
||||
lua_getfield(L, -1, "type_name");
|
||||
if(ss_(lua_tostring(L, -1)) != "IntVector2"){
|
||||
lua_pop(L, 2); // type_name, metatable
|
||||
throw Exception("Value is not a sandboxed IntVector2");
|
||||
}
|
||||
lua_pop(L, 1); // type_name
|
||||
lua_getfield(L, -1, "unsafe");
|
||||
int top_L = lua_gettop(L);
|
||||
GET_TOLUA_STUFF(v, top_L, IntVector2);
|
||||
GET_SANDBOX_STUFF(v, 2, IntVector2);
|
||||
def.select_segment = *v;
|
||||
lua_pop(L, 2); // unsafe, metatable
|
||||
}
|
||||
|
||||
luabind::object vdef_get_textures(const VoxelDefinition &def, lua_State *L)
|
||||
|
102
src/lua_bindings/voxel_volume.cpp
Normal file
102
src/lua_bindings/voxel_volume.cpp
Normal file
@ -0,0 +1,102 @@
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Copyright 2014 Perttu Ahola <celeron55@gmail.com>
|
||||
#include "core/log.h"
|
||||
#include "lua_bindings/util.h"
|
||||
#include "lua_bindings/sandbox_util.h"
|
||||
#include "client/app.h"
|
||||
#include "interface/voxel_volume.h"
|
||||
#include <c55/os.h>
|
||||
#include <tolua++.h>
|
||||
#include <luabind/luabind.hpp>
|
||||
#include <luabind/adopt_policy.hpp>
|
||||
#include <luabind/pointer_traits.hpp>
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
#include <VectorBuffer.h>
|
||||
#pragma GCC diagnostic pop
|
||||
#define MODULE "lua_bindings"
|
||||
|
||||
namespace magic = Urho3D;
|
||||
namespace pv = PolyVox;
|
||||
|
||||
using interface::VoxelInstance;
|
||||
using interface::VoxelRegistry;
|
||||
using interface::AtlasRegistry;
|
||||
using namespace Urho3D;
|
||||
|
||||
namespace lua_bindings {
|
||||
|
||||
#define TRY_GET_TOLUA_STUFF(result_name, index, type) \
|
||||
type *result_name = nullptr; \
|
||||
{ \
|
||||
tolua_Error tolua_err; \
|
||||
if(tolua_isusertype(L, index, #type, 0, &tolua_err)) \
|
||||
result_name = (type*)tolua_tousertype(L, index, 0); \
|
||||
}
|
||||
|
||||
typedef pv::RawVolume<VoxelInstance> CommonVolume;
|
||||
|
||||
sp_<CommonVolume> deserialize_volume_8bit(
|
||||
const luabind::object &buffer_o, lua_State *L)
|
||||
{
|
||||
TRY_GET_SANDBOX_STUFF(buf, 1, VectorBuffer);
|
||||
|
||||
ss_ data;
|
||||
if(buf == nullptr)
|
||||
data = lua_tocppstring(L, 2);
|
||||
else
|
||||
data.assign((const char*)&buf->GetBuffer()[0], buf->GetBuffer().Size());
|
||||
|
||||
up_<pv::RawVolume<uint8_t>> volume_8bit =
|
||||
interface::deserialize_volume_8bit(data);
|
||||
|
||||
auto region = volume_8bit->getEnclosingRegion();
|
||||
|
||||
sp_<CommonVolume> volume(new CommonVolume(region));
|
||||
|
||||
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++){
|
||||
uint8_t v = volume_8bit->getVoxelAt(x, y, z);
|
||||
volume->setVoxelAt(x, y, z, VoxelInstance(v));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return volume;
|
||||
}
|
||||
|
||||
#define LUABIND_FUNC(name) def("__buildat_" #name, name)
|
||||
|
||||
void init_voxel_volume(lua_State *L)
|
||||
{
|
||||
using namespace luabind;
|
||||
module(L)[
|
||||
class_<pv::Region, bases<>, sp_<pv::Region>>("__buildat_Region")
|
||||
.def(constructor<int, int, int, int, int ,int>())
|
||||
,
|
||||
class_<VoxelInstance>("__buildat_VoxelInstance")
|
||||
.def(constructor<uint32_t>())
|
||||
.def_readwrite("data", &VoxelInstance::data)
|
||||
.property("id", &VoxelInstance::getId)
|
||||
.def("get_id", &VoxelInstance::getId)
|
||||
,
|
||||
class_<CommonVolume, bases<>, sp_<CommonVolume>>("__buildat_Volume")
|
||||
.def(constructor<const pv::Region &>())
|
||||
.def("get_voxel_at", (VoxelInstance (CommonVolume::*)
|
||||
(int32_t, int32_t, int32_t) const) &CommonVolume::getVoxelAt)
|
||||
.def("set_voxel_at", (bool (CommonVolume::*)
|
||||
(int32_t, int32_t, int32_t, VoxelInstance))
|
||||
&CommonVolume::setVoxelAt)
|
||||
.def("get_enclosing_region", &CommonVolume::getEnclosingRegion)
|
||||
,
|
||||
LUABIND_FUNC(deserialize_volume_8bit)
|
||||
];
|
||||
}
|
||||
|
||||
} // namespace lua_bindingss
|
||||
|
||||
// codestyle:disable (currently util/codestyle.sh screws up the .def formatting)
|
||||
// vim: set noet ts=4 sw=4:
|
Loading…
x
Reference in New Issue
Block a user