builtin/voxelworld: WIP

This commit is contained in:
Perttu Ahola 2014-10-13 01:32:58 +03:00
parent cd3abc2048
commit 3f69453efe
12 changed files with 388 additions and 167 deletions

View File

@ -100,6 +100,11 @@ struct Module: public interface::Module, public network::Interface
!m_listening_socket->listen_fd()){
log_i(MODULE, "Failed to bind to %s:%s, fd=%i", cs(address), cs(port),
m_listening_socket->fd());
// We don't want to be in this state for any amount of time; it will
// confuse the hell out of everybody otherwise
m_server->shutdown(1, "Failed to bind socket");
throw Exception("Failed to bind socket");
return;
} else {
log_i(MODULE, "Listening at %s:%s, fd=%i", cs(address), cs(port),
m_listening_socket->fd());

View File

@ -11,6 +11,7 @@ local M = {}
local camera_node = nil
local update_counter = -1
local camera_last_dir = magic.Vector3(0, 0, 0)
local camera_last_p = magic.Vector3(0, 0, 0)
M.chunk_size_voxels = nil
M.section_size_chunks = nil
@ -84,18 +85,23 @@ function M.init()
end
end
-- Handle geometry updates a few nodes per frame
if #node_geometry_update_queue > 0 then
local nodes_per_frame = 2
if camera_node.direction ~= camera_last_dir then
nodes_per_frame = 1 -- Limit when camera is turning
end
for i = 1, nodes_per_frame do
local node = get_next_geometry_update_node()
if not node then break end
setup_buildat_voxel_data(node)
if camera_node then
local camera_dir = camera_node.direction
local camera_p = camera_node:GetWorldPosition()
if #node_geometry_update_queue > 0 then
local nodes_per_frame = 2
if camera_dir ~= camera_last_dir or camera_p ~= camera_last_p then
nodes_per_frame = 1 -- Limit when camera is turning
end
for i = 1, nodes_per_frame do
local node = get_next_geometry_update_node()
if not node then break end
setup_buildat_voxel_data(node)
end
end
camera_last_dir = camera_dir
camera_last_p = camera_p
end
camera_last_dir = camera_node.direction
end)
replicate.sub_sync_node_added({}, function(node)

View File

@ -197,11 +197,12 @@ struct Module: public interface::Module, public voxelworld::Interface
sp_<interface::BlockRegistry> m_block_reg;
// One node holds one chunk of voxels (eg. 32x32x32)
pv::Vector3DInt16 m_chunk_size_voxels = pv::Vector3DInt16(32, 32, 32);
//pv::Vector3DInt16 m_chunk_size_voxels = pv::Vector3DInt16(32, 32, 32);
// The world is loaded and unloaded by sections (eg. 2x2x2)
pv::Vector3DInt16 m_section_size_chunks = pv::Vector3DInt16(2, 2, 2);
// These are suitable for running under valgrind
pv::Vector3DInt16 m_chunk_size_voxels = pv::Vector3DInt16(16, 16, 16);
//pv::Vector3DInt16 m_chunk_size_voxels = pv::Vector3DInt16(8, 8, 8);
//pv::Vector3DInt16 m_section_size_chunks = pv::Vector3DInt16(2, 2, 2);
@ -492,9 +493,9 @@ struct Module: public interface::Module, public voxelworld::Interface
int h = m_chunk_size_voxels.getY();
int d = m_chunk_size_voxels.getZ();
// NOTE: These volumes have one extra voxel at the positive axis in
// order to make proper meshes without gaps
pv::Region region(0, 0, 0, w, h, d);
// NOTE: These volumes have one extra voxel at each edge in order to
// make proper meshes without gaps
pv::Region region(-1, -1, -1, w, h, d);
pv::RawVolume<VoxelInstance> volume(region);
auto lc = region.getLowerCorner();
@ -503,6 +504,12 @@ struct Module: public interface::Module, public voxelworld::Interface
for(int y = lc.getY(); y <= uc.getY(); y++){
for(int x = lc.getX(); x <= uc.getX(); x++){
volume.setVoxelAt(x, y, z, VoxelInstance(0));
/*if(x % 2 == 0){
if(z == lc.getZ() + 0)
volume.setVoxelAt(x, y, z, VoxelInstance(2));
if(z == uc.getZ() - 0)
volume.setVoxelAt(x, y, z, VoxelInstance(3));
}*/
}
}
}
@ -523,9 +530,9 @@ struct Module: public interface::Module, public voxelworld::Interface
PODVector<uint8_t>((const uint8_t*)data.c_str(), data.size())));
// There is no collision shape initially, but add the components now
RigidBody *body = n->CreateComponent<RigidBody>();
RigidBody *body = n->CreateComponent<RigidBody>(LOCAL);
body->SetFriction(0.75f);
CollisionShape *shape = n->CreateComponent<CollisionShape>();
CollisionShape *shape = n->CreateComponent<CollisionShape>(LOCAL);
}
void create_section(Section &section)
@ -641,10 +648,11 @@ struct Module: public interface::Module, public voxelworld::Interface
up_<pv::RawVolume<VoxelInstance>> volume =
interface::deserialize_volume(data);
// NOTE: +1 offset needed for mesh generation
pv::Vector3DInt32 voxel_p(
p.getX() - chunk_p.getX() * m_chunk_size_voxels.getX(),
p.getY() - chunk_p.getY() * m_chunk_size_voxels.getY(),
p.getZ() - chunk_p.getZ() * m_chunk_size_voxels.getZ()
p.getX() - chunk_p.getX() * m_chunk_size_voxels.getX() + 1,
p.getY() - chunk_p.getY() * m_chunk_size_voxels.getY() + 1,
p.getZ() - chunk_p.getZ() * m_chunk_size_voxels.getZ() + 1
);
log_t(MODULE, "set_voxel_direct() p=" PV3I_FORMAT ", v=%i: "
"Chunk " PV3I_FORMAT " in section " PV3I_FORMAT
@ -724,10 +732,11 @@ struct Module: public interface::Module, public voxelworld::Interface
for(const JournalEntry &entry : write_journal){
const pv::Vector3DInt32 &p = entry.p;
const interface::VoxelInstance &v = entry.v;
// NOTE: +1 offset needed for mesh generation
pv::Vector3DInt32 voxel_p(
p.getX() - chunk_p.getX() * m_chunk_size_voxels.getX(),
p.getY() - chunk_p.getY() * m_chunk_size_voxels.getY(),
p.getZ() - chunk_p.getZ() * m_chunk_size_voxels.getZ()
p.getX() - chunk_p.getX() * m_chunk_size_voxels.getX() + 1,
p.getY() - chunk_p.getY() * m_chunk_size_voxels.getY() + 1,
p.getZ() - chunk_p.getZ() * m_chunk_size_voxels.getZ() + 1
);
volume->setVoxelAt(voxel_p, v);
}
@ -740,6 +749,9 @@ struct Module: public interface::Module, public voxelworld::Interface
// Update collision shape
// TODO: Create multiple box collision shapes insteade of one mesh;
// vertical voxel sectors should work well enough maybe
SharedPtr<Model> model(interface::
create_voxel_physics_model(context, *volume,
m_voxel_reg.get()));
@ -749,6 +761,8 @@ struct Module: public interface::Module, public voxelworld::Interface
log_v(MODULE, "Chunk " PV3I_FORMAT " has collision shape",
PV3I_PARAMS(chunk_p));
shape->SetTriangleMesh(model, 0, Vector3::ONE);
//shape->SetConvexHull(model, 0, Vector3::ONE);
//log_w(MODULE, "CollisionShape disabled");
} else {
log_v(MODULE, "Chunk " PV3I_FORMAT " does not have collision shape",
PV3I_PARAMS(chunk_p));

View File

@ -38,3 +38,4 @@ Buildat TODO
- Use tags for module dependencies (tags, require_tag, disallow_tag), and
auto-tag each module at runtime with game_<current_game>
- Threaded mesh generation
- If an error occurs on the client in module initialization, show error and quit

View File

@ -248,6 +248,7 @@ function Safe.SubscribeToEvent(x, y, z)
local global_callback_name = "__buildat_sandbox_callback_"..global_function_i
sandbox_callback_to_global_function_name[callback] = global_callback_name
_G[global_callback_name] = function(event_type_thing, unsafe_event_data)
local error = error
local f = function()
-- How the hell does one get a string out of event_type_thing?
-- It is not a Variant, and none of the Lua examples try to do anything
@ -265,16 +266,32 @@ function Safe.SubscribeToEvent(x, y, z)
local safe_type = field_def.safe
local safe_value = nil
if variant_type == "Ptr" then
local get_type = field_def.get_type or safe_type
local unsafe_value = unsafe_event_data:GetPtr(
safe_type, field_name)
get_type, field_name)
if unsafe_value == nil then
error("Value for field "..dump(field_name).." as "..
dump(safe_type).." in "..dump(got_event_type)..
" gotten as "..dump(get_type).." is nil")
end
safe_value = wrap_instance(safe_type, unsafe_value)
safe_event_data["SetPtr"](
safe_event_data, field_name, safe_value)
else
local unsafe_value = unsafe_event_data["Get"..variant_type](
local get_type = field_def.get_type or variant_type
local unsafe_value = unsafe_event_data["Get"..get_type](
unsafe_event_data, field_name)
safe_value = magic_sandbox.unsafe_to_safe(unsafe_value, safe_type)
if safe_type == 'number' or safe_type == 'string' or
safe_type == 'boolean' then
-- Regular type
safe_value = magic_sandbox.unsafe_to_safe(unsafe_value, safe_type)
else
-- Object wrapper
safe_value = wrap_instance(safe_type, unsafe_value)
end
safe_event_data["Set"..get_type](
safe_event_data, field_name, safe_value)
end
safe_event_data["Set"..variant_type](
safe_event_data, field_name, safe_value)
end
-- Call callback
if object then

View File

@ -19,9 +19,21 @@ function M.define(dst, util)
instance = {
GetSize = util.self_function(
"GetSize", {"number"}, {"VectorBuffer"}),
ReadString = util.self_function(
"ReadString", {"string"}, {"VectorBuffer"}),
ReadInt = util.self_function(
"ReadInt", {"number"}, {"VectorBuffer"}),
ReadFloat = util.self_function(
"ReadFloat", {"number"}, {"VectorBuffer"}),
ReadVector3 = util.wrap_function({"VectorBuffer"},
function(self)
return util.wrap_instance("Vector3", self:ReadVector3())
end
),
},
properties = {
size = util.simple_property("number"),
eof = util.simple_property("boolean"),
},
})
@ -63,6 +75,10 @@ function M.define(dst, util)
"SetString", {}, {"VariantMap", "string", "string"}),
GetString = util.self_function(
"GetString", {"string"}, {"VariantMap", "string"}),
SetBuffer = util.self_function(
"SetBuffer", {}, {"VariantMap", "string", "VectorBuffer"}),
GetBuffer = util.self_function(
"GetBuffer", {dst.VectorBuffer}, {"VariantMap", "string"}),
SetPtr = util.self_function(
"SetPtr", {}, {"VariantMap", "string",
@ -83,6 +99,16 @@ function M.define(dst, util)
instance = {
Length = util.self_function(
"Length", {"number"}, {"Vector3"}),
CrossProduct = util.wrap_function({"Vector3", "Vector3"},
function(self, other)
return util.wrap_instance("Vector3", self:CrossProduct(other))
end
),
Normalized = util.wrap_function({"Vector3"},
function(self)
return util.wrap_instance("Vector3", self:Normalized())
end
),
},
instance_meta = {
__mul = util.wrap_function({"Vector3", "number"}, function(self, n)
@ -165,6 +191,17 @@ function M.define(dst, util)
},
})
util.wc("BoundingBox", {
unsafe_constructor = util.wrap_function({"number", "number"},
function(min, max) -- TOOD: Many alternative constructors
return util.wrap_instance("BoundingBox", BoundingBox(min, max))
end),
instance_meta = {
},
properties = {
},
})
util.wc("BiasParameters", {
unsafe_constructor = util.wrap_function({"number", "number"},
function(constant_bias, slope_scaled_bias)
@ -218,9 +255,19 @@ function M.define(dst, util)
util.wc("RigidBody", {
inherited_from_by_wrapper = dst.Component,
instance = {
ApplyForce = util.self_function(
"ApplyForce", {}, {"RigidBody", "Vector3"}),
ApplyImpulse = util.self_function(
"ApplyImpulse", {}, {"RigidBody", "Vector3"}),
},
properties = {
mass = util.simple_property("number"),
gravityOverride = util.simple_property(dst.Vector3),
friction = util.simple_property("number"),
angularFactor = util.simple_property(dst.Vector3),
kinematic = util.simple_property("boolean"),
linearVelocity = util.simple_property(dst.Vector3),
},
})
@ -229,6 +276,21 @@ function M.define(dst, util)
instance = {
SetBox = util.self_function(
"SetBox", {}, {"CollisionShape", "Vector3"}),
SetCapsule = util.self_function(
"SetCapsule", {}, {"CollisionShape", "number", "number"}),
},
})
util.wc("Zone", {
inherited_from_by_wrapper = dst.Component,
instance = {
},
properties = {
boundingBox = util.simple_property(dst.BoundingBox),
ambientColor = util.simple_property(dst.Color),
fogColor = util.simple_property(dst.Color),
fogStart = util.simple_property("number"),
fogEnd = util.simple_property("number"),
},
})
@ -581,6 +643,7 @@ function M.define(dst, util)
instance = {
SetMouseVisible = util.self_function("SetMouseVisible", {}, {"Input", "boolean"}),
GetKeyDown = util.self_function("GetKeyDown", {"boolean"}, {"Input", "number"}),
GetKeyPress = util.self_function("GetKeyPress", {"boolean"}, {"Input", "number"}),
GetMouseMove = util.self_function("GetMouseMove", {dst.IntVector2}, {"Input"}),
},
})

