From b9b47ee6d943314d2d79bb988fef24ef4e34c145 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sun, 19 Oct 2014 23:39:35 +0300 Subject: [PATCH] WIP --- builtin/voxelworld/client_lua/module.lua | 25 +++ builtin/voxelworld/voxelworld.cpp | 22 ++- client/api.lua | 17 +++ extensions/urho3d/safe_classes.lua | 11 +- games/digger/main/client_lua/init.lua | 184 ++++++++++++++--------- games/digger/main/main.cpp | 7 +- src/impl/mesh.cpp | 25 +++ src/impl/voxel_volume.cpp | 100 ++++++++++++ src/interface/mesh.h | 5 + src/interface/voxel_volume.h | 5 +- src/lua_bindings/mesh.cpp | 18 ++- 11 files changed, 346 insertions(+), 73 deletions(-) diff --git a/builtin/voxelworld/client_lua/module.lua b/builtin/voxelworld/client_lua/module.lua index 77e2d82..10b067d 100644 --- a/builtin/voxelworld/client_lua/module.lua +++ b/builtin/voxelworld/client_lua/module.lua @@ -127,6 +127,31 @@ 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 + end + local near_trigger_d = nil local near_weight = nil local far_trigger_d = nil diff --git a/builtin/voxelworld/voxelworld.cpp b/builtin/voxelworld/voxelworld.cpp index 2e0ee02..a8ada96 100644 --- a/builtin/voxelworld/voxelworld.cpp +++ b/builtin/voxelworld/voxelworld.cpp @@ -344,9 +344,10 @@ struct Module: public interface::Module, public voxelworld::Interface // TODO: Load from disk or something //pv::Region region(-1, 0, -1, 1, 0, 1); - pv::Region region(-1, -1, -1, 1, 1, 1); + //pv::Region region(-1, -1, -1, 1, 1, 1); //pv::Region region(-2, -1, -2, 2, 1, 2); //pv::Region region(-3, -1, -3, 3, 1, 3); + pv::Region region(-5, -1, 0, 0, 1, 5); //pv::Region region(-5, -1, -5, 5, 1, 5); //pv::Region region(-6, -1, -6, 6, 1, 6); //pv::Region region(-8, -1, -8, 8, 1, 8); @@ -624,6 +625,25 @@ struct Module: public interface::Module, public voxelworld::Interface n->SetVar(StringHash("buildat_voxel_data"), Variant( PODVector((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 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((const uint8_t*)data.c_str(), + data.size()))); + } + // There are no collision shapes initially, but add the rigid body now RigidBody *body = n->CreateComponent(LOCAL); body->SetFriction(0.75f); diff --git a/client/api.lua b/client/api.lua index 851b376..19a89ab 100644 --- a/client/api.lua +++ b/client/api.lua @@ -63,6 +63,9 @@ buildat.safe.SpatialUpdateQueue = function() } end +-- TODO: Implement sandbox unwrapping in C++ and remove these from here +-- (already done in lua_bindings/voxel.cpp) + function buildat.safe.set_simple_voxel_model(safe_node, w, h, d, safe_buffer) if not getmetatable(safe_node) or getmetatable(safe_node).type_name ~= "Node" then @@ -184,6 +187,20 @@ function buildat.safe.clear_voxel_physics_boxes(safe_node) __buildat_clear_voxel_physics_boxes(node) end +function buildat.safe.voxel_heuristic_has_sunlight(safe_buffer, ...) + buffer = nil + if type(safe_buffer) == 'string' then + buffer = safe_buffer + else + if not getmetatable(safe_buffer) or + getmetatable(safe_buffer).type_name ~= "VectorBuffer" then + error("safe_buffer is not a sandboxed VectorBuffer instance") + end + buffer = getmetatable(safe_buffer).unsafe + end + return __buildat_voxel_heuristic_has_sunlight(buffer, ...) +end + local Vector3_prototype = { x = 0, y = 0, diff --git a/extensions/urho3d/safe_classes.lua b/extensions/urho3d/safe_classes.lua index 87a384d..adc2800 100644 --- a/extensions/urho3d/safe_classes.lua +++ b/extensions/urho3d/safe_classes.lua @@ -235,8 +235,9 @@ function M.define(dst, util) }) util.wc("BoundingBox", { - unsafe_constructor = util.wrap_function({"number", "number"}, - function(min, max) -- TOOD: Many alternative constructors + unsafe_constructor = util.wrap_function({ + {"number", "Vector3"}, {"number", "Vector3"}}, + function(min, max) return util.wrap_instance("BoundingBox", BoundingBox(min, max)) end), instance_meta = { @@ -285,6 +286,8 @@ function M.define(dst, util) shadowBias = util.simple_property("BiasParameters"), shadowCascade = util.simple_property("CascadeParameters"), color = util.simple_property("Color"), + range = util.simple_property("number"), + fadeDistance = util.simple_property("number"), }, }) @@ -335,6 +338,10 @@ function M.define(dst, util) fogColor = util.simple_property(dst.Color), fogStart = util.simple_property("number"), fogEnd = util.simple_property("number"), + priority = util.simple_property("number"), + heightFog = util.simple_property("boolean"), + override = util.simple_property("boolean"), + ambientGradient = util.simple_property("boolean"), }, }) diff --git a/games/digger/main/client_lua/init.lua b/games/digger/main/client_lua/init.lua index 1b80640..504dd18 100644 --- a/games/digger/main/client_lua/init.lua +++ b/games/digger/main/client_lua/init.lua @@ -27,94 +27,144 @@ local scene = replicate.main_scene magic.input:SetMouseVisible(false) -- Set up zone (global visual parameters) -local zone_node = scene:CreateChild("Zone") -local zone = zone_node:CreateComponent("Zone") -zone.boundingBox = magic.BoundingBox(-1000, 1000) ---zone.ambientColor = magic.Color(0.15, 0.15, 0.15) -zone.ambientColor = magic.Color(0.1, 0.1, 0.1) ---zone.ambientColor = magic.Color(0, 0, 0) -zone.fogColor = magic.Color(0.6, 0.7, 0.8) -zone.fogStart = 10 -zone.fogEnd = FOG_END +--[[ +do + local zone_node = scene:CreateChild("Zone") + local zone = zone_node:CreateComponent("Zone") + zone.boundingBox = magic.BoundingBox(-1000, 1000) + zone.ambientColor = magic.Color(0.1, 0.1, 0.1) + --zone.ambientColor = magic.Color(0, 0, 0) + zone.fogColor = magic.Color(0.6, 0.7, 0.8) + --zone.fogColor = magic.Color(0, 0, 0) + zone.fogStart = 10 + zone.fogEnd = FOG_END +end +--]] -- Add lights ---local node = scene:CreateChild("DirectionalLight") ---node.direction = magic.Vector3(0.0, -1.0, 0.0) ---local light = node:CreateComponent("Light") ---light.lightType = magic.LIGHT_DIRECTIONAL ---light.castShadows = true ---light.brightness = 0.1 ---light.color = magic.Color(1.0, 1.0, 1.0) +do + --[[ + local dirs = { + magic.Vector3( 1.0, -1.0, 1.0), + magic.Vector3( 1.0, -1.0, -1.0), + magic.Vector3(-1.0, -1.0, -1.0), + magic.Vector3(-1.0, -1.0, 1.0), + } + for _, dir in ipairs(dirs) do + local node = scene:CreateChild("DirectionalLight") + node.direction = dir + local light = node:CreateComponent("Light") + light.lightType = magic.LIGHT_DIRECTIONAL + light.castShadows = true + light.brightness = 0.2 + light.color = magic.Color(0.7, 0.7, 1.0) + end + --]] -local node = scene:CreateChild("DirectionalLight") -node.direction = magic.Vector3(-0.6, -1.0, 0.8) -local light = node:CreateComponent("Light") -light.lightType = magic.LIGHT_DIRECTIONAL -light.castShadows = true -light.brightness = 0.8 -light.color = magic.Color(1.0, 1.0, 0.95) + local node = scene:CreateChild("DirectionalLight") + node.direction = magic.Vector3(-0.6, -1.0, 0.8) + local light = node:CreateComponent("Light") + light.lightType = magic.LIGHT_DIRECTIONAL + light.castShadows = true + light.brightness = 0.8 + light.color = magic.Color(1.0, 1.0, 0.95) -local node = scene:CreateChild("DirectionalLight") -node.direction = magic.Vector3(0.3, -1.0, -0.4) -local light = node:CreateComponent("Light") -light.lightType = magic.LIGHT_DIRECTIONAL -light.castShadows = true -light.brightness = 0.2 -light.color = magic.Color(0.7, 0.7, 1.0) + ---[[ + local node = scene:CreateChild("DirectionalLight") + node.direction = magic.Vector3(0.3, -1.0, -0.4) + local light = node:CreateComponent("Light") + light.lightType = magic.LIGHT_DIRECTIONAL + light.castShadows = true + light.brightness = 0.2 + light.color = magic.Color(0.7, 0.7, 1.0) + --]] + + --[[ + local node = scene:CreateChild("DirectionalLight") + node.direction = magic.Vector3(0.0, -1.0, 0.0) + local light = node:CreateComponent("Light") + light.lightType = magic.LIGHT_DIRECTIONAL + light.castShadows = false + light.brightness = 0.05 + light.color = magic.Color(1.0, 1.0, 1.0) + --]] +end -- Add a node that the player can use to walk around with local player_node = scene:CreateChild("Player") ---player_node.position = magic.Vector3(0, 30, 0) -player_node.position = magic.Vector3(55, 30, 40) -player_node.direction = magic.Vector3(-1, 0, 0.4) ---player_node:Yaw(-177.49858) ----[[ -local body = player_node:CreateComponent("RigidBody") ---body.mass = 70.0 -body.friction = 0 ---body.linearVelocity = magic.Vector3(0, -10, 0) -body.angularFactor = magic.Vector3(0, 0, 0) -body.gravityOverride = magic.Vector3(0, -15.0, 0) -- A bit more than normally -local shape = player_node:CreateComponent("CollisionShape") ---shape:SetBox(magic.Vector3(1, 1.7*PLAYER_SCALE, 1)) -shape:SetCapsule(PLAYER_WIDTH, PLAYER_HEIGHT) ---]] +local player_shape = player_node:CreateComponent("CollisionShape") +do + --player_node.position = magic.Vector3(0, 30, 0) + --player_node.position = magic.Vector3(55, 30, 40) + player_node.position = magic.Vector3(-5, 1, 257) + player_node.direction = magic.Vector3(-1, 0, 0.4) + --player_node:Yaw(-177.49858) + ---[[ + local body = player_node:CreateComponent("RigidBody") + --body.mass = 70.0 + body.friction = 0 + --body.linearVelocity = magic.Vector3(0, -10, 0) + body.angularFactor = magic.Vector3(0, 0, 0) + body.gravityOverride = magic.Vector3(0, -15.0, 0) -- A bit more than normally + --player_shape:SetBox(magic.Vector3(1, 1.7*PLAYER_SCALE, 1)) + player_shape:SetCapsule(PLAYER_WIDTH, PLAYER_HEIGHT) + --]] +end local player_touches_ground = false local player_crouched = false -- Add a camera so we can look at the scene local camera_node = player_node:CreateChild("Camera") -camera_node.position = magic.Vector3(0, 0.411*PLAYER_HEIGHT, 0) ---camera_node:Pitch(13.60000) -local camera = camera_node:CreateComponent("Camera") -camera.nearClip = 0.3 -camera.farClip = RENDER_DISTANCE -camera.fov = 75 +do + camera_node.position = magic.Vector3(0, 0.411*PLAYER_HEIGHT, 0) + --camera_node:Pitch(13.60000) + local camera = camera_node:CreateComponent("Camera") + camera.nearClip = 0.3 + camera.farClip = RENDER_DISTANCE + camera.fov = 75 --- And this thing so the camera is shown on the screen -local viewport = magic.Viewport:new(scene, camera_node:GetComponent("Camera")) -magic.renderer:SetViewport(0, viewport) + -- And this thing so the camera is shown on the screen + local viewport = magic.Viewport:new(scene, camera_node:GetComponent("Camera")) + magic.renderer:SetViewport(0, viewport) +end -- Tell about the camera to the voxel world so it can do stuff based on the -- camera's position and other properties voxelworld.set_camera(camera_node) +--[[ +-- Add a light to the camera +do + local node = camera_node:CreateChild("Light") + local light = node:CreateComponent("Light") + light.lightType = magic.LIGHT_POINT + light.castShadows = false + light.brightness = 0.15 + light.color = magic.Color(1.0, 1.0, 0.8) + light.range = 10.0 + light.fadeDistance = 10.0 +end +--]] + -- Add some text local title_text = magic.ui.root:CreateChild("Text") -title_text:SetText("digger/init.lua") -title_text:SetFont(magic.cache:GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15) -title_text.horizontalAlignment = magic.HA_CENTER -title_text.verticalAlignment = magic.VA_CENTER -title_text:SetPosition(0, -magic.ui.root.height/2 + 20) - local misc_text = magic.ui.root:CreateChild("Text") -misc_text:SetText("") -misc_text:SetFont(magic.cache:GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15) -misc_text.horizontalAlignment = magic.HA_CENTER -misc_text.verticalAlignment = magic.VA_CENTER -misc_text:SetPosition(0, -magic.ui.root.height/2 + 40) +do + title_text:SetText("digger/init.lua") + title_text:SetFont(magic.cache:GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15) + title_text.horizontalAlignment = magic.HA_CENTER + title_text.verticalAlignment = magic.VA_CENTER + title_text:SetPosition(0, -magic.ui.root.height/2 + 20) + misc_text:SetText("") + misc_text:SetFont(magic.cache:GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15) + misc_text.horizontalAlignment = magic.HA_CENTER + misc_text.verticalAlignment = magic.VA_CENTER + misc_text:SetPosition(0, -magic.ui.root.height/2 + 40) +end + +-- Unfocus UI magic.ui:SetFocusElement(nil) magic.SubscribeToEvent("KeyDown", function(event_type, event_data) @@ -210,13 +260,13 @@ magic.SubscribeToEvent("Update", function(event_type, event_data) end if not player_crouched then - shape:SetCapsule(PLAYER_WIDTH, PLAYER_HEIGHT/2) + player_shape:SetCapsule(PLAYER_WIDTH, PLAYER_HEIGHT/2) camera_node.position = magic.Vector3(0, 0.411*PLAYER_HEIGHT/2, 0) player_crouched = true end else if player_crouched then - shape:SetCapsule(PLAYER_WIDTH, PLAYER_HEIGHT) + player_shape:SetCapsule(PLAYER_WIDTH, PLAYER_HEIGHT) player_node:Translate(magic.Vector3(0, PLAYER_HEIGHT/4, 0)) camera_node.position = magic.Vector3(0, 0.411*PLAYER_HEIGHT, 0) player_crouched = false diff --git a/games/digger/main/main.cpp b/games/digger/main/main.cpp index 5c172a2..ad59476 100644 --- a/games/digger/main/main.cpp +++ b/games/digger/main/main.cpp @@ -321,6 +321,11 @@ struct Module: public interface::Module ivoxelworld->set_voxel(p, VoxelInstance(1)); continue; } + if(y >= 2 && y <= 3 && z >= 256 && z <= 258 && + x >= -112 && x <= -5){ + ivoxelworld->set_voxel(p, VoxelInstance(1)); + continue; + } if(z > 37 && z < 50 && y > 20){ ivoxelworld->set_voxel(p, VoxelInstance(1)); continue; @@ -424,7 +429,7 @@ struct Module: public interface::Module for(int y = lc.getY(); y <= uc.getY(); y++){ for(int x = lc.getX(); x <= uc.getX(); x++){ ivoxelworld->set_voxel( - voxel_p + pv::Vector3DInt32(x, y, z), v); + voxel_p + pv::Vector3DInt32(x, y, z), v); } } } diff --git a/src/impl/mesh.cpp b/src/impl/mesh.cpp index 174335c..156b27c 100644 --- a/src/impl/mesh.cpp +++ b/src/impl/mesh.cpp @@ -995,6 +995,31 @@ void set_voxel_physics_boxes(Node *node, Context *context, set_voxel_physics_boxes(node, context, result_boxes, true); } +bool voxel_heuristic_has_sunlight(pv::RawVolume &volume, + VoxelRegistry *voxel_reg) +{ + auto region = volume.getEnclosingRegion(); + auto &lc = region.getLowerCorner(); + auto &uc = region.getUpperCorner(); + + int num = 0; + int y = uc.getY()-1; + for(int z = lc.getZ()+1; z <= uc.getZ()-1; z++){ + for(int x = lc.getX()+1; x <= uc.getX()-1; x++){ + VoxelInstance v = volume.getVoxelAt(x, y, z); + const interface::CachedVoxelDefinition *def = voxel_reg->get_cached(v); + if(!def) + throw Exception(ss_()+"Undefined voxel: "+itos(v.getId())); + // TODO: Some proper lighting property + bool light_passes = (!def || !def->physically_solid); + if(light_passes) + num++; + } + } + log_w(MODULE, "num=%i", num); + return (num >= 3); +} + } // namespace mesh } // namespace interface // vim: set noet ts=4 sw=4: diff --git a/src/impl/voxel_volume.cpp b/src/impl/voxel_volume.cpp index a9ab796..ab0c826 100644 --- a/src/impl/voxel_volume.cpp +++ b/src/impl/voxel_volume.cpp @@ -9,6 +9,8 @@ namespace interface { +// pv::RawVolume + ss_ serialize_volume_simple(const pv::RawVolume &volume) { std::ostringstream os(std::ios::binary); @@ -105,5 +107,103 @@ up_> deserialize_volume(const ss_ &data) return up_>(); } +// pv::RawVolume + +ss_ serialize_volume_simple(const pv::RawVolume &volume) +{ + std::ostringstream os(std::ios::binary); + { + cereal::PortableBinaryOutputArchive ar(os); + ar((uint8_t)2); // Format + ar((int32_t)volume.getWidth()); + ar((int32_t)volume.getHeight()); + ar((int32_t)volume.getDepth()); + auto region = volume.getEnclosingRegion(); + auto lc = region.getLowerCorner(); + auto uc = region.getUpperCorner(); + for(size_t i = 0; i &volume) +{ + std::ostringstream os(std::ios::binary); + { + cereal::PortableBinaryOutputArchive ar(os); + ar((uint8_t)3); // Format + ar((int32_t)volume.getWidth()); + ar((int32_t)volume.getHeight()); + ar((int32_t)volume.getDepth()); + std::ostringstream raw_os(std::ios::binary); + { + cereal::PortableBinaryOutputArchive ar(raw_os); + auto region = volume.getEnclosingRegion(); + auto lc = region.getLowerCorner(); + auto uc = region.getUpperCorner(); + for(size_t i = 0; i> deserialize_volume_uint8(const ss_ &data) +{ + std::istringstream is(data, std::ios::binary); + cereal::PortableBinaryInputArchive ar(is); + int8_t format = 0; + ar(format); + if(format == 2){ + int32_t w = 0; + int32_t h = 0; + int32_t d = 0; + ar(w, h, d); + pv::Region region(0, 0, 0, w-1, h-1, d-1); + up_> volume( + new pv::RawVolume(region)); + for(size_t i = 0; im_dataSize; i++){ + uint8_t v; + ar(v); + volume->m_pData[i] = v; + } + return volume; + } + if(format == 3){ + int32_t w = 0; + int32_t h = 0; + int32_t d = 0; + ar(w, h, d); + pv::Region region(0, 0, 0, w-1, h-1, d-1); + up_> volume( + new pv::RawVolume(region)); + ss_ compressed_data; + ar(compressed_data); + std::istringstream compressed_is(compressed_data, std::ios::binary); + std::ostringstream raw_os(std::ios::binary); + decompress_zlib(compressed_is, raw_os); + { + std::istringstream raw_is(raw_os.str(), std::ios::binary); + cereal::PortableBinaryInputArchive ar(raw_is); + for(size_t i = 0; im_dataSize; i++){ + uint8_t v; + ar(v); + volume->m_pData[i] = v; + } + } + return volume; + } + return up_>(); +} + } // vim: set noet ts=4 sw=4: diff --git a/src/interface/mesh.h b/src/interface/mesh.h index c08280c..375f205 100644 --- a/src/interface/mesh.h +++ b/src/interface/mesh.h @@ -127,6 +127,11 @@ namespace interface void set_voxel_physics_boxes(Node *node, Context *context, pv::RawVolume &volume, VoxelRegistry *voxel_reg); + + // Heuristics + + bool voxel_heuristic_has_sunlight(pv::RawVolume &volume, + VoxelRegistry *voxel_reg); } } // vim: set noet ts=4 sw=4: diff --git a/src/interface/voxel_volume.h b/src/interface/voxel_volume.h index 7518fa5..2240776 100644 --- a/src/interface/voxel_volume.h +++ b/src/interface/voxel_volume.h @@ -9,7 +9,10 @@ namespace interface { ss_ serialize_volume_simple(const pv::RawVolume &volume); ss_ serialize_volume_compressed(const pv::RawVolume &volume); - up_> deserialize_volume(const ss_ &data); + + ss_ serialize_volume_simple(const pv::RawVolume &volume); + ss_ serialize_volume_compressed(const pv::RawVolume &volume); + up_> deserialize_volume_8bit(const ss_ &data); } // vim: set noet ts=4 sw=4: diff --git a/src/lua_bindings/mesh.cpp b/src/lua_bindings/mesh.cpp index d433180..4e9715b 100644 --- a/src/lua_bindings/mesh.cpp +++ b/src/lua_bindings/mesh.cpp @@ -442,6 +442,21 @@ void clear_voxel_physics_boxes(const luabind::object &node_o) node->RemoveComponent(previous_shapes[i]); } +bool voxel_heuristic_has_sunlight(const luabind::object &buffer_o, + sp_ voxel_reg, lua_State *L) +{ + TRY_GET_TOLUA_STUFF(buf, 1, const VectorBuffer); + + ss_ data; + if(buf == nullptr) + data = lua_tocppstring(L, 2); + else + data.assign((const char*)&buf->GetBuffer()[0], buf->GetBuffer().Size()); + + up_> volume = interface::deserialize_volume(data); + return interface::mesh::voxel_heuristic_has_sunlight(*volume, voxel_reg.get()); +} + #define LUABIND_FUNC(name) def("__buildat_" #name, name) void init_mesh(lua_State *L) @@ -454,7 +469,8 @@ void init_mesh(lua_State *L) LUABIND_FUNC(set_voxel_lod_geometry), LUABIND_FUNC(clear_voxel_geometry), LUABIND_FUNC(set_voxel_physics_boxes), - LUABIND_FUNC(clear_voxel_physics_boxes) + LUABIND_FUNC(clear_voxel_physics_boxes), + LUABIND_FUNC(voxel_heuristic_has_sunlight) ]; }