interface/mesh: set_8bit_voxel_geometry() - Definition-based voxel geometry generation with textures, utilizing texture atlases
This commit is contained in:
parent
fe57e5b127
commit
3530493f84
@ -17,4 +17,13 @@ function buildat.safe.set_simple_voxel_model(safe_node, w, h, d, data)
|
||||
__buildat_set_simple_voxel_model(node, w, h, d, data)
|
||||
end
|
||||
|
||||
function buildat.safe.set_8bit_voxel_geometry(safe_node, w, h, d, data)
|
||||
if not getmetatable(safe_node) or
|
||||
getmetatable(safe_node).type_name ~= "Node" then
|
||||
error("Node is not a sandboxed Node instance")
|
||||
end
|
||||
node = getmetatable(safe_node).unsafe
|
||||
__buildat_set_8bit_voxel_geometry(node, w, h, d, data)
|
||||
end
|
||||
|
||||
-- vim: set noet ts=4 sw=4:
|
||||
|
64
games/geometry2/__loader/__loader.cpp
Normal file
64
games/geometry2/__loader/__loader.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
#include "core/log.h"
|
||||
#include "interface/module.h"
|
||||
#include "interface/module_info.h"
|
||||
#include "interface/server.h"
|
||||
#include "interface/fs.h"
|
||||
#include "interface/event.h"
|
||||
#include "loader/api.h"
|
||||
|
||||
using interface::Event;
|
||||
|
||||
namespace __loader {
|
||||
|
||||
struct Module: public interface::Module
|
||||
{
|
||||
interface::Server *m_server;
|
||||
|
||||
Module(interface::Server *server):
|
||||
interface::Module("__loader"),
|
||||
m_server(server)
|
||||
{
|
||||
log_v(MODULE, "__loader construct");
|
||||
}
|
||||
|
||||
~Module()
|
||||
{
|
||||
log_v(MODULE, "__loader destruct");
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
log_v(MODULE, "__loader init");
|
||||
m_server->sub_event(this, Event::t("core:load_modules"));
|
||||
}
|
||||
|
||||
void event(const Event::Type &type, const Event::Private *p)
|
||||
{
|
||||
EVENT_VOIDN("core:load_modules", on_load_modules)
|
||||
}
|
||||
|
||||
void on_load_modules()
|
||||
{
|
||||
interface::ModuleInfo info;
|
||||
info.name = "loader";
|
||||
info.path = m_server->get_builtin_modules_path()+"/"+info.name;
|
||||
|
||||
bool ok = m_server->load_module(info);
|
||||
if(!ok){
|
||||
m_server->shutdown(1, "Error loading builtin/loader");
|
||||
return;
|
||||
}
|
||||
|
||||
loader::access(m_server, [&](loader::Interface *i){
|
||||
i->activate();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
BUILDAT_EXPORT void* createModule___loader(interface::Server *server){
|
||||
return (void*)(new Module(server));
|
||||
}
|
||||
}
|
||||
}
|
||||
// vim: set noet ts=4 sw=4:
|
2
games/geometry2/__loader/meta.json
Normal file
2
games/geometry2/__loader/meta.json
Normal file
@ -0,0 +1,2 @@
|
||||
{
|
||||
}
|
BIN
games/geometry2/main/client_data/green_texture.png
Normal file
BIN
games/geometry2/main/client_data/green_texture.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 608 B |
BIN
games/geometry2/main/client_data/pink_texture.png
Normal file
BIN
games/geometry2/main/client_data/pink_texture.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
80
games/geometry2/main/client_lua/init.lua
Normal file
80
games/geometry2/main/client_lua/init.lua
Normal file
@ -0,0 +1,80 @@
|
||||
-- Buildat: geometry2/client_lua/init.lua
|
||||
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||
-- Copyright 2014 Perttu Ahola <celeron55@gmail.com>
|
||||
-- Copyright 2014 Břetislav Štec <valsiterb@gmail.com>
|
||||
local log = buildat.Logger("geometry2")
|
||||
local dump = buildat.dump
|
||||
local cereal = require("buildat/extension/cereal")
|
||||
local magic = require("buildat/extension/urho3d")
|
||||
local replicate = require("buildat/extension/replicate")
|
||||
|
||||
local scene = replicate.main_scene
|
||||
|
||||
-- Add a camera so we can look at the scene
|
||||
local camera_node = scene:CreateChild("Camera")
|
||||
camera_node:CreateComponent("Camera")
|
||||
camera_node.position = magic.Vector3(7.0, 7.0, 7.0)
|
||||
camera_node:LookAt(magic.Vector3(0, 1, 0))
|
||||
|
||||
-- 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)
|
||||
|
||||
-- Add some text
|
||||
local title_text = magic.ui.root:CreateChild("Text")
|
||||
title_text:SetText("geometry2/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*(-0.33))
|
||||
|
||||
magic.ui:SetFocusElement(nil)
|
||||
|
||||
function handle_keydown(event_type, event_data)
|
||||
local key = event_data:GetInt("Key")
|
||||
if key == magic.KEY_ESC then
|
||||
log:info("KEY_ESC pressed")
|
||||
buildat.disconnect()
|
||||
end
|
||||
end
|
||||
magic.SubscribeToEvent("KeyDown", "handle_keydown")
|
||||
|
||||
function setup_simple_voxel_data(node)
|
||||
--buildat.set_simple_voxel_model(node, 3, 3, 3, "010111010111111111010111010")
|
||||
--node:SetScale(magic.Vector3(0.5, 0.5, 0.5))
|
||||
--buildat.set_simple_voxel_model(node, 2, 2, 2, "11101111")
|
||||
--node:SetScale(magic.Vector3(1, 1, 1))
|
||||
--buildat.set_simple_voxel_model(node, 1, 1, 1, "1")
|
||||
--node:SetScale(magic.Vector3(2, 2, 2))
|
||||
|
||||
-- Should be something like "11101111"
|
||||
local data = node:GetVar("simple_voxel_data"):GetString()
|
||||
local w = node:GetVar("simple_voxel_w"):GetInt()
|
||||
local h = node:GetVar("simple_voxel_h"):GetInt()
|
||||
local d = node:GetVar("simple_voxel_d"):GetInt()
|
||||
log:info(dump(node:GetName()).." voxel data size: "..#data)
|
||||
buildat.set_8bit_voxel_geometry(node, w, h, d, data)
|
||||
node:SetScale(magic.Vector3(1, 1, 1))
|
||||
|
||||
--local object = node:GetComponent("StaticModel")
|
||||
--object.castShadows = true
|
||||
end
|
||||
|
||||
magic.sub_sync_node_added({}, function(node)
|
||||
if not node:GetVar("simple_voxel_data"):IsEmpty() then
|
||||
setup_simple_voxel_data(node)
|
||||
end
|
||||
local name = node:GetName()
|
||||
--[[if name == "Testbox" then
|
||||
local object = node:CreateComponent("StaticModel")
|
||||
object.model = magic.cache:GetResource("Model", "Models/Box.mdl")
|
||||
object.material = magic.Material:new()
|
||||
object.material:SetTechnique(0,
|
||||
magic.cache:GetResource("Technique", "Techniques/Diff.xml"))
|
||||
object.material:SetTexture(magic.TU_DIFFUSE,
|
||||
magic.cache:GetResource("Texture2D", "main/pink_texture.png"))
|
||||
object.castShadows = true
|
||||
end]]
|
||||
end)
|
||||
|
||||
-- vim: set noet ts=4 sw=4:
|
276
games/geometry2/main/main.cpp
Normal file
276
games/geometry2/main/main.cpp
Normal file
@ -0,0 +1,276 @@
|
||||
#include "core/log.h"
|
||||
#include "client_file/api.h"
|
||||
#include "network/api.h"
|
||||
#include "replicate/api.h"
|
||||
#include "interface/module.h"
|
||||
#include "interface/server.h"
|
||||
#include "interface/event.h"
|
||||
#include "interface/mesh.h"
|
||||
#include "interface/voxel.h"
|
||||
#include <Scene.h>
|
||||
#include <RigidBody.h>
|
||||
#include <CollisionShape.h>
|
||||
#include <ResourceCache.h>
|
||||
#include <Context.h>
|
||||
#include <StaticModel.h>
|
||||
#include <Model.h>
|
||||
#include <Material.h>
|
||||
#include <Texture2D.h>
|
||||
#include <Technique.h>
|
||||
#include <cereal/archives/portable_binary.hpp>
|
||||
#include <cereal/types/unordered_map.hpp>
|
||||
#include <cereal/types/vector.hpp>
|
||||
|
||||
namespace geometry {
|
||||
|
||||
using interface::Event;
|
||||
using namespace Urho3D;
|
||||
namespace magic = Urho3D;
|
||||
|
||||
struct Module: public interface::Module
|
||||
{
|
||||
interface::Server *m_server;
|
||||
uint m_slow_count = 0;
|
||||
sp_<interface::TextureAtlasRegistry> m_atlas_reg;
|
||||
sp_<interface::VoxelRegistry> m_voxel_reg;
|
||||
|
||||
Module(interface::Server *server):
|
||||
interface::Module("geometry"),
|
||||
m_server(server)
|
||||
{
|
||||
}
|
||||
|
||||
~Module()
|
||||
{}
|
||||
|
||||
void init()
|
||||
{
|
||||
m_server->sub_event(this, Event::t("core:start"));
|
||||
m_server->sub_event(this, Event::t("core:continue"));
|
||||
m_server->sub_event(this, Event::t("core:tick"));
|
||||
m_server->sub_event(this, Event::t("client_file:files_transmitted"));
|
||||
|
||||
m_server->access_scene([&](Scene *scene)
|
||||
{
|
||||
Context *context = scene->GetContext();
|
||||
m_atlas_reg.reset(interface::createTextureAtlasRegistry(context));
|
||||
m_voxel_reg.reset(interface::createVoxelRegistry(m_atlas_reg.get()));
|
||||
{
|
||||
interface::VoxelDefinition vdef;
|
||||
vdef.name.block_name = "air";
|
||||
vdef.name.segment_x = 0;
|
||||
vdef.name.segment_y = 0;
|
||||
vdef.name.segment_z = 0;
|
||||
vdef.name.rotation_primary = 0;
|
||||
vdef.name.rotation_secondary = 0;
|
||||
vdef.handler_module = "";
|
||||
for(size_t i = 0; i < 6; i++){
|
||||
interface::AtlasSegmentDefinition &seg = vdef.textures[i];
|
||||
seg.resource_name = "";
|
||||
seg.total_segments = magic::IntVector2(0, 0);
|
||||
seg.select_segment = magic::IntVector2(0, 0);
|
||||
}
|
||||
vdef.edge_material_id = interface::EDGEMATERIALID_EMPTY;
|
||||
m_voxel_reg->add_voxel(vdef); // id 1
|
||||
}
|
||||
{
|
||||
interface::VoxelDefinition vdef;
|
||||
vdef.name.block_name = "ground";
|
||||
vdef.name.segment_x = 0;
|
||||
vdef.name.segment_y = 0;
|
||||
vdef.name.segment_z = 0;
|
||||
vdef.name.rotation_primary = 0;
|
||||
vdef.name.rotation_secondary = 0;
|
||||
vdef.handler_module = "";
|
||||
for(size_t i = 0; i < 6; i++){
|
||||
interface::AtlasSegmentDefinition &seg = vdef.textures[i];
|
||||
seg.resource_name = "main/green_texture.png";
|
||||
seg.total_segments = magic::IntVector2(1, 1);
|
||||
seg.select_segment = magic::IntVector2(0, 0);
|
||||
}
|
||||
vdef.edge_material_id = interface::EDGEMATERIALID_GROUND;
|
||||
vdef.physically_solid = true;
|
||||
m_voxel_reg->add_voxel(vdef); // id 2
|
||||
}
|
||||
{
|
||||
interface::VoxelDefinition vdef;
|
||||
vdef.name.block_name = "testblock";
|
||||
vdef.name.segment_x = 0;
|
||||
vdef.name.segment_y = 0;
|
||||
vdef.name.segment_z = 0;
|
||||
vdef.name.rotation_primary = 0;
|
||||
vdef.name.rotation_secondary = 0;
|
||||
vdef.handler_module = "";
|
||||
for(size_t i = 0; i < 6; i++){
|
||||
interface::AtlasSegmentDefinition &seg = vdef.textures[i];
|
||||
seg.resource_name = "main/pink_texture.png";
|
||||
seg.total_segments = magic::IntVector2(1, 1);
|
||||
seg.select_segment = magic::IntVector2(0, 0);
|
||||
}
|
||||
vdef.edge_material_id = interface::EDGEMATERIALID_GROUND;
|
||||
vdef.physically_solid = true;
|
||||
m_voxel_reg->add_voxel(vdef); // id 3
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void event(const Event::Type &type, const Event::Private *p)
|
||||
{
|
||||
EVENT_VOIDN("core:start", on_start)
|
||||
EVENT_VOIDN("core:continue", on_continue)
|
||||
EVENT_TYPEN("core:tick", on_tick, interface::TickEvent)
|
||||
EVENT_TYPEN("client_file:files_transmitted", on_files_transmitted,
|
||||
client_file::FilesTransmitted)
|
||||
}
|
||||
|
||||
void on_start()
|
||||
{
|
||||
m_server->access_scene([&](Scene *scene)
|
||||
{
|
||||
Context *context = scene->GetContext();
|
||||
ResourceCache *cache = context->GetSubsystem<ResourceCache>();
|
||||
|
||||
{
|
||||
Node *node = scene->CreateChild("DirectionalLight");
|
||||
node->SetDirection(Vector3(-0.6f, -1.0f, 0.8f));
|
||||
Light *light = node->CreateComponent<Light>();
|
||||
light->SetLightType(LIGHT_DIRECTIONAL);
|
||||
light->SetCastShadows(true);
|
||||
}
|
||||
{
|
||||
Node *n = scene->CreateChild("Base");
|
||||
}
|
||||
{
|
||||
Node *n = scene->CreateChild("Testbox");
|
||||
n->SetPosition(Vector3(0.0f, 6.0f, 0.0f));
|
||||
n->SetScale(Vector3(1.0f, 1.0f, 1.0f));
|
||||
|
||||
/*int w = 1, h = 1, d = 1;
|
||||
ss_ data = "1";*/
|
||||
int w = 2, h = 2, d = 1;
|
||||
ss_ data = "1333";
|
||||
|
||||
// Convert data to the actually usable voxel type id namespace
|
||||
// starting from VOXELTYPEID_UNDEFINED=0
|
||||
for(size_t i = 0; i < data.size(); i++){
|
||||
data[i] = data[i] - '0';
|
||||
}
|
||||
|
||||
// Crude way of dynamically defining a voxel model
|
||||
n->SetVar(StringHash("simple_voxel_data"), Variant(
|
||||
magic::String(data.c_str(), data.size())));
|
||||
n->SetVar(StringHash("simple_voxel_w"), Variant(w));
|
||||
n->SetVar(StringHash("simple_voxel_h"), Variant(h));
|
||||
n->SetVar(StringHash("simple_voxel_d"), Variant(d));
|
||||
|
||||
// Load the same model in here and give it to the physics
|
||||
// subsystem so that it can be collided to
|
||||
SharedPtr<Model> model(interface::
|
||||
create_8bit_voxel_physics_model(context, w, h, d, data,
|
||||
m_voxel_reg.get()));
|
||||
|
||||
RigidBody *body = n->CreateComponent<RigidBody>();
|
||||
body->SetFriction(0.75f);
|
||||
body->SetMass(1.0);
|
||||
CollisionShape *shape = n->CreateComponent<CollisionShape>();
|
||||
shape->SetConvexHull(model, 0, Vector3::ONE);
|
||||
//shape->SetTriangleMesh(model, 0, Vector3::ONE);
|
||||
//shape->SetBox(Vector3::ONE);
|
||||
}
|
||||
});
|
||||
|
||||
update_scene();
|
||||
}
|
||||
|
||||
void on_continue()
|
||||
{
|
||||
update_scene();
|
||||
}
|
||||
|
||||
void update_scene()
|
||||
{
|
||||
m_server->access_scene([&](Scene *scene)
|
||||
{
|
||||
Context *context = scene->GetContext();
|
||||
ResourceCache *cache = context->GetSubsystem<ResourceCache>();
|
||||
|
||||
{
|
||||
Node *n = scene->GetChild("Base");
|
||||
n->SetScale(Vector3(1.0f, 1.0f, 1.0f));
|
||||
//n->SetPosition(Vector3(0.0f, 0.5f, 0.0f));
|
||||
n->SetPosition(Vector3(0.0f, 0.5f, 0.0f));
|
||||
//n->SetRotation(Quaternion(0, 90, 0));
|
||||
|
||||
int w = 10, h = 3, d = 10;
|
||||
ss_ data =
|
||||
"222222222211211111211111111111"
|
||||
"222222222211111111111111111111"
|
||||
"222222222211111111111111111111"
|
||||
"222222222211111111111111111111"
|
||||
"222222222211122111111112111111"
|
||||
"222233222211123111111112111111"
|
||||
"222233222211111111111111111111"
|
||||
"222222222211111111111111111111"
|
||||
"222222222211111111111111111111"
|
||||
"222222222211111111111111111111"
|
||||
;
|
||||
|
||||
// Convert data to the actually usable voxel type id namespace
|
||||
// starting from VOXELTYPEID_UNDEFINED=0
|
||||
for(size_t i = 0; i < data.size(); i++){
|
||||
data[i] = data[i] - '0';
|
||||
}
|
||||
|
||||
// Crude way of dynamically defining a voxel model
|
||||
n->SetVar(StringHash("simple_voxel_data"), Variant(
|
||||
magic::String(data.c_str(), data.size())));
|
||||
n->SetVar(StringHash("simple_voxel_w"), Variant(w));
|
||||
n->SetVar(StringHash("simple_voxel_h"), Variant(h));
|
||||
n->SetVar(StringHash("simple_voxel_d"), Variant(d));
|
||||
|
||||
// Load the same model in here and give it to the physics
|
||||
// subsystem so that it can be collided to
|
||||
SharedPtr<Model> model(interface::
|
||||
create_8bit_voxel_physics_model(context, w, h, d, data,
|
||||
m_voxel_reg.get()));
|
||||
|
||||
RigidBody *body = n->CreateComponent<RigidBody>();
|
||||
body->SetFriction(0.75f);
|
||||
CollisionShape *shape = n->CreateComponent<CollisionShape>();
|
||||
shape->SetTriangleMesh(model, 0, Vector3::ONE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void on_tick(const interface::TickEvent &event)
|
||||
{
|
||||
static uint a = 0;
|
||||
if(((a++) % 50) == 0){
|
||||
m_server->access_scene([&](Scene *scene){
|
||||
Node *n = scene->GetChild("Testbox");
|
||||
//n->SetPosition(Vector3(0.0f, 8.0f, 0.0f));
|
||||
n->SetRotation(Quaternion(30, 60, 90));
|
||||
//n->SetPosition(Vector3(0.0f, 6.0f, 0.0f));
|
||||
//n->SetPosition(Vector3(0.0f, 4.0f, 0.0f));
|
||||
n->SetPosition(Vector3(-0.5f, 8.0f, 0.0f));
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void on_files_transmitted(const client_file::FilesTransmitted &event)
|
||||
{
|
||||
network::access(m_server, [&](network::Interface *inetwork){
|
||||
inetwork->send(event.recipient, "core:run_script",
|
||||
"buildat.run_script_file(\"main/init.lua\")");
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
BUILDAT_EXPORT void* createModule_main(interface::Server *server){
|
||||
return (void*)(new Module(server));
|
||||
}
|
||||
}
|
||||
}
|
||||
// vim: set noet ts=4 sw=4:
|
8
games/geometry2/main/meta.json
Normal file
8
games/geometry2/main/meta.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"dependencies": [
|
||||
{"module": "network"},
|
||||
{"module": "client_lua"},
|
||||
{"module": "client_data"},
|
||||
{"module": "replicate"}
|
||||
]
|
||||
}
|
@ -7,6 +7,7 @@
|
||||
#include "lua_bindings/init.h"
|
||||
#include "lua_bindings/util.h"
|
||||
#include "interface/fs.h"
|
||||
#include "interface/voxel.h"
|
||||
#include <c55/getopt.h>
|
||||
#include <c55/os.h>
|
||||
#pragma GCC diagnostic push
|
||||
@ -75,8 +76,8 @@ public:
|
||||
}
|
||||
ss_ path = m_client->get_file_path(name.CString());
|
||||
if(path == ""){
|
||||
log_v(MODULE, "Resource route access: %s (assuming local file)",
|
||||
name.CString());
|
||||
log_v(MODULE, "Resource route access: %s (assuming local file; "
|
||||
"NOTE: Security checks not implemented)", name.CString());
|
||||
// TODO: Check that it is in a safe path
|
||||
return;
|
||||
}
|
||||
@ -99,6 +100,10 @@ struct CApp: public App, public magic::Application
|
||||
magic::SharedPtr<magic::Scene> m_scene;
|
||||
magic::SharedPtr<magic::Node> m_camera_node;
|
||||
|
||||
sp_<interface::TextureAtlasRegistry> m_atlas_reg;
|
||||
sp_<interface::VoxelRegistry> m_voxel_reg;
|
||||
//sp_<interface::BlockRegistry> m_block_reg;
|
||||
|
||||
CApp(magic::Context *context, const Options &options):
|
||||
magic::Application(context),
|
||||
m_script(nullptr),
|
||||
@ -132,6 +137,9 @@ struct CApp: public App, public magic::Application
|
||||
magic_fs->RegisterPath(
|
||||
fs->get_absolute_path(g_client_config.cache_path).c_str());
|
||||
|
||||
// Useful for saving stuff for inspection when debugging
|
||||
magic_fs->RegisterPath("/tmp");
|
||||
|
||||
// Set Urho3D engine parameters
|
||||
engineParameters_["WindowTitle"] = "Buildat Client";
|
||||
engineParameters_["Headless"] = false;
|
||||
@ -173,6 +181,69 @@ struct CApp: public App, public magic::Application
|
||||
magic_cache->SetAutoReloadResources(true);
|
||||
m_router = new BuildatResourceRouter(context_);
|
||||
magic_cache->SetResourceRouter(m_router);
|
||||
|
||||
// Create atlas and voxel registries
|
||||
m_atlas_reg.reset(interface::createTextureAtlasRegistry(context));
|
||||
m_voxel_reg.reset(interface::createVoxelRegistry(m_atlas_reg.get()));
|
||||
|
||||
// Add test voxels
|
||||
// TOOD: Remove this from here
|
||||
{
|
||||
interface::VoxelDefinition vdef;
|
||||
vdef.name.block_name = "air";
|
||||
vdef.name.segment_x = 0;
|
||||
vdef.name.segment_y = 0;
|
||||
vdef.name.segment_z = 0;
|
||||
vdef.name.rotation_primary = 0;
|
||||
vdef.name.rotation_secondary = 0;
|
||||
vdef.handler_module = "";
|
||||
for(size_t i = 0; i < 6; i++){
|
||||
interface::AtlasSegmentDefinition &seg = vdef.textures[i];
|
||||
seg.resource_name = "";
|
||||
seg.total_segments = magic::IntVector2(0, 0);
|
||||
seg.select_segment = magic::IntVector2(0, 0);
|
||||
}
|
||||
vdef.edge_material_id = interface::EDGEMATERIALID_EMPTY;
|
||||
m_voxel_reg->add_voxel(vdef); // id 1
|
||||
}
|
||||
{
|
||||
interface::VoxelDefinition vdef;
|
||||
vdef.name.block_name = "ground";
|
||||
vdef.name.segment_x = 0;
|
||||
vdef.name.segment_y = 0;
|
||||
vdef.name.segment_z = 0;
|
||||
vdef.name.rotation_primary = 0;
|
||||
vdef.name.rotation_secondary = 0;
|
||||
vdef.handler_module = "";
|
||||
for(size_t i = 0; i < 6; i++){
|
||||
interface::AtlasSegmentDefinition &seg = vdef.textures[i];
|
||||
seg.resource_name = "main/green_texture.png";
|
||||
seg.total_segments = magic::IntVector2(1, 1);
|
||||
seg.select_segment = magic::IntVector2(0, 0);
|
||||
}
|
||||
vdef.edge_material_id = interface::EDGEMATERIALID_GROUND;
|
||||
vdef.physically_solid = true;
|
||||
m_voxel_reg->add_voxel(vdef); // id 2
|
||||
}
|
||||
{
|
||||
interface::VoxelDefinition vdef;
|
||||
vdef.name.block_name = "testblock";
|
||||
vdef.name.segment_x = 0;
|
||||
vdef.name.segment_y = 0;
|
||||
vdef.name.segment_z = 0;
|
||||
vdef.name.rotation_primary = 0;
|
||||
vdef.name.rotation_secondary = 0;
|
||||
vdef.handler_module = "";
|
||||
for(size_t i = 0; i < 6; i++){
|
||||
interface::AtlasSegmentDefinition &seg = vdef.textures[i];
|
||||
seg.resource_name = "main/pink_texture.png";
|
||||
seg.total_segments = magic::IntVector2(1, 1);
|
||||
seg.select_segment = magic::IntVector2(0, 0);
|
||||
}
|
||||
vdef.edge_material_id = interface::EDGEMATERIALID_GROUND;
|
||||
vdef.physically_solid = true;
|
||||
m_voxel_reg->add_voxel(vdef); // id 3
|
||||
}
|
||||
}
|
||||
|
||||
~CApp()
|
||||
@ -279,6 +350,11 @@ struct CApp: public App, public magic::Application
|
||||
return m_scene;
|
||||
}
|
||||
|
||||
interface::VoxelRegistry* get_voxel_registry()
|
||||
{
|
||||
return m_voxel_reg.get();
|
||||
}
|
||||
|
||||
// Non-public methods
|
||||
|
||||
void Start()
|
||||
@ -387,6 +463,8 @@ struct CApp: public App, public magic::Application
|
||||
|
||||
void on_update(magic::StringHash event_type, magic::VariantMap &event_data)
|
||||
{
|
||||
m_atlas_reg->update();
|
||||
|
||||
if(g_sigint_received)
|
||||
shutdown();
|
||||
if(m_state)
|
||||
|
@ -11,6 +11,9 @@ namespace Urho3D {
|
||||
namespace client {
|
||||
struct State;
|
||||
}
|
||||
namespace interface {
|
||||
struct VoxelRegistry;
|
||||
}
|
||||
|
||||
namespace app
|
||||
{
|
||||
@ -58,6 +61,7 @@ namespace app
|
||||
virtual void file_updated_in_cache(const ss_ &file_name,
|
||||
const ss_ &file_hash, const ss_ &cached_path) = 0;
|
||||
virtual Urho3D::Scene* get_scene() = 0;
|
||||
virtual interface::VoxelRegistry* get_voxel_registry() = 0;
|
||||
};
|
||||
|
||||
App* createApp(Urho3D::Context *context, const Options &options = Options());
|
||||
|
@ -1,11 +1,13 @@
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Copyright 2014 Perttu Ahola <celeron55@gmail.com>
|
||||
#include "interface/atlas.h"
|
||||
#include "core/log.h"
|
||||
#include <Context.h>
|
||||
#include <ResourceCache.h>
|
||||
#include <Texture2D.h>
|
||||
#include <Graphics.h>
|
||||
#include <Image.h>
|
||||
#define MODULE "atlas"
|
||||
|
||||
namespace interface {
|
||||
|
||||
@ -26,7 +28,9 @@ struct CTextureAtlasRegistry: public TextureAtlasRegistry
|
||||
|
||||
CTextureAtlasRegistry(magic::Context *context):
|
||||
m_context(context)
|
||||
{}
|
||||
{
|
||||
m_defs.resize(1); // id=0 is ATLAS_UNDEFINED
|
||||
}
|
||||
|
||||
const AtlasSegmentReference add_segment(
|
||||
const AtlasSegmentDefinition &segment_def)
|
||||
@ -37,15 +41,19 @@ struct CTextureAtlasRegistry: public TextureAtlasRegistry
|
||||
magic::Image *seg_img = magic_cache->GetResource<magic::Image>(
|
||||
segment_def.resource_name.c_str());
|
||||
if(seg_img == nullptr)
|
||||
throw Exception("Couldn't find image: "+segment_def.resource_name);
|
||||
throw Exception("CTextureAtlasRegistry::add_segment(): Couldn't "
|
||||
"find image \""+segment_def.resource_name+"\" when adding "
|
||||
"segment");
|
||||
// Get resolution of texture
|
||||
magic::IntVector2 seg_img_size(seg_img->GetWidth(), seg_img->GetHeight());
|
||||
// Try to find a texture atlas for this texture size
|
||||
TextureAtlasDefinition *atlas_def = nullptr;
|
||||
for(TextureAtlasDefinition &def0 : m_defs){
|
||||
if(def0.id == ATLAS_UNDEFINED)
|
||||
continue;
|
||||
if(def0.segment_resolution == seg_img_size){
|
||||
size_t max = def0.total_segments.x_ * def0.total_segments.y_;
|
||||
if(atlas_def->segments.size() >= max)
|
||||
if(def0.segments.size() >= max)
|
||||
continue; // Full
|
||||
atlas_def = &def0;
|
||||
break;
|
||||
@ -58,19 +66,20 @@ struct CTextureAtlasRegistry: public TextureAtlasRegistry
|
||||
atlas_def = &m_defs[m_defs.size()-1];
|
||||
atlas_def->id = m_defs.size()-1;
|
||||
// Calculate segment resolution
|
||||
atlas_def->segment_resolution = magic::IntVector2(
|
||||
magic::IntVector2 seg_res(
|
||||
seg_img_size.x_ / segment_def.total_segments.x_,
|
||||
seg_img_size.y_ / segment_def.total_segments.y_
|
||||
);
|
||||
atlas_def->segment_resolution = seg_res;
|
||||
// Calculate total segments based on segment resolution
|
||||
const int max_res = 2048;
|
||||
atlas_def->total_segments = magic::IntVector2(
|
||||
max_res / atlas_def->segment_resolution.x_,
|
||||
max_res / atlas_def->segment_resolution.y_
|
||||
max_res / seg_res.x_ / 2,
|
||||
max_res / seg_res.y_ / 2
|
||||
);
|
||||
magic::IntVector2 atlas_resolution(
|
||||
atlas_def->total_segments.x_ * atlas_def->segment_resolution.x_,
|
||||
atlas_def->total_segments.y_ * atlas_def->segment_resolution.y_
|
||||
atlas_def->total_segments.x_ * seg_res.x_ * 2,
|
||||
atlas_def->total_segments.y_ * seg_res.y_ * 2
|
||||
);
|
||||
// Create image for new atlas
|
||||
magic::Image *atlas_img = new magic::Image(m_context);
|
||||
@ -126,6 +135,8 @@ struct CTextureAtlasRegistry: public TextureAtlasRegistry
|
||||
|
||||
const TextureAtlasDefinition* get_atlas_definition(uint atlas_id)
|
||||
{
|
||||
if(atlas_id == ATLAS_UNDEFINED)
|
||||
return nullptr;
|
||||
if(atlas_id >= m_defs.size())
|
||||
return nullptr;
|
||||
return &m_defs[atlas_id];
|
||||
@ -153,19 +164,24 @@ struct CTextureAtlasRegistry: public TextureAtlasRegistry
|
||||
// Set segment texture
|
||||
cache.texture = atlas.texture;
|
||||
// Calculate segment's position in atlas texture
|
||||
uint seg_iy = seg_id / atlas.total_segments.x_;
|
||||
magic::IntVector2 total_segs = atlas.total_segments;
|
||||
uint seg_iy = seg_id / total_segs.x_;
|
||||
uint seg_ix = seg_id - seg_iy;
|
||||
magic::IntVector2 seg_size = atlas.segment_resolution;
|
||||
magic::IntVector2 dst_p0(seg_ix * seg_size.x_, seg_iy * seg_size.y_);
|
||||
magic::IntVector2 dst_p00(
|
||||
seg_ix * seg_size.x_ * 2,
|
||||
seg_iy * seg_size.y_ * 2
|
||||
);
|
||||
magic::IntVector2 dst_p0 = dst_p00 + seg_size / 2;
|
||||
magic::IntVector2 dst_p1 = dst_p0 + seg_size;
|
||||
// Set coordinates in cache
|
||||
cache.coord0 = magic::Vector2(
|
||||
(float)dst_p0.x_ / (float)(atlas.total_segments.x_ * seg_size.x_),
|
||||
(float)dst_p0.y_ / (float)(atlas.total_segments.y_ * seg_size.y_)
|
||||
(float)dst_p0.x_ / (float)(total_segs.x_ * seg_size.x_ * 2),
|
||||
(float)dst_p0.y_ / (float)(total_segs.y_ * seg_size.y_ * 2)
|
||||
);
|
||||
cache.coord0 = magic::Vector2(
|
||||
(float)dst_p1.x_ / (float)(atlas.total_segments.x_ * seg_size.x_),
|
||||
(float)dst_p1.y_ / (float)(atlas.total_segments.y_ * seg_size.y_)
|
||||
cache.coord1 = magic::Vector2(
|
||||
(float)dst_p1.x_ / (float)(total_segs.x_ * seg_size.x_ * 2),
|
||||
(float)dst_p1.y_ / (float)(total_segs.y_ * seg_size.y_ * 2)
|
||||
);
|
||||
// Draw segment into atlas image
|
||||
magic::IntVector2 seg_img_size(seg_img->GetWidth(), seg_img->GetHeight());
|
||||
@ -173,33 +189,66 @@ struct CTextureAtlasRegistry: public TextureAtlasRegistry
|
||||
seg_img_size.x_ / def.total_segments.x_ * def.select_segment.x_,
|
||||
seg_img_size.y_ / def.total_segments.y_ * def.select_segment.y_
|
||||
);
|
||||
for(int y = 0; y<seg_size.y_; y++){
|
||||
for(int x = 0; x<seg_size.x_; x++){
|
||||
magic::IntVector2 src_p = src_off + magic::IntVector2(x, y);
|
||||
magic::IntVector2 dst_p = dst_p0 + magic::IntVector2(x, y);
|
||||
// Draw main texture
|
||||
for(int y = 0; y<seg_size.y_ * 2; y++){
|
||||
for(int x = 0; x<seg_size.x_ * 2; x++){
|
||||
magic::IntVector2 src_p = src_off + magic::IntVector2(
|
||||
(x + seg_size.x_ / 2) % seg_size.x_,
|
||||
(y + seg_size.y_ / 2) % seg_size.y_
|
||||
);
|
||||
magic::IntVector2 dst_p = dst_p00 + magic::IntVector2(x, y);
|
||||
magic::Color c = seg_img->GetPixel(src_p.x_, src_p.y_);
|
||||
atlas.image->SetPixel(dst_p.x_, dst_p.y_, c);
|
||||
}
|
||||
}
|
||||
// Update atlas texture from atlas image
|
||||
// TODO: Does this require something more?
|
||||
atlas.texture->SetData(atlas.image);
|
||||
|
||||
// Debug: save atlas image to file
|
||||
/*ss_ atlas_img_name = "/tmp/atlas_"+itos(seg_size.x_)+"x"+
|
||||
itos(seg_size.y_)+".png";
|
||||
magic::File f(m_context, atlas_img_name.c_str(), magic::FILE_WRITE);
|
||||
atlas.image->Save(f);*/
|
||||
}
|
||||
|
||||
const TextureAtlasCache* get_atlas_cache(uint atlas_id)
|
||||
{
|
||||
if(atlas_id == ATLAS_UNDEFINED)
|
||||
return nullptr;
|
||||
if(atlas_id >= m_cache.size()){
|
||||
// Cache is always up-to-date
|
||||
return nullptr;
|
||||
}
|
||||
return &m_cache[atlas_id];
|
||||
}
|
||||
|
||||
const AtlasSegmentCache* get_texture(const AtlasSegmentReference &ref)
|
||||
{
|
||||
if(ref.atlas_id >= m_cache.size()){
|
||||
const TextureAtlasCache *cache = get_atlas_cache(ref.atlas_id);
|
||||
if(cache == nullptr)
|
||||
return nullptr;
|
||||
if(ref.segment_id >= cache->segments.size()){
|
||||
// Cache is always up-to-date
|
||||
return nullptr;
|
||||
}
|
||||
TextureAtlasCache &cache = m_cache[ref.atlas_id];
|
||||
if(ref.segment_id >= cache.segments.size()){
|
||||
// Cache is always up-to-date
|
||||
return nullptr;
|
||||
}
|
||||
AtlasSegmentCache &seg_cache = cache.segments[ref.segment_id];
|
||||
const AtlasSegmentCache &seg_cache = cache->segments[ref.segment_id];
|
||||
return &seg_cache;
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
// Re-create textures if a device reset has destroyed them
|
||||
for(uint atlas_id = ATLAS_UNDEFINED + 1;
|
||||
atlas_id < m_cache.size(); atlas_id++){
|
||||
TextureAtlasCache &cache = m_cache[atlas_id];
|
||||
if(cache.texture->IsDataLost()){
|
||||
log_v(MODULE, "Atlas %i texture data lost - re-creating",
|
||||
atlas_id);
|
||||
cache.texture->SetData(cache.image);
|
||||
cache.texture->ClearDataLost();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TextureAtlasRegistry* createTextureAtlasRegistry(magic::Context *context)
|
||||
|
@ -1,6 +1,8 @@
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Copyright 2014 Perttu Ahola <celeron55@gmail.com>
|
||||
#include "interface/mesh.h"
|
||||
#include "interface/voxel.h"
|
||||
#include "core/log.h"
|
||||
#include <PolyVoxCore/SimpleVolume.h>
|
||||
#include <PolyVoxCore/SurfaceMesh.h>
|
||||
#include <PolyVoxCore/CubicSurfaceExtractorWithNormals.h>
|
||||
@ -9,11 +11,18 @@
|
||||
#include <Scene.h>
|
||||
#include <Node.h>
|
||||
#include <StaticModel.h>
|
||||
#include <Model.h>
|
||||
#include <Model.h> // Resource parameter of StaticModel
|
||||
#include <Geometry.h>
|
||||
#include <IndexBuffer.h>
|
||||
#include <VertexBuffer.h>
|
||||
#include <CustomGeometry.h> // A Drawable similarly as StaticModel
|
||||
#include <Material.h>
|
||||
#include <Technique.h>
|
||||
#include <Context.h>
|
||||
#include <ResourceCache.h>
|
||||
#include <Texture2D.h> // Allows cast to Texture
|
||||
#pragma GCC diagnostic pop
|
||||
#define MODULE "mesh"
|
||||
|
||||
namespace magic = Urho3D;
|
||||
namespace pv = PolyVox;
|
||||
@ -23,7 +32,7 @@ using namespace Urho3D;
|
||||
|
||||
namespace interface {
|
||||
|
||||
// Creates a model from a string; eg. (2, 2, 2, "11101111")
|
||||
// Create a model from a string; eg. (2, 2, 2, "11101111")
|
||||
Model* create_simple_voxel_model(Context *context,
|
||||
int w, int h, int d, const ss_ &source_data)
|
||||
{
|
||||
@ -35,12 +44,14 @@ Model* create_simple_voxel_model(Context *context,
|
||||
pv::Vector3DInt32(-1, -1, -1),
|
||||
pv::Vector3DInt32(w, h, d)));
|
||||
size_t i = 0;
|
||||
for(int z = 0; z < d; z++)
|
||||
for(int y = 0; y < h; y++)
|
||||
for(int z = 0; z < d; z++){
|
||||
for(int y = 0; y < h; y++){
|
||||
for(int x = 0; x < w; x++){
|
||||
char c = source_data[i++];
|
||||
volData.setVoxelAt(x, y, z, c == '0' ? 0 : 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pv::SurfaceMesh<pv::PositionMaterialNormal> pv_mesh;
|
||||
pv::CubicSurfaceExtractorWithNormals<pv::SimpleVolume<uint8_t>>
|
||||
@ -98,5 +109,378 @@ Model* create_simple_voxel_model(Context *context,
|
||||
return fromScratchModel;
|
||||
}
|
||||
|
||||
template<typename VoxelType>
|
||||
class IsQuadNeededByRegistryPhysics
|
||||
{
|
||||
interface::VoxelRegistry *m_voxel_reg;
|
||||
// NOTE: The voxel type id is used directly as PolyVox material value
|
||||
public:
|
||||
IsQuadNeededByRegistryPhysics(interface::VoxelRegistry *voxel_reg):
|
||||
m_voxel_reg(voxel_reg)
|
||||
{}
|
||||
IsQuadNeededByRegistryPhysics(): // PolyVox wants this
|
||||
m_voxel_reg(nullptr)
|
||||
{}
|
||||
bool operator()(VoxelType back, VoxelType front, uint32_t &materialToUse)
|
||||
{
|
||||
if(m_voxel_reg == nullptr)
|
||||
throw Exception("IsQuadNeededByRegistryPhysics not initialized");
|
||||
const interface::CachedVoxelDefinition *back_def =
|
||||
m_voxel_reg->get_cached(back);
|
||||
const interface::CachedVoxelDefinition *front_def =
|
||||
m_voxel_reg->get_cached(front);
|
||||
if(!back_def || !back_def->physically_solid)
|
||||
return false;
|
||||
if(!front_def || !front_def->physically_solid){
|
||||
materialToUse = 1; // Doesn't matter
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Create a model from 8-bit voxel data, using a voxel registry, without
|
||||
// textures or normals, based on the physically_solid flag.
|
||||
Model* create_8bit_voxel_physics_model(Context *context,
|
||||
int w, int h, int d, const ss_ &source_data,
|
||||
VoxelRegistry *voxel_reg)
|
||||
{
|
||||
if(w < 0 || h < 0 || d < 0)
|
||||
throw Exception("Negative dimension");
|
||||
if(w * h * d != (int)source_data.size())
|
||||
throw Exception("Mismatched data size");
|
||||
pv::SimpleVolume<uint8_t> volData(pv::Region(
|
||||
pv::Vector3DInt32(-1, -1, -1),
|
||||
pv::Vector3DInt32(w, h, d)));
|
||||
size_t i = 0;
|
||||
for(int z = 0; z < d; z++){
|
||||
for(int y = 0; y < h; y++){
|
||||
for(int x = 0; x < w; x++){
|
||||
char c = source_data[i++];
|
||||
volData.setVoxelAt(x, y, z, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IsQuadNeededByRegistryPhysics<uint8_t> iqn(voxel_reg);
|
||||
pv::SurfaceMesh<pv::PositionMaterialNormal> pv_mesh;
|
||||
pv::CubicSurfaceExtractorWithNormals<
|
||||
pv::SimpleVolume<uint8_t>, IsQuadNeededByRegistryPhysics<uint8_t>>
|
||||
surfaceExtractor(&volData, volData.getEnclosingRegion(), &pv_mesh, iqn);
|
||||
surfaceExtractor.execute();
|
||||
|
||||
const sv_<uint32_t> &pv_indices = pv_mesh.getIndices();
|
||||
const sv_<pv::PositionMaterialNormal> &pv_vertices = pv_mesh.getVertices();
|
||||
|
||||
const size_t num_vertices = pv_vertices.size();
|
||||
const size_t num_indices = pv_indices.size();
|
||||
|
||||
sv_<float> vertex_data;
|
||||
vertex_data.resize(num_vertices * 6); // vertex + normal
|
||||
for(size_t i = 0; i < num_vertices; i++){
|
||||
vertex_data[i*6 + 0] = pv_vertices[i].position.getX() - w/2.0f - 0.5f;
|
||||
vertex_data[i*6 + 1] = pv_vertices[i].position.getY() - h/2.0f - 0.5f;
|
||||
vertex_data[i*6 + 2] = pv_vertices[i].position.getZ() - d/2.0f - 0.5f;
|
||||
vertex_data[i*6 + 3] = pv_vertices[i].normal.getX();
|
||||
vertex_data[i*6 + 4] = pv_vertices[i].normal.getY();
|
||||
vertex_data[i*6 + 5] = pv_vertices[i].normal.getZ();
|
||||
}
|
||||
|
||||
//sv_<short> index_data;
|
||||
sv_<unsigned> index_data;
|
||||
index_data.resize(num_indices);
|
||||
for(size_t i = 0; i < num_indices; i++){
|
||||
/*if(pv_indices[i] >= 0x10000)
|
||||
throw Exception("Index too large");*/
|
||||
index_data[i] = pv_indices[i];
|
||||
}
|
||||
|
||||
SharedPtr<VertexBuffer> vb(new VertexBuffer(context));
|
||||
// Shadowed buffer needed for raycasts to work, and so that data can be
|
||||
// automatically restored on device loss
|
||||
vb->SetShadowed(true);
|
||||
// TODO: Normals are probably unnecessary for a physics model
|
||||
vb->SetSize(num_vertices, magic::MASK_POSITION | magic::MASK_NORMAL);
|
||||
vb->SetData(&vertex_data[0]);
|
||||
|
||||
SharedPtr<IndexBuffer> ib(new IndexBuffer(context));
|
||||
ib->SetShadowed(true);
|
||||
//ib->SetSize(num_indices, false);
|
||||
ib->SetSize(num_indices, true);
|
||||
ib->SetData(&index_data[0]);
|
||||
|
||||
SharedPtr<Geometry> geom(new Geometry(context));
|
||||
geom->SetVertexBuffer(0, vb);
|
||||
geom->SetIndexBuffer(ib);
|
||||
geom->SetDrawRange(TRIANGLE_LIST, 0, num_indices);
|
||||
|
||||
Model *fromScratchModel = new Model(context);
|
||||
fromScratchModel->SetNumGeometries(1);
|
||||
fromScratchModel->SetGeometry(0, 0, geom);
|
||||
fromScratchModel->SetBoundingBox(BoundingBox(
|
||||
Vector3(-0.5f*w, -0.5f*h, -0.5f*d), Vector3(0.5f*w, 0.5f*h, 0.5f*d)));
|
||||
|
||||
return fromScratchModel;
|
||||
}
|
||||
|
||||
template<typename VoxelType>
|
||||
class IsQuadNeededByRegistry
|
||||
{
|
||||
interface::VoxelRegistry *m_voxel_reg;
|
||||
// NOTE: The voxel type id is used directly as PolyVox material value
|
||||
public:
|
||||
IsQuadNeededByRegistry(interface::VoxelRegistry *voxel_reg):
|
||||
m_voxel_reg(voxel_reg)
|
||||
{}
|
||||
IsQuadNeededByRegistry(): // PolyVox wants this
|
||||
m_voxel_reg(nullptr)
|
||||
{}
|
||||
bool operator()(VoxelType back, VoxelType front, uint32_t &materialToUse)
|
||||
{
|
||||
if(m_voxel_reg == nullptr)
|
||||
throw Exception("IsQuadNeededByRegistry not initialized");
|
||||
const interface::CachedVoxelDefinition *back_def =
|
||||
m_voxel_reg->get_cached(back);
|
||||
const interface::CachedVoxelDefinition *front_def =
|
||||
m_voxel_reg->get_cached(front);
|
||||
if(!back_def){
|
||||
log_t(MODULE, "back=%i: Definition not found", back);
|
||||
return false;
|
||||
}
|
||||
else if(back_def->face_draw_type == interface::FaceDrawType::NEVER){
|
||||
log_t(MODULE, "back=%i: FaceDrawType::NEVER", back);
|
||||
return false;
|
||||
}
|
||||
else if(back_def->face_draw_type == interface::FaceDrawType::ALWAYS){
|
||||
log_t(MODULE, "back=%i: FaceDrawType::ALWAYS", back);
|
||||
materialToUse = back;
|
||||
return true;
|
||||
}
|
||||
// interface::FaceDrawType::ON_EDGE
|
||||
if(!front_def){
|
||||
log_t(MODULE, "back=%i: FaceDrawType::ON_EDGE; front undef", back);
|
||||
materialToUse = back;
|
||||
return true;
|
||||
}
|
||||
if(back_def->edge_material_id != front_def->edge_material_id){
|
||||
log_t(MODULE, "back=%i: FaceDrawType::ON_EDGE; edge m. diff", back);
|
||||
materialToUse = back;
|
||||
return true;
|
||||
}
|
||||
log_t(MODULE, "back=%i: FaceDrawType::ON_EDGE; edge same m.", back);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
#if 0
|
||||
// TODO: Create a custom Drawable that can use an index buffer
|
||||
struct TemporaryGeometry
|
||||
{
|
||||
uint atlas_id = 0;
|
||||
sv_<float> vertex_data; // vertex(3) + normal(3) + texcoord(2)
|
||||
sv_<unsigned> index_data; // Urho3D eats unsigned as large indices
|
||||
};
|
||||
#else
|
||||
struct TemporaryGeometry
|
||||
{
|
||||
uint atlas_id = 0;
|
||||
// CustomGeometry can't handle an index buffer
|
||||
PODVector<CustomGeometryVertex> vertex_data;
|
||||
};
|
||||
#endif
|
||||
|
||||
// Set custom geometry from 8-bit voxel data, using a voxel registry
|
||||
void set_8bit_voxel_geometry(CustomGeometry *cg, Context *context,
|
||||
int w, int h, int d, const ss_ &source_data,
|
||||
VoxelRegistry *voxel_reg)
|
||||
{
|
||||
if(w < 0 || h < 0 || d < 0)
|
||||
throw Exception("Negative dimension");
|
||||
if(w * h * d != (int)source_data.size())
|
||||
throw Exception("Mismatched data size");
|
||||
pv::SimpleVolume<uint8_t> volData(pv::Region(
|
||||
pv::Vector3DInt32(-1, -1, -1),
|
||||
pv::Vector3DInt32(w, h, d)));
|
||||
size_t i = 0;
|
||||
for(int z = 0; z < d; z++){
|
||||
for(int y = 0; y < h; y++){
|
||||
for(int x = 0; x < w; x++){
|
||||
char c = source_data[i++];
|
||||
volData.setVoxelAt(x, y, z, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IsQuadNeededByRegistry<uint8_t> iqn(voxel_reg);
|
||||
pv::SurfaceMesh<pv::PositionMaterialNormal> pv_mesh;
|
||||
pv::CubicSurfaceExtractorWithNormals<
|
||||
pv::SimpleVolume<uint8_t>, IsQuadNeededByRegistry<uint8_t>>
|
||||
surfaceExtractor(&volData, volData.getEnclosingRegion(), &pv_mesh, iqn);
|
||||
surfaceExtractor.execute();
|
||||
|
||||
const sv_<uint32_t> &pv_indices = pv_mesh.getIndices();
|
||||
const sv_<pv::PositionMaterialNormal> &pv_vertices = pv_mesh.getVertices();
|
||||
|
||||
sm_<uint, TemporaryGeometry> temp_geoms;
|
||||
|
||||
// Handle vertices face-by-face in order to copy indices at the same time
|
||||
for(size_t pv_face_i = 0; pv_face_i < pv_vertices.size() / 4; pv_face_i++){
|
||||
size_t pv_vertex_i0 = pv_face_i * 4;
|
||||
VoxelTypeId voxel_id0 = (VoxelTypeId)pv_vertices[pv_vertex_i0].material;
|
||||
// We need to get this definition only once per face
|
||||
const interface::CachedVoxelDefinition *voxel_def0 =
|
||||
voxel_reg->get_cached(voxel_id0);
|
||||
if(voxel_def0 == nullptr)
|
||||
throw Exception("Unknown voxel in generated geometry: "+
|
||||
itos(voxel_id0));
|
||||
// Figure out which face this is
|
||||
uint face_id = 0;
|
||||
const pv::Vector3DFloat &n = pv_vertices[pv_vertex_i0].normal;
|
||||
if(n.getY() > 0)
|
||||
face_id = 0;
|
||||
else if(n.getY() < 0)
|
||||
face_id = 1;
|
||||
else if(n.getX() > 0)
|
||||
face_id = 2;
|
||||
else if(n.getX() < 0)
|
||||
face_id = 3;
|
||||
else if(n.getZ() > 0)
|
||||
face_id = 4;
|
||||
else if(n.getZ() < 0)
|
||||
face_id = 5;
|
||||
// Get texture coordinates (contained in AtlasSegmentCache)
|
||||
AtlasSegmentReference seg_ref = voxel_def0->textures[face_id];
|
||||
if(seg_ref.atlas_id == interface::ATLAS_UNDEFINED){
|
||||
// This is usually intentional for invisible voxels
|
||||
log_t(MODULE, "Voxel %i face %i atlas undefined", voxel_id0, face_id);
|
||||
continue;
|
||||
}
|
||||
const AtlasSegmentCache *aseg =
|
||||
voxel_reg->get_atlas()->get_texture(seg_ref);
|
||||
if(aseg == nullptr)
|
||||
throw Exception("No atlas segment cache for voxel "+itos(voxel_id0)+
|
||||
" face "+itos(face_id));
|
||||
#if 0
|
||||
// TODO: Create a custom Drawable that can use an index buffer
|
||||
// Get or create the appropriate temporary geometry for this atlas
|
||||
TemporaryGeometry &tg = temp_geoms[seg_ref.atlas_id];
|
||||
if(tg.vertex_data.empty()){
|
||||
tg.atlas_id = seg_ref.atlas_id;
|
||||
// It can't get larger than these and will only exist temporarily in
|
||||
// memory, so let's do only one big memory allocation
|
||||
tg.vertex_data.reserve(pv_vertices.size());
|
||||
tg.index_data.reserve(pv_indices.size());
|
||||
}
|
||||
// Mangle vertices into temporary geometry
|
||||
size_t dst_vertex_i = tg.vertex_data.size() / 8;
|
||||
for(size_t vertex_i1 = 0; vertex_i1 < 4; vertex_i1++){
|
||||
size_t vertex_i = pv_vertex_i0 + vertex_i1;
|
||||
// Each vertex of the face must be of the same voxel; otherwise the
|
||||
// face makes no sense at all
|
||||
VoxelTypeId voxel_id = (VoxelTypeId)pv_vertices[pv_vertex_i0].material;
|
||||
if(voxel_id != voxel_id0)
|
||||
throw Exception("voxel_id != voxel_id0");
|
||||
// Add new values to temporary geometry
|
||||
const auto &pv_vert = pv_vertices[vertex_i];
|
||||
tg.vertex_data.push_back(pv_vert.position.getX() - w/2.0f - 0.5f);
|
||||
tg.vertex_data.push_back(pv_vert.position.getY() - h/2.0f - 0.5f);
|
||||
tg.vertex_data.push_back(pv_vert.position.getZ() - d/2.0f - 0.5f);
|
||||
tg.vertex_data.push_back(pv_vert.normal.getX());
|
||||
tg.vertex_data.push_back(pv_vert.normal.getY());
|
||||
tg.vertex_data.push_back(pv_vert.normal.getZ());
|
||||
tg.vertex_data.push_back(0);
|
||||
tg.vertex_data.push_back(0);
|
||||
}
|
||||
// Mangle indices into temporary geometry
|
||||
size_t index_i0 = pv_face_i * 6;
|
||||
// First index value from polyvox
|
||||
unsigned src_index0_value = pv_indices[index_i0];
|
||||
// First index value to be created (NOTE: This relies on the fact that
|
||||
// pv::CubicSurfaceExtractorWithNormals always references the first
|
||||
// vertex with the first index)
|
||||
unsigned dst_index0_value = dst_vertex_i / 4;
|
||||
pv_indices[index_i0];
|
||||
for(size_t index_i1 = 0; index_i1 < 6; index_i1++){
|
||||
size_t index_i = index_i0 + index_i1;
|
||||
tg.index_data[dst_vertex_i * 6 + index_i1] =
|
||||
pv_indices[index_i] - src_index0_value + dst_index0_value;
|
||||
}
|
||||
#else
|
||||
// Get or create the appropriate temporary geometry for this atlas
|
||||
TemporaryGeometry &tg = temp_geoms[seg_ref.atlas_id];
|
||||
if(tg.vertex_data.Empty()){
|
||||
tg.atlas_id = seg_ref.atlas_id;
|
||||
// It can't get larger than this and will only exist temporarily in
|
||||
// memory, so let's do only one big memory allocation
|
||||
tg.vertex_data.Reserve(pv_vertices.size() / 4 * 6);
|
||||
}
|
||||
// Go through indices of the face and mangle vertices according to them
|
||||
// into the temporary vertex buffer
|
||||
size_t pv_index_i0 = pv_face_i * 6;
|
||||
for(size_t pv_index_i1 = 0; pv_index_i1 < 6; pv_index_i1++){
|
||||
size_t pv_index_i = pv_index_i0 + pv_index_i1;
|
||||
size_t pv_vertex_i = pv_indices[pv_index_i];
|
||||
if(pv_index_i1 == 0 && pv_vertex_i0 != pv_vertex_i)
|
||||
throw Exception("First index of face does not point to first "
|
||||
"vertex of face");
|
||||
const auto &pv_vert = pv_vertices[pv_vertex_i];
|
||||
tg.vertex_data.Resize(tg.vertex_data.Size() + 1);
|
||||
CustomGeometryVertex &tg_vert = tg.vertex_data.Back();
|
||||
tg_vert.position_.x_ = pv_vert.position.getX() - w/2.0f - 0.5f;
|
||||
tg_vert.position_.y_ = pv_vert.position.getY() - h/2.0f - 0.5f;
|
||||
tg_vert.position_.z_ = pv_vert.position.getZ() - d/2.0f - 0.5f;
|
||||
tg_vert.normal_.x_ = pv_vert.normal.getX();
|
||||
tg_vert.normal_.y_ = pv_vert.normal.getY();
|
||||
tg_vert.normal_.z_ = pv_vert.normal.getZ();
|
||||
// Figure out texture coordinates
|
||||
size_t pv_vertex_i1 = pv_vertex_i - pv_vertex_i0;
|
||||
if(pv_vertex_i1 == 0){
|
||||
tg_vert.texCoord_.x_ = aseg->coord0.x_;
|
||||
tg_vert.texCoord_.y_ = aseg->coord1.y_;
|
||||
} else if(pv_vertex_i1 == 1){
|
||||
tg_vert.texCoord_.x_ = aseg->coord1.x_;
|
||||
tg_vert.texCoord_.y_ = aseg->coord1.y_;
|
||||
} else if(pv_vertex_i1 == 2){
|
||||
tg_vert.texCoord_.x_ = aseg->coord0.x_;
|
||||
tg_vert.texCoord_.y_ = aseg->coord0.y_;
|
||||
} else if(pv_vertex_i1 == 3){
|
||||
tg_vert.texCoord_.x_ = aseg->coord1.x_;
|
||||
tg_vert.texCoord_.y_ = aseg->coord0.y_;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Generate CustomGeometry from TemporaryGeometry
|
||||
|
||||
ResourceCache *cache = context->GetSubsystem<ResourceCache>();
|
||||
|
||||
cg->SetNumGeometries(temp_geoms.size());
|
||||
Vector<PODVector<CustomGeometryVertex>> &cg_all_vertices = cg->GetVertices();
|
||||
|
||||
unsigned cg_i = 0;
|
||||
for(auto &pair : temp_geoms){
|
||||
const TemporaryGeometry &tg = pair.second;
|
||||
const TextureAtlasCache *atlas_cache =
|
||||
voxel_reg->get_atlas()->get_atlas_cache(tg.atlas_id);
|
||||
if(atlas_cache == nullptr)
|
||||
throw Exception("atlas_cache == nullptr");
|
||||
if(atlas_cache->texture == nullptr)
|
||||
throw Exception("atlas_cache->texture == nullptr");
|
||||
cg->DefineGeometry(cg_i, TRIANGLE_LIST, tg.vertex_data.Size(),
|
||||
true, false, true, false);
|
||||
PODVector<CustomGeometryVertex> &cg_vertices = cg_all_vertices[cg_i];
|
||||
cg_vertices = tg.vertex_data;
|
||||
Material *material = new Material(context);
|
||||
material->SetTechnique(0,
|
||||
cache->GetResource<Technique>("Techniques/Diff.xml"));
|
||||
material->SetTexture(TU_DIFFUSE, atlas_cache->texture);
|
||||
cg->SetMaterial(cg_i, material);
|
||||
cg_i++;
|
||||
}
|
||||
|
||||
cg->Commit();
|
||||
}
|
||||
|
||||
} // namespace interface
|
||||
// vim: set noet ts=4 sw=4:
|
||||
|
@ -56,13 +56,22 @@ struct CVoxelRegistry: public VoxelRegistry
|
||||
m_defs.resize(1); // Id 0 is VOXELTYPEID_UNDEFINEDD
|
||||
}
|
||||
|
||||
TextureAtlasRegistry* get_atlas()
|
||||
{
|
||||
return m_atlas_reg;
|
||||
}
|
||||
|
||||
VoxelTypeId add_voxel(const VoxelDefinition &def)
|
||||
{
|
||||
VoxelTypeId id = m_defs.size();
|
||||
// NOTE: This invalidates all previous pointers to cache entries that
|
||||
// were given out
|
||||
m_defs.resize(id + 1);
|
||||
m_defs[id] = def;
|
||||
m_defs[id].id = id;
|
||||
m_name_to_id[def.name] = id;
|
||||
log_v(MODULE, "CVoxelRegistyr::add_voxel(): Added id=%i name=%s",
|
||||
id, cs(def.name.dump()));
|
||||
return id;
|
||||
}
|
||||
|
||||
@ -95,6 +104,9 @@ struct CVoxelRegistry: public VoxelRegistry
|
||||
log_w(MODULE, "CVoxelRegistry::get_cached(): id=%i not found", id);
|
||||
return NULL;
|
||||
}
|
||||
if(m_cached_defs.size() < m_defs.size()){
|
||||
m_cached_defs.resize(m_defs.size());
|
||||
}
|
||||
const VoxelDefinition &def = m_defs[id];
|
||||
CachedVoxelDefinition &cache = m_cached_defs[id];
|
||||
update_cache(cache, def);
|
||||
@ -104,15 +116,22 @@ struct CVoxelRegistry: public VoxelRegistry
|
||||
|
||||
void update_cache(CachedVoxelDefinition &cache, const VoxelDefinition &def)
|
||||
{
|
||||
cache.handler_module = def.handler_module;
|
||||
log_d(MODULE, "CVoxelRegistry::update_cache(): id=%i", def.id);
|
||||
for(size_t i = 0; i<6; i++){
|
||||
const AtlasSegmentDefinition &seg_def = def.textures[i];
|
||||
if(seg_def.resource_name == ""){
|
||||
AtlasSegmentReference seg_ref; // Use default values
|
||||
cache.textures[i] = seg_ref;
|
||||
} else {
|
||||
AtlasSegmentReference seg_ref =
|
||||
m_atlas_reg->find_or_add_segment(seg_def);
|
||||
const AtlasSegmentCache *seg_cache =
|
||||
m_atlas_reg->get_texture(seg_ref);
|
||||
cache.textures[i] = seg_cache;
|
||||
cache.textures[i] = seg_ref;
|
||||
}
|
||||
}
|
||||
cache.handler_module = def.handler_module;
|
||||
cache.face_draw_type = def.face_draw_type;
|
||||
cache.edge_material_id = def.edge_material_id;
|
||||
cache.physically_solid = def.physically_solid;
|
||||
// Caller sets cache.valid = true
|
||||
}
|
||||
|
||||
|
@ -16,15 +16,17 @@ namespace interface
|
||||
{
|
||||
namespace magic = Urho3D;
|
||||
|
||||
static constexpr uint ATLAS_UNDEFINED = 0;
|
||||
|
||||
struct AtlasSegmentReference
|
||||
{
|
||||
uint atlas_id = 0;
|
||||
uint atlas_id = ATLAS_UNDEFINED;// 0 = undefined atlas
|
||||
uint segment_id = 0;
|
||||
};
|
||||
|
||||
struct AtlasSegmentDefinition
|
||||
{
|
||||
ss_ resource_name;
|
||||
ss_ resource_name; // If "", segment won't be added
|
||||
magic::IntVector2 total_segments;
|
||||
magic::IntVector2 select_segment;
|
||||
// TODO: Rotation
|
||||
@ -41,7 +43,7 @@ namespace interface
|
||||
|
||||
struct TextureAtlasDefinition
|
||||
{
|
||||
uint id;
|
||||
uint id = ATLAS_UNDEFINED;
|
||||
magic::IntVector2 segment_resolution;
|
||||
magic::IntVector2 total_segments;
|
||||
sv_<AtlasSegmentDefinition> segments;
|
||||
@ -50,7 +52,7 @@ namespace interface
|
||||
struct TextureAtlasCache
|
||||
{
|
||||
magic::SharedPtr<magic::Image> image;
|
||||
magic::Texture2D *texture = nullptr;
|
||||
magic::SharedPtr<magic::Texture2D> texture;
|
||||
magic::IntVector2 segment_resolution;
|
||||
magic::IntVector2 total_segments;
|
||||
sv_<AtlasSegmentCache> segments;
|
||||
@ -70,8 +72,12 @@ namespace interface
|
||||
virtual const AtlasSegmentDefinition* get_segment_definition(
|
||||
const AtlasSegmentReference &ref) = 0;
|
||||
|
||||
virtual const TextureAtlasCache* get_atlas_cache(uint atlas_id) = 0;
|
||||
|
||||
virtual const AtlasSegmentCache* get_texture(
|
||||
const AtlasSegmentReference &ref) = 0;
|
||||
|
||||
virtual void update() = 0;
|
||||
};
|
||||
|
||||
TextureAtlasRegistry* createTextureAtlasRegistry(magic::Context *context);
|
||||
|
@ -7,13 +7,26 @@ namespace Urho3D
|
||||
{
|
||||
class Context;
|
||||
class Model;
|
||||
class CustomGeometry;
|
||||
}
|
||||
|
||||
namespace interface
|
||||
{
|
||||
using namespace Urho3D;
|
||||
|
||||
struct VoxelRegistry;
|
||||
|
||||
// Create a model from a string; eg. (2, 2, 2, "11101111")
|
||||
Model* create_simple_voxel_model(Context *context, int w, int h, int d,
|
||||
const ss_ &source_data);
|
||||
// Create a model from 8-bit voxel data, using a voxel registry, without
|
||||
// textures or normals, based on the physically_solid flag.
|
||||
Model* create_8bit_voxel_physics_model(Context *context,
|
||||
int w, int h, int d, const ss_ &source_data,
|
||||
VoxelRegistry *voxel_reg);
|
||||
// Set custom geometry from 8-bit voxel data, using a voxel registry
|
||||
void set_8bit_voxel_geometry(CustomGeometry *cg, Context *context,
|
||||
int w, int h, int d, const ss_ &source_data,
|
||||
VoxelRegistry *voxel_reg);
|
||||
}
|
||||
// vim: set noet ts=4 sw=4:
|
||||
|
@ -10,8 +10,8 @@ namespace interface
|
||||
namespace pv = PolyVox;
|
||||
|
||||
typedef uint32_t VoxelTypeId;
|
||||
static constexpr uint32_t VOXELTYPEID_MAX = 1398101-1;
|
||||
static constexpr uint32_t VOXELTYPEID_UNDEFINED = 0;
|
||||
static constexpr VoxelTypeId VOXELTYPEID_MAX = 1398101-1;
|
||||
static constexpr VoxelTypeId VOXELTYPEID_UNDEFINED = 0;
|
||||
|
||||
struct VoxelName
|
||||
{
|
||||
@ -26,6 +26,17 @@ namespace interface
|
||||
bool operator==(const VoxelName &other) const;
|
||||
};
|
||||
|
||||
enum class FaceDrawType {
|
||||
NEVER = 0,
|
||||
ALWAYS = 1,
|
||||
ON_EDGE = 2,
|
||||
};
|
||||
|
||||
// A thing that allows distinguishing which voxel faces generate edges
|
||||
typedef uint8_t EdgeMaterialId;
|
||||
static constexpr EdgeMaterialId EDGEMATERIALID_EMPTY = 0;
|
||||
static constexpr EdgeMaterialId EDGEMATERIALID_GROUND = 1;
|
||||
|
||||
struct VoxelDefinition
|
||||
{
|
||||
VoxelName name;
|
||||
@ -36,9 +47,11 @@ namespace interface
|
||||
AtlasSegmentDefinition textures[6];
|
||||
// Other properties
|
||||
ss_ handler_module;
|
||||
FaceDrawType face_draw_type = FaceDrawType::ON_EDGE;
|
||||
EdgeMaterialId edge_material_id = EDGEMATERIALID_EMPTY;
|
||||
bool physically_solid = false;
|
||||
// TODO: Flag for whether all faces should be always drawn (in case the
|
||||
// textures contain holes)
|
||||
// TODO: Flag for whether this voxel is physically solid
|
||||
// TODO: Some kind of property for defining whether this is a thing for
|
||||
// which adjacent voxels of the same thing type don't have faces,
|
||||
// and what thing type that is in this case
|
||||
@ -50,13 +63,17 @@ namespace interface
|
||||
struct CachedVoxelDefinition
|
||||
{
|
||||
bool valid = false;
|
||||
const AtlasSegmentCache *textures[6] = {NULL};
|
||||
AtlasSegmentReference textures[6];
|
||||
ss_ handler_module;
|
||||
FaceDrawType face_draw_type = FaceDrawType::ON_EDGE;
|
||||
EdgeMaterialId edge_material_id = EDGEMATERIALID_EMPTY;
|
||||
bool physically_solid = false;
|
||||
};
|
||||
|
||||
struct VoxelRegistry
|
||||
{
|
||||
virtual ~VoxelRegistry(){}
|
||||
virtual TextureAtlasRegistry* get_atlas() = 0;
|
||||
|
||||
virtual VoxelTypeId add_voxel(const VoxelDefinition &def) = 0;
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <Scene.h>
|
||||
#include <StaticModel.h>
|
||||
#include <Model.h>
|
||||
#include <CustomGeometry.h>
|
||||
#pragma GCC diagnostic pop
|
||||
#define MODULE "lua_bindings"
|
||||
|
||||
@ -61,6 +62,50 @@ static int l_set_simple_voxel_model(lua_State *L)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// set_8bit_voxel_geometry(node, w, h, d, data: string)
|
||||
static int l_set_8bit_voxel_geometry(lua_State *L)
|
||||
{
|
||||
int w = lua_tointeger(L, 2);
|
||||
int h = lua_tointeger(L, 3);
|
||||
int d = lua_tointeger(L, 4);
|
||||
ss_ data = lua_tocppstring(L, 5);
|
||||
|
||||
if((int)data.size() != w * h * d){
|
||||
log_e(MODULE, "set_8bit_voxel_geometry(): Data size does not match "
|
||||
"with dimensions (%zu vs. %i)", data.size(), w*h*d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
tolua_Error tolua_err;
|
||||
//if(!tolua_isusertype(L, 1, "const Node", 0, &tolua_err)){
|
||||
if(!tolua_isusertype(L, 1, "Node", 0, &tolua_err)){
|
||||
tolua_error(L, "Error in set_8bit_voxel_geometry", &tolua_err);
|
||||
return 0;
|
||||
}
|
||||
log_d(MODULE, "set_8bit_voxel_geometry(): Valid Node given");
|
||||
//const Node *node = (const Node*)tolua_tousertype(L, 1, 0);
|
||||
Node *node = (Node*)tolua_tousertype(L, 1, 0);
|
||||
log_d(MODULE, "set_8bit_voxel_geometry(): node=%p", node);
|
||||
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, "__buildat_app");
|
||||
app::App *buildat_app = (app::App*)lua_touserdata(L, -1);
|
||||
lua_pop(L, 1);
|
||||
Context *context = buildat_app->get_scene()->GetContext();
|
||||
auto *voxel_reg = buildat_app->get_voxel_registry();
|
||||
|
||||
CustomGeometry *cg = node->CreateComponent<CustomGeometry>();
|
||||
|
||||
interface::set_8bit_voxel_geometry(cg, context, w, h, d, data, voxel_reg);
|
||||
|
||||
// Maybe appropriate
|
||||
cg->SetOccluder(true);
|
||||
|
||||
// TODO: Don't do this here; allow the caller to do this
|
||||
cg->SetCastShadows(true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void init_voxel(lua_State *L)
|
||||
{
|
||||
#define DEF_BUILDAT_FUNC(name){ \
|
||||
@ -68,6 +113,7 @@ void init_voxel(lua_State *L)
|
||||
lua_setglobal(L, "__buildat_" #name); \
|
||||
}
|
||||
DEF_BUILDAT_FUNC(set_simple_voxel_model);
|
||||
DEF_BUILDAT_FUNC(set_8bit_voxel_geometry);
|
||||
}
|
||||
|
||||
} // namespace lua_bindingss
|
||||
|
Loading…
x
Reference in New Issue
Block a user