View File

@ -41,5 +41,12 @@ return {
Scene = {variant = "Ptr", safe = "Scene"},
Node = {variant = "Ptr", safe = "Node"},
},
PhysicsCollision = {
NodeA = {variant = "Ptr", safe = "Node"},
NodeB = {variant = "Ptr", safe = "Node"},
BodyA = {variant = "Ptr", safe = "RigidBody"},
BodyB = {variant = "Ptr", safe = "RigidBody"},
Contacts = {variant = "Buffer", safe = "VectorBuffer", get_type = "Buffer"},
},
}
-- vim: set noet ts=4 sw=4:

View File

@ -9,25 +9,47 @@ local magic = require("buildat/extension/urho3d")
local replicate = require("buildat/extension/replicate")
local voxelworld = require("buildat/module/voxelworld")
local PLAYER_HEIGHT = 1.7
local PLAYER_WIDTH = 0.9
local MOVE_SPEED = 10
local JUMP_SPEED = 7 -- Barely 2 voxels
local scene = replicate.main_scene
magic.input:SetMouseVisible(false)
local zone_node = scene:CreateChild("Zone")
local zone = zone_node:CreateComponent("Zone")
zone.boundingBox = magic.BoundingBox(-1000, 1000)
--zone.ambientColor = magic.Color(0.5, 0.7, 0.9)
zone.fogColor = magic.Color(0.6, 0.7, 0.8)
zone.fogStart = 10
zone.fogEnd = 300
-- Add a node that the player can use to walk around with
local player_node = scene:CreateChild("Player")
player_node.position = magic.Vector3(55, 30, 40)
---[[
local body = player_node:CreateComponent("RigidBody")
--body.mass = 70.0
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, 1))
--shape:SetBox(magic.Vector3(1, 1.7*PLAYER_SCALE, 1))
shape:SetCapsule(PLAYER_WIDTH, PLAYER_HEIGHT)
--]]
local other_node = scene:CreateChild("Other")
local player_touches_ground = false
--[[local other_node = scene:CreateChild("Other")
other_node.position = magic.Vector3(0, 10, 0)
local body = other_node:CreateComponent("RigidBody")
body.mass = 0
--body.friction = 0.7
local shape = other_node:CreateComponent("CollisionShape")
shape:SetBox(magic.Vector3(50, 1, 50))
shape:SetBox(magic.Vector3(50, 1, 50))]]
--[[
-- Add a camera so we can look at the scene
@ -41,12 +63,11 @@ camera.farClip = 500.0
-- Add a camera so we can look at the scene
local camera_node = player_node:CreateChild("Camera")
camera_node.position = magic.Vector3(0, 0.7, 0)
--camera_node:LookAt(magic.Vector3(30, 10, 0))
camera_node:LookAt(magic.Vector3(30, 20, 40))
camera_node.position = magic.Vector3(0, 0.411*PLAYER_HEIGHT, 0)
local camera = camera_node:CreateComponent("Camera")
camera.nearClip = 1.0
camera.nearClip = 0.3
camera.farClip = 500.0
camera.fov = 75
-- And this thing so the camera is shown on the screen
local viewport = magic.Viewport:new(scene, camera_node:GetComponent("Camera"))
@ -62,6 +83,13 @@ 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)
magic.ui:SetFocusElement(nil)
magic.SubscribeToEvent("KeyDown", function(event_type, event_data)
@ -73,30 +101,80 @@ magic.SubscribeToEvent("KeyDown", function(event_type, event_data)
end)
magic.SubscribeToEvent("Update", function(event_type, event_data)
--log:info("Update")
if player_node then
local dmouse = magic.input:GetMouseMove()
--log:info("dmouse: ("..dmouse.x..", "..dmouse.y..")")
camera_node:Pitch(dmouse.y * 0.1)
player_node:Yaw(dmouse.x * 0.1)
local MOVE_AMOUNT = event_data:GetFloat("TimeStep") * 20.0
local body = player_node:GetComponent("RigidBody")
local wanted_v = magic.Vector3(0, 0, 0)
if magic.input:GetKeyDown(magic.KEY_W) then
player_node:Translate(magic.Vector3( 1, 0, 0) * MOVE_AMOUNT)
wanted_v.x = wanted_v.x + 1
end
if magic.input:GetKeyDown(magic.KEY_S) then
player_node:Translate(magic.Vector3(-1, 0, 0) * MOVE_AMOUNT)
end
if magic.input:GetKeyDown(magic.KEY_A) then
player_node:Translate(magic.Vector3( 0, 0, 1) * MOVE_AMOUNT)
wanted_v.x = wanted_v.x - 1
end
if magic.input:GetKeyDown(magic.KEY_D) then
player_node:Translate(magic.Vector3( 0, 0,-1) * MOVE_AMOUNT)
wanted_v.z = wanted_v.z - 1
end
if magic.input:GetKeyDown(magic.KEY_A) then
wanted_v.z = wanted_v.z + 1
end
wanted_v = wanted_v:Normalized() * MOVE_SPEED
--if magic.input:GetKeyPress(magic.KEY_SPACE) then
if magic.input:GetKeyDown(magic.KEY_SPACE) then
player_node:Translate(magic.Vector3( 0, 1, 0) * MOVE_AMOUNT)
if player_touches_ground and
math.abs(body.linearVelocity.y) < JUMP_SPEED then
wanted_v.y = wanted_v.y + JUMP_SPEED
end
end
if magic.input:GetKeyDown(magic.KEY_SHIFT) then
player_node:Translate(magic.Vector3( 0,-1, 0) * MOVE_AMOUNT)
wanted_v.y = wanted_v.y - MOVE_SPEED
end
local u = player_node.direction
local v = u:CrossProduct(magic.Vector3(0, 1, 0))
local bv = body.linearVelocity
bv.x = 0
bv.z = 0
if wanted_v.y ~= 0 then
bv.y = 0
end
bv = bv + u * wanted_v.x
bv = bv + v * wanted_v.z
bv = bv + magic.Vector3(0, 1, 0) * wanted_v.y
body.linearVelocity = bv
local p = player_node:GetWorldPosition()
misc_text:SetText("("..math.floor(p.x + 0.5)..", "..
math.floor(p.y + 0.5)..", "..math.floor(p.z + 0.5)..")")
end
player_touches_ground = false
end)
magic.SubscribeToEvent("PhysicsCollision", function(event_type, event_data)
--log:info("PhysicsCollision")
local node_a = event_data:GetPtr("Node", "NodeA")
local node_b = event_data:GetPtr("Node", "NodeB")
local contacts = event_data:GetBuffer("Contacts")
if node_a:GetID() == player_node:GetID() or
node_b:GetID() == player_node:GetID() then
while not contacts.eof do
local position = contacts:ReadVector3()
local normal = contacts:ReadVector3()
local distance = contacts:ReadFloat()
local impulse = contacts:ReadFloat()
--log:info("normal: ("..normal.x..", "..normal.y..", "..normal.z..")")
if normal.y > 0.5 then
player_touches_ground = true
end
end
end
end)

View File

@ -126,10 +126,10 @@ struct Module: public interface::Module
create_8bit_voxel_physics_model(context, w, h, d, data,
voxel_reg));
RigidBody *body = n->CreateComponent<RigidBody>();
RigidBody *body = n->CreateComponent<RigidBody>(LOCAL);
body->SetFriction(0.75f);
body->SetMass(1.0);
CollisionShape *shape = n->CreateComponent<CollisionShape>();
CollisionShape *shape = n->CreateComponent<CollisionShape>(LOCAL);
shape->SetConvexHull(model, 0, Vector3::ONE);
//shape->SetTriangleMesh(model, 0, Vector3::ONE);
//shape->SetBox(Vector3::ONE);

View File

@ -111,6 +111,66 @@ Model* create_simple_voxel_model(Context *context,
return fromScratchModel;
}
// 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::RawVolume<VoxelInstance> volume(pv::Region(
pv::Vector3DInt32(-1, -1, -1),
pv::Vector3DInt32(w, h, d)));
size_t i = 0;
for(int z = -1; z <= d; z++){
for(int y = -1; y <= h; y++){
for(int x = -1; x <= w; x++){
if(z == -1 || y == -1 || x == -1 ||
z == d || y == h || x == w){
volume.setVoxelAt(x, y, z, VoxelInstance(0));
} else {
char c = source_data[i++];
volume.setVoxelAt(x, y, z, VoxelInstance(c));
}
}
}
}
return create_voxel_physics_model(context, volume, 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)
{
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::RawVolume<VoxelInstance> volume(pv::Region(
pv::Vector3DInt32(-1, -1, -1),
pv::Vector3DInt32(w, h, d)));
size_t i = 0;
for(int z = -1; z <= d; z++){
for(int y = -1; y <= h; y++){
for(int x = -1; x <= w; x++){
if(z == -1 || y == -1 || x == -1 ||
z == d || y == h || x == w){
volume.setVoxelAt(x, y, z, VoxelInstance(0));
} else {
char c = source_data[i++];
volume.setVoxelAt(x, y, z, VoxelInstance(c));
}
}
}
}
return set_voxel_geometry(cg, context, volume, voxel_reg);
}
template<typename VoxelType>
class IsQuadNeededByRegistryPhysics
{
@ -141,29 +201,79 @@ public:
}
};
// Create a model from 8-bit voxel data, using a voxel registry, without
// Create a model from voxel volume, 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,
// Volume should be padded by one voxel on each edge
// Returns nullptr if there is no geometry
Model* create_voxel_physics_model(Context *context,
pv::RawVolume<VoxelInstance> &volume,
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::RawVolume<VoxelInstance> volume(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++];
volume.setVoxelAt(x, y, z, VoxelInstance(c));
}
}
IsQuadNeededByRegistryPhysics<VoxelInstance> iqn(voxel_reg);
pv::SurfaceMesh<pv::PositionMaterialNormal> pv_mesh;
pv::CubicSurfaceExtractorWithNormals<pv::RawVolume<VoxelInstance>,
IsQuadNeededByRegistryPhysics<VoxelInstance>>
surfaceExtractor(&volume, volume.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();
if(num_indices == 0)
return nullptr;
int w = volume.getWidth() - 2;
int h = volume.getHeight() - 2;
int d = volume.getDepth() - 2;
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();
}
return create_voxel_physics_model(context, volume, voxel_reg);
//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>
@ -233,106 +343,8 @@ struct TemporaryGeometry
};
#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::RawVolume<VoxelInstance> volume(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++];
volume.setVoxelAt(x, y, z, VoxelInstance(c));
}
}
}
return set_voxel_geometry(cg, context, volume, voxel_reg);
}
// Create a model from voxel volume, using a voxel registry, without
// textures or normals, based on the physically_solid flag.
// Returns nullptr if there is no geometry
Model* create_voxel_physics_model(Context *context,
pv::RawVolume<VoxelInstance> &volume,
VoxelRegistry *voxel_reg)
{
IsQuadNeededByRegistryPhysics<VoxelInstance> iqn(voxel_reg);
pv::SurfaceMesh<pv::PositionMaterialNormal> pv_mesh;
pv::CubicSurfaceExtractorWithNormals<pv::RawVolume<VoxelInstance>,
IsQuadNeededByRegistryPhysics<VoxelInstance>>
surfaceExtractor(&volume, volume.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();
if(num_indices == 0)
return nullptr;
int w = volume.getWidth() - 1;
int h = volume.getHeight() - 1;
int d = volume.getDepth() - 1;
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;
}
// Set custom geometry from voxel volume, using a voxel registry
// Volume should be padded by one voxel on each edge
void set_voxel_geometry(CustomGeometry *cg, Context *context,
pv::RawVolume<VoxelInstance> &volume,
VoxelRegistry *voxel_reg)
@ -349,9 +361,9 @@ void set_voxel_geometry(CustomGeometry *cg, Context *context,
sm_<uint, TemporaryGeometry> temp_geoms;
int w = volume.getWidth() - 1;
int h = volume.getHeight() - 1;
int d = volume.getDepth() -1;
int w = volume.getWidth() - 2;
int h = volume.getHeight() - 2;
int d = volume.getDepth() - 2;
// 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++){

View File

@ -34,11 +34,13 @@ namespace interface
// Create a model from voxel volume, using a voxel registry, without
// textures or normals, based on the physically_solid flag.
// Returns nullptr if there is no geometry
// Volume should be padded by one voxel on each edge
// NOTE: volume is non-const due to PolyVox deficiency
Model* create_voxel_physics_model(Context *context,
pv::RawVolume<VoxelInstance> &volume,
VoxelRegistry *voxel_reg);
// Set custom geometry from voxel volume, using a voxel registry
// Volume should be padded by one voxel on each edge
// NOTE: volume is non-const due to PolyVox deficiency
void set_voxel_geometry(CustomGeometry *cg, Context *context,
pv::RawVolume<VoxelInstance> &volume,

View File

@ -12,6 +12,8 @@
#include <StaticModel.h>
#include <Model.h>
#include <CustomGeometry.h>
#include <CollisionShape.h>
#include <RigidBody.h>
#pragma GCC diagnostic pop
#define MODULE "lua_bindings"
@ -159,6 +161,20 @@ static int l_set_voxel_geometry(lua_State *L)
// TODO: Don't do this here; allow the caller to do this
cg->SetCastShadows(true);
// TODO: Don't do this here; allow caller to do this explicitly
SharedPtr<Model> model(interface::
create_voxel_physics_model(context, *volume, voxel_reg));
RigidBody *body = node->GetOrCreateComponent<RigidBody>(LOCAL);
body->SetFriction(0.7);
CollisionShape *shape = node->GetOrCreateComponent<CollisionShape>(LOCAL);
if(model){
shape->SetTriangleMesh(model, 0, Vector3::ONE);
//shape->SetConvexHull(model, 0, Vector3::ONE);
//log_w(MODULE, "CollisionShape disabled");
} else {
shape->ReleaseShape();
}
return 0;
}