builtin/voxelworld: WIP
This commit is contained in:
parent
cd3abc2048
commit
3f69453efe
@ -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());
|
||||
|
@ -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)
|
||||
|
@ -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 §ion)
|
||||
@ -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));
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"}),
|
||||
},
|
||||
})
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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++){
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user