This commit is contained in:
Perttu Ahola 2014-10-20 11:12:30 +03:00
parent 9d22c4a7cb
commit adbb3290e7
14 changed files with 379 additions and 117 deletions

View File

@ -159,6 +159,7 @@ if(BUILD_CLIENT)
src/lua_bindings/voxel.cpp src/lua_bindings/voxel.cpp
src/lua_bindings/atlas.cpp src/lua_bindings/atlas.cpp
src/lua_bindings/mesh.cpp src/lua_bindings/mesh.cpp
src/lua_bindings/voxel_volume.cpp
src/lua_bindings/spatial_update_queue.cpp src/lua_bindings/spatial_update_queue.cpp
src/lua_bindings/misc_urho3d.cpp src/lua_bindings/misc_urho3d.cpp
) )

View File

@ -14,6 +14,7 @@ namespace Urho3D
{ {
class Context; class Context;
class Scene; class Scene;
class Node;
} }
namespace voxelworld namespace voxelworld
@ -38,14 +39,35 @@ namespace voxelworld
NodeVoxelDataUpdatedEvent(uint node_id): node_id(node_id){} 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 struct Interface
{ {
virtual void load_or_generate_section( virtual interface::VoxelRegistry* get_voxel_reg() = 0;
const pv::Vector3DInt16 &section_p) = 0;
virtual void add_commit_hook(up_<CommitHook> hook) = 0;
virtual pv::Region get_section_region_voxels( virtual pv::Region get_section_region_voxels(
const pv::Vector3DInt16 &section_p) = 0; const pv::Vector3DInt16 &section_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 &section_p) = 0;
virtual void set_voxel(const pv::Vector3DInt32 &p, virtual void set_voxel(const pv::Vector3DInt32 &p,
const VoxelInstance &v, const VoxelInstance &v,
bool disable_warnings = false) = 0; bool disable_warnings = false) = 0;
@ -57,8 +79,6 @@ namespace voxelworld
virtual VoxelInstance get_voxel(const pv::Vector3DInt32 &p, virtual VoxelInstance get_voxel(const pv::Vector3DInt32 &p,
bool disable_warnings = false) = 0; bool disable_warnings = false) = 0;
virtual interface::VoxelRegistry* get_voxel_reg() = 0;
// NOTE: There is no interface in here for directly accessing chunk // NOTE: There is no interface in here for directly accessing chunk
// volumes of static nodes, because it is so much more hassly and was // 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 // tested to improve speed only by 53% compared to the current very

View File

@ -28,9 +28,6 @@ local update_counter = -1
local camera_p = magic.Vector3(0, 0, 0) local camera_p = magic.Vector3(0, 0, 0)
local camera_dir = 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_p = camera_p
local camera_last_dir = camera_dir local camera_last_dir = camera_dir
@ -43,6 +40,9 @@ local atlas_reg = buildat.createAtlasRegistry()
M.chunk_size_voxels = nil M.chunk_size_voxels = nil
M.section_size_chunks = nil M.section_size_chunks = nil
M.section_size_voxels = 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 -- voxelworld is ready once the init and voxel_registry packets (and in the
-- future possibly some other ones) have been received) -- future possibly some other ones) have been received)
@ -60,6 +60,8 @@ function on_ready()
on_ready_callbacks = nil on_ready_callbacks = nil
end end
local geometry_update_cbs = {} -- function(node)
function M.init() function M.init()
log:info("voxelworld.init()") log:info("voxelworld.init()")
@ -67,7 +69,7 @@ function M.init()
local function queue_initial_node_update(node) local function queue_initial_node_update(node)
node_update_queue:put(node:GetWorldPosition(), 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, { nil, nil, {
type = "geometry", type = "geometry",
current_lod = 0, current_lod = 0,
@ -127,29 +129,8 @@ function M.init()
", d="..math.floor(d)..", #data="..data:GetSize().. ", d="..math.floor(d)..", #data="..data:GetSize()..
", node="..node:GetID()) ", node="..node:GetID())
do for _, cb in ipairs(geometry_update_cbs) do
local zone_node = replicate.main_scene:CreateChild("Zone") cb(node)
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
end end
local near_trigger_d = nil local near_trigger_d = nil
@ -157,13 +138,13 @@ function M.init()
local far_trigger_d = nil local far_trigger_d = nil
local far_weight = 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 (" log:verbose("Clearing voxel geometry outside camera far clip ("
..camera_far_clip..")") ..M.camera_far_clip..")")
buildat.clear_voxel_geometry(node) buildat.clear_voxel_geometry(node)
-- clip out -> 4 -- clip out -> 4
near_trigger_d = 1.2 * camera_far_clip near_trigger_d = 1.2 * M.camera_far_clip
near_weight = 0.4 near_weight = 0.4
elseif lod == 1 then elseif lod == 1 then
buildat.set_voxel_geometry( buildat.set_voxel_geometry(
@ -196,7 +177,7 @@ function M.init()
near_trigger_d = 3 * LOD_DISTANCE * (1.0 - LOD_THRESHOLD) near_trigger_d = 3 * LOD_DISTANCE * (1.0 - LOD_THRESHOLD)
near_weight = 0.5 near_weight = 0.5
-- 4 -> clip out -- 4 -> clip out
far_trigger_d = camera_far_clip * 1.4 far_trigger_d = M.camera_far_clip * 1.4
far_weight = 0.4 far_weight = 0.4
end end
end end
@ -257,7 +238,7 @@ function M.init()
if camera_node then if camera_node then
camera_dir = camera_node.direction camera_dir = camera_node.direction
camera_p = camera_node:GetWorldPosition() camera_p = camera_node:GetWorldPosition()
camera_far_clip = camera_node:GetComponent("Camera").farClip M.camera_far_clip = camera_node:GetComponent("Camera").farClip
end end
if camera_node and M.section_size_voxels then if camera_node and M.section_size_voxels then
@ -360,6 +341,10 @@ function M.sub_ready(cb)
end end
end end
function M.sub_geometry_update(cb)
table.insert(geometry_update_cbs, cb)
end
function send_get_section(p) function send_get_section(p)
local data = cereal.binary_output({ local data = cereal.binary_output({
p = { p = {

View File

@ -269,6 +269,8 @@ struct Module: public interface::Module, public voxelworld::Interface
sp_<interface::VoxelRegistry> m_voxel_reg; sp_<interface::VoxelRegistry> m_voxel_reg;
sp_<interface::BlockRegistry> m_block_reg; sp_<interface::BlockRegistry> m_block_reg;
sv_<up_<CommitHook>> m_commit_hooks;
// One node holds one chunk of voxels (eg. 24x24x24) // 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(32, 32, 32);
//pv::Vector3DInt16 m_chunk_size_voxels = pv::Vector3DInt16(24, 24, 24); //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) 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); Node *n = scene->GetNode(node_id);
if(!n){ if(!n){
log_w(MODULE, "Cannot unload node %i: Not found in scene", node_id); log_w(MODULE, "Cannot unload node %i: Not found in scene", node_id);
return; 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->RemoveAllComponents();
n->Remove(); n->Remove();
} }
@ -383,7 +390,11 @@ struct Module: public interface::Module, public voxelworld::Interface
// Remove everything managed by us from the scene // Remove everything managed by us from the scene
m_server->access_scene([&](Scene *scene) m_server->access_scene([&](Scene *scene)
{ {
size_t progress = 0;
for(auto &sector_pair: m_sections){ for(auto &sector_pair: m_sections){
log_v(MODULE, "Unloading nodes... %i%%",
100 * progress / m_sections.size());
progress++;
for(auto &section_pair: sector_pair.second){ for(auto &section_pair: sector_pair.second){
Section &section = section_pair.second; Section &section = 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 // 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 // NOTE: These volumes have one extra voxel at each edge in order to
// make proper meshes without gaps // make proper meshes without gaps
pv::Region region(-1, -1, -1, w, h, d); 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 lc = region.getLowerCorner();
auto uc = region.getUpperCorner(); auto uc = region.getUpperCorner();
for(int z = lc.getZ(); z <= uc.getZ(); z++){ for(int z = lc.getZ(); z <= uc.getZ(); z++){
for(int y = lc.getY(); y <= uc.getY(); y++){ for(int y = lc.getY(); y <= uc.getY(); y++){
for(int x = lc.getX(); x <= uc.getX(); x++){ 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( n->SetVar(StringHash("buildat_voxel_data"), Variant(
PODVector<uint8_t>((const uint8_t*)data.c_str(), data.size()))); PODVector<uint8_t>((const uint8_t*)data.c_str(), data.size())));
{ run_commit_hooks_in_scene(chunk_p, n);
// 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())));
}
// There are no collision shapes initially, but add the rigid body now // There are no collision shapes initially, but add the rigid body now
RigidBody *body = n->CreateComponent<RigidBody>(LOCAL); 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 // Interface
void load_or_generate_section(const pv::Vector3DInt16 &section_p) interface::VoxelRegistry* get_voxel_reg()
{ {
Section &section = force_get_section(section_p); return m_voxel_reg.get();
if(!section.loaded) }
load_section(section);
if(!section.generated) void add_commit_hook(up_<CommitHook> hook)
generate_section(section); {
m_commit_hooks.push_back(std::move(hook));
} }
pv::Region get_section_region_voxels(const pv::Vector3DInt16 &section_p) pv::Region get_section_region_voxels(const pv::Vector3DInt16 &section_p)
@ -738,6 +753,35 @@ struct Module: public interface::Module, public voxelworld::Interface
return pv::Region(p0, p1); 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 &section_p)
{
Section &section = 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, void set_voxel_direct(const pv::Vector3DInt32 &p,
const interface::VoxelInstance &v) const interface::VoxelInstance &v)
{ {
@ -794,11 +838,15 @@ struct Module: public interface::Module, public voxelworld::Interface
PV3I_PARAMS(section_p), PV3I_PARAMS(voxel_p)); PV3I_PARAMS(section_p), PV3I_PARAMS(voxel_p));
volume->setVoxelAt(voxel_p, v); volume->setVoxelAt(voxel_p, v);
run_commit_hooks_in_thread(chunk_p, volume);
ss_ new_data = interface::serialize_volume_compressed(*volume); ss_ new_data = interface::serialize_volume_compressed(*volume);
n->SetVar(StringHash("buildat_voxel_data"), Variant( n->SetVar(StringHash("buildat_voxel_data"), Variant(
PODVector<uint8_t>((const uint8_t*)new_data.c_str(), PODVector<uint8_t>((const uint8_t*)new_data.c_str(),
new_data.size()))); new_data.size())));
run_commit_hooks_in_scene(chunk_p, n);
}); });
// Mark node for collision box update // Mark node for collision box update
@ -878,6 +926,8 @@ struct Module: public interface::Module, public voxelworld::Interface
return; return;
} }
run_commit_hooks_in_thread(chunk_p, chunk_buffer.volume);
ss_ new_data = interface::serialize_volume_compressed( ss_ new_data = interface::serialize_volume_compressed(
*chunk_buffer.volume); *chunk_buffer.volume);
@ -902,6 +952,8 @@ struct Module: public interface::Module, public voxelworld::Interface
n->SetVar(StringHash("buildat_voxel_data"), Variant( n->SetVar(StringHash("buildat_voxel_data"), Variant(
PODVector<uint8_t>((const uint8_t*)new_data.c_str(), PODVector<uint8_t>((const uint8_t*)new_data.c_str(),
new_data.size()))); new_data.size())));
run_commit_hooks_in_scene(chunk_p, n);
}); });
// Tell replicate to emit events once it has done its job // 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; return v;
} }
interface::VoxelRegistry* get_voxel_reg()
{
return m_voxel_reg.get();
}
void* get_interface() void* get_interface()
{ {
return dynamic_cast<Interface*>(this); return dynamic_cast<Interface*>(this);

View File

@ -17,6 +17,10 @@ buildat.safe.AtlasSegmentDefinition = __buildat_AtlasSegmentDefinition
buildat.safe.VoxelDefinition = __buildat_VoxelDefinition buildat.safe.VoxelDefinition = __buildat_VoxelDefinition
buildat.safe.createVoxelRegistry = __buildat_createVoxelRegistry buildat.safe.createVoxelRegistry = __buildat_createVoxelRegistry
buildat.safe.createAtlasRegistry = __buildat_createAtlasRegistry 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 -- NOTE: Maybe not actually safe
--buildat.safe.class_info = class_info -- Luabind class_info() --buildat.safe.class_info = class_info -- Luabind class_info()

View File

@ -23,8 +23,6 @@ Buildat TODO
- "Continuation voxels" - "Continuation voxels"
- Support Cereal's shared pointer serialization in Lua - Support Cereal's shared pointer serialization in Lua
- Automatically fetch and build and patch Urho3D when building buildat - 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 - 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 generally be started with any working directory in either the in-source or
windows-installed configuration windows-installed configuration
@ -54,3 +52,12 @@ Buildat TODO
- Multiple replicated scens and voxelworlds, with threading, for servers with a - Multiple replicated scens and voxelworlds, with threading, for servers with a
lobby and multiple worlds lobby and multiple worlds
- Don't emit all world generation events at once - 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()

View File

@ -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", { util.wc("ResourceCache", {
instance = { instance = {
GetResource = util.wrap_function({"ResourceCache", "string", "string"}, GetResource = util.wrap_function({"ResourceCache", "string", "string"},

View File

@ -41,6 +41,57 @@ do
end 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 -- Add lights
do do
--[[ --[[

View File

@ -9,6 +9,7 @@
#include "interface/mesh.h" #include "interface/mesh.h"
#include "interface/voxel.h" #include "interface/voxel.h"
#include "interface/noise.h" #include "interface/noise.h"
#include "interface/voxel_volume.h"
#include <Scene.h> #include <Scene.h>
#include <RigidBody.h> #include <RigidBody.h>
#include <CollisionShape.h> #include <CollisionShape.h>
@ -53,6 +54,63 @@ namespace digger {
using namespace Urho3D; 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 struct Module: public interface::Module
{ {
interface::Server *m_server; interface::Server *m_server;
@ -98,8 +156,10 @@ struct Module: public interface::Module
{ {
voxelworld::access(m_server, [&](voxelworld::Interface *ivoxelworld) voxelworld::access(m_server, [&](voxelworld::Interface *ivoxelworld)
{ {
interface::VoxelRegistry *voxel_reg = ivoxelworld->add_commit_hook(
ivoxelworld->get_voxel_reg(); up_<YstCommitHook>(new YstCommitHook()));
interface::VoxelRegistry *voxel_reg = ivoxelworld->get_voxel_reg();
{ {
interface::VoxelDefinition vdef; interface::VoxelDefinition vdef;
vdef.name.block_name = "air"; vdef.name.block_name = "air";

View File

@ -157,7 +157,7 @@ ss_ serialize_volume_compressed(const pv::RawVolume<uint8_t> &volume)
return os.str(); 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); std::istringstream is(data, std::ios::binary);
cereal::PortableBinaryInputArchive ar(is); cereal::PortableBinaryInputArchive ar(is);

View File

@ -14,6 +14,7 @@ extern void init_cereal(lua_State *L);
extern void init_voxel(lua_State *L); extern void init_voxel(lua_State *L);
extern void init_atlas(lua_State *L); extern void init_atlas(lua_State *L);
extern void init_mesh(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_spatial_update_queue(lua_State *L);
extern void init_misc_urho3d(lua_State *L); extern void init_misc_urho3d(lua_State *L);
@ -27,6 +28,7 @@ void init(lua_State *L)
init_voxel(L); init_voxel(L);
init_atlas(L); init_atlas(L);
init_mesh(L); init_mesh(L);
init_voxel_volume(L);
init_spatial_update_queue(L); init_spatial_update_queue(L);
init_misc_urho3d(L); init_misc_urho3d(L);
} }

View File

@ -22,9 +22,26 @@
} \ } \
lua_pop(L, 1); /* type_name */ \ lua_pop(L, 1); /* type_name */ \
lua_getfield(L, -1, "unsafe"); \ lua_getfield(L, -1, "unsafe"); \
int top_##name##_L = lua_gettop(L); \ int top_##result_name##_L = lua_gettop(L); \
GET_TOLUA_STUFF(result_name, top_##name##_L, type); \ GET_TOLUA_STUFF(result_name, top_##result_name##_L, type); \
lua_pop(L, 2); /* unsafe, metatable */ 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: // vim: set noet ts=4 sw=4:

View File

@ -4,6 +4,7 @@
#include "interface/voxel.h" #include "interface/voxel.h"
#include "lua_bindings/util.h" #include "lua_bindings/util.h"
#include "lua_bindings/luabind_util.h" #include "lua_bindings/luabind_util.h"
#include "lua_bindings/sandbox_util.h"
#include <luabind/luabind.hpp> #include <luabind/luabind.hpp>
#include <luabind/adopt_policy.hpp> #include <luabind/adopt_policy.hpp>
#include <luabind/object.hpp> #include <luabind/object.hpp>
@ -14,15 +15,6 @@
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#define MODULE "lua_bindings" #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 namespace interface;
using Urho3D::IntVector2; using Urho3D::IntVector2;
@ -40,28 +32,8 @@ luabind::object asd_get_total_segments(
void asd_set_total_segments( void asd_set_total_segments(
AtlasSegmentDefinition &def, luabind::object value_safe, lua_State *L) AtlasSegmentDefinition &def, luabind::object value_safe, lua_State *L)
{ {
// This won't work GET_SANDBOX_STUFF(v, 2, IntVector2);
/*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);
def.total_segments = *v; def.total_segments = *v;
lua_pop(L, 2); // unsafe, metatable
} }
luabind::object asd_get_select_segment( luabind::object asd_get_select_segment(
@ -74,18 +46,8 @@ luabind::object asd_get_select_segment(
void asd_set_select_segment( void asd_set_select_segment(
AtlasSegmentDefinition &def, luabind::object value, lua_State *L) AtlasSegmentDefinition &def, luabind::object value, lua_State *L)
{ {
lua_getmetatable(L, 2); GET_SANDBOX_STUFF(v, 2, IntVector2);
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);
def.select_segment = *v; def.select_segment = *v;
lua_pop(L, 2); // unsafe, metatable
} }
luabind::object vdef_get_textures(const VoxelDefinition &def, lua_State *L) luabind::object vdef_get_textures(const VoxelDefinition &def, lua_State *L)

View 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: