Add optional physics collision through node-less static bodies
This commit is contained in:
parent
332041751d
commit
4ff5f91e6f
@ -1,5 +1,5 @@
|
||||
#include "voxel_block.h"
|
||||
#include <scene/resources/world.h>
|
||||
#include <scene/3d/spatial.h>
|
||||
|
||||
// Helper
|
||||
VoxelBlock *VoxelBlock::create(Vector3i bpos, Ref<VoxelBuffer> buffer, unsigned int size, unsigned int p_lod_index) {
|
||||
@ -22,7 +22,7 @@ VoxelBlock::VoxelBlock() {
|
||||
VoxelBlock::~VoxelBlock() {
|
||||
}
|
||||
|
||||
void VoxelBlock::set_mesh(Ref<Mesh> mesh, Ref<World> world) {
|
||||
void VoxelBlock::set_mesh(Ref<Mesh> mesh, Spatial *node, bool generate_collision, bool debug_collision) {
|
||||
// TODO Don't add mesh instance to the world if it's not visible.
|
||||
// I suspect Godot is trying to include invisible mesh instances into the culling process,
|
||||
// which is killing performance when LOD is used (i.e many meshes are in pool but hidden)
|
||||
@ -30,23 +30,47 @@ void VoxelBlock::set_mesh(Ref<Mesh> mesh, Ref<World> world) {
|
||||
|
||||
if (mesh.is_valid()) {
|
||||
|
||||
ERR_FAIL_COND(node == nullptr);
|
||||
Ref<World> world = node->get_world();
|
||||
ERR_FAIL_COND(world.is_null());
|
||||
|
||||
if (!_mesh_instance.is_valid()) {
|
||||
// Create instance if it doesn't exist
|
||||
ERR_FAIL_COND(world.is_null());
|
||||
_mesh_instance.create();
|
||||
_mesh_instance.set_world(*world);
|
||||
}
|
||||
|
||||
Transform transform(Basis(), _position_in_voxels.to_vec3());
|
||||
|
||||
_mesh_instance.set_mesh(mesh);
|
||||
_mesh_instance.set_transform(Transform(Basis(), _position_in_voxels.to_vec3()));
|
||||
_mesh_instance.set_transform(transform);
|
||||
// TODO The day VoxelTerrain becomes a Spatial, this transform will need to be updatable separately
|
||||
|
||||
if (generate_collision) {
|
||||
Ref<Shape> shape = mesh->create_trimesh_shape();
|
||||
|
||||
if (!_static_body.is_valid()) {
|
||||
_static_body.create();
|
||||
_static_body.set_world(*world);
|
||||
_static_body.set_attached_object(node);
|
||||
_static_body.set_transform(transform);
|
||||
} else {
|
||||
_static_body.remove_shape(0);
|
||||
}
|
||||
_static_body.add_shape(shape);
|
||||
_static_body.set_debug(debug_collision, *world);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (_mesh_instance.is_valid()) {
|
||||
// Delete instance if it exists
|
||||
_mesh_instance.destroy();
|
||||
}
|
||||
|
||||
if (_static_body.is_valid()) {
|
||||
_static_body.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
++_mesh_update_count;
|
||||
@ -80,12 +104,18 @@ void VoxelBlock::set_world(World *world) {
|
||||
if (_mesh_instance.is_valid()) {
|
||||
_mesh_instance.set_world(world);
|
||||
}
|
||||
if (_static_body.is_valid()) {
|
||||
_static_body.set_world(world);
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelBlock::set_visible(bool visible) {
|
||||
if (_mesh_instance.is_valid()) {
|
||||
_mesh_instance.set_visible(visible);
|
||||
}
|
||||
if (_static_body.is_valid()) {
|
||||
_static_body.set_shape_enabled(0, visible);
|
||||
}
|
||||
_visible = visible;
|
||||
}
|
||||
|
||||
|
@ -2,8 +2,11 @@
|
||||
#define VOXEL_BLOCK_H
|
||||
|
||||
#include "../util/direct_mesh_instance.h"
|
||||
#include "../util/direct_static_body.h"
|
||||
#include "../voxel_buffer.h"
|
||||
|
||||
class Spatial;
|
||||
|
||||
// Internal structure holding a reference to mesh visuals, physics and a block of voxel data.
|
||||
class VoxelBlock {
|
||||
public:
|
||||
@ -23,7 +26,7 @@ public:
|
||||
|
||||
~VoxelBlock();
|
||||
|
||||
void set_mesh(Ref<Mesh> mesh, Ref<World> world);
|
||||
void set_mesh(Ref<Mesh> mesh, Spatial *node, bool generate_collision, bool debug_collision);
|
||||
bool has_mesh() const;
|
||||
|
||||
void set_mesh_state(MeshState ms);
|
||||
@ -46,6 +49,7 @@ private:
|
||||
Vector3i _position_in_voxels;
|
||||
|
||||
DirectMeshInstance _mesh_instance;
|
||||
DirectStaticBody _static_body;
|
||||
|
||||
int _mesh_update_count = 0;
|
||||
bool _visible = true;
|
||||
|
@ -309,6 +309,18 @@ int VoxelLodTerrain::get_lod_count() const {
|
||||
return _lod_octree.get_lod_count();
|
||||
}
|
||||
|
||||
void VoxelLodTerrain::set_generate_collisions(bool enabled) {
|
||||
_generate_collisions = enabled;
|
||||
}
|
||||
|
||||
void VoxelLodTerrain::set_collision_lod_count(int lod_count) {
|
||||
_collision_lod_count = CLAMP(lod_count, -1, get_lod_count());
|
||||
}
|
||||
|
||||
int VoxelLodTerrain::get_collision_lod_count() const {
|
||||
return _collision_lod_count;
|
||||
}
|
||||
|
||||
void VoxelLodTerrain::set_viewer_path(NodePath path) {
|
||||
_viewer_path = path;
|
||||
}
|
||||
@ -888,7 +900,12 @@ void VoxelLodTerrain::_process() {
|
||||
mesh = Ref<Mesh>();
|
||||
}
|
||||
|
||||
block->set_mesh(mesh, world);
|
||||
bool has_collision = _generate_collisions;
|
||||
if (has_collision && _collision_lod_count != -1) {
|
||||
has_collision = ob.lod < _collision_lod_count;
|
||||
}
|
||||
|
||||
block->set_mesh(mesh, this, has_collision, get_tree()->is_debugging_collisions_hint());
|
||||
block->mark_been_meshed();
|
||||
}
|
||||
|
||||
@ -931,6 +948,12 @@ void VoxelLodTerrain::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_view_distance", "distance_in_voxels"), &VoxelLodTerrain::set_view_distance);
|
||||
ClassDB::bind_method(D_METHOD("get_view_distance"), &VoxelLodTerrain::get_view_distance);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_generate_collisions"), &VoxelLodTerrain::get_generate_collisions);
|
||||
ClassDB::bind_method(D_METHOD("set_generate_collisions", "enabled"), &VoxelLodTerrain::set_generate_collisions);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_collision_lod_count"), &VoxelLodTerrain::get_collision_lod_count);
|
||||
ClassDB::bind_method(D_METHOD("set_collision_lod_count", "count"), &VoxelLodTerrain::set_collision_lod_count);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_viewer_path"), &VoxelLodTerrain::get_viewer_path);
|
||||
ClassDB::bind_method(D_METHOD("set_viewer_path", "path"), &VoxelLodTerrain::set_viewer_path);
|
||||
|
||||
@ -953,4 +976,6 @@ void VoxelLodTerrain::_bind_methods() {
|
||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "lod_split_scale"), "set_lod_split_scale", "get_lod_split_scale");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "viewer_path"), "set_viewer_path", "get_viewer_path");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "Material"), "set_material", "get_material");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "generate_collisions"), "set_generate_collisions", "get_generate_collisions");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_lod_count"), "set_collision_lod_count", "get_collision_lod_count");
|
||||
}
|
||||
|
@ -38,6 +38,13 @@ public:
|
||||
void set_lod_count(int p_lod_count);
|
||||
int get_lod_count() const;
|
||||
|
||||
void set_generate_collisions(bool enabled);
|
||||
bool get_generate_collisions() const { return _generate_collisions; }
|
||||
|
||||
// Sets up to which amount of LODs collision will generate. -1 means all of them.
|
||||
void set_collision_lod_count(int lod_count);
|
||||
int get_collision_lod_count() const;
|
||||
|
||||
void set_viewer_path(NodePath path);
|
||||
NodePath get_viewer_path() const;
|
||||
|
||||
@ -111,6 +118,9 @@ private:
|
||||
|
||||
Ref<Material> _material;
|
||||
|
||||
bool _generate_collisions = true;
|
||||
int _collision_lod_count = -1;
|
||||
|
||||
// Each LOD works in a set of coordinates spanning 2x more voxels the higher their index is
|
||||
struct Lod {
|
||||
Ref<VoxelMap> map;
|
||||
|
@ -25,7 +25,6 @@ VoxelTerrain::VoxelTerrain() {
|
||||
_stream_thread = NULL;
|
||||
_block_updater = NULL;
|
||||
|
||||
_generate_collisions = false;
|
||||
_run_in_editor = false;
|
||||
_smooth_meshing_enabled = false;
|
||||
}
|
||||
@ -936,7 +935,7 @@ void VoxelTerrain::_process() {
|
||||
CRASH_COND(*block_state != BLOCK_UPDATE_NOT_SENT);
|
||||
|
||||
// The block contains empty voxels
|
||||
block->set_mesh(Ref<Mesh>(), Ref<World>());
|
||||
block->set_mesh(Ref<Mesh>(), this, _generate_collisions, get_tree()->is_debugging_collisions_hint());
|
||||
_dirty_blocks.erase(block_pos);
|
||||
|
||||
// Optional, but I guess it might spare some memory
|
||||
@ -1058,7 +1057,7 @@ void VoxelTerrain::_process() {
|
||||
mesh = Ref<Mesh>();
|
||||
}
|
||||
|
||||
block->set_mesh(mesh, world);
|
||||
block->set_mesh(mesh, this, _generate_collisions, get_tree()->is_debugging_collisions_hint());
|
||||
}
|
||||
|
||||
shift_up(_blocks_pending_main_thread_update, queue_index);
|
||||
|
@ -158,7 +158,7 @@ private:
|
||||
Vector3i _last_viewer_block_pos;
|
||||
int _last_view_distance_blocks;
|
||||
|
||||
bool _generate_collisions;
|
||||
bool _generate_collisions = true;
|
||||
bool _run_in_editor;
|
||||
bool _smooth_meshing_enabled;
|
||||
|
||||
|
115
util/direct_static_body.cpp
Normal file
115
util/direct_static_body.cpp
Normal file
@ -0,0 +1,115 @@
|
||||
#include "direct_static_body.h"
|
||||
|
||||
#include <scene/resources/world.h>
|
||||
#include <servers/physics/physics_server_sw.h>
|
||||
|
||||
DirectStaticBody::DirectStaticBody() {
|
||||
}
|
||||
|
||||
DirectStaticBody::~DirectStaticBody() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
void DirectStaticBody::create() {
|
||||
ERR_FAIL_COND(_body.is_valid());
|
||||
PhysicsServer &ps = *PhysicsServer::get_singleton();
|
||||
_body = ps.body_create(PhysicsServer::BODY_MODE_STATIC);
|
||||
ps.body_set_ray_pickable(_body, false);
|
||||
}
|
||||
|
||||
void DirectStaticBody::destroy() {
|
||||
if (_body.is_valid()) {
|
||||
PhysicsServer &ps = *PhysicsServer::get_singleton();
|
||||
ps.free(_body);
|
||||
_body = RID();
|
||||
// The shape need to be destroyed after the body
|
||||
_shape.unref();
|
||||
}
|
||||
if (_debug_mesh_instance.is_valid()) {
|
||||
_debug_mesh_instance.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
bool DirectStaticBody::is_valid() const {
|
||||
return _body.is_valid();
|
||||
}
|
||||
|
||||
void DirectStaticBody::set_transform(Transform transform) {
|
||||
ERR_FAIL_COND(!_body.is_valid());
|
||||
PhysicsServer::get_singleton()->body_set_state(_body, PhysicsServer::BODY_STATE_TRANSFORM, transform);
|
||||
|
||||
if (_debug_mesh_instance.is_valid()) {
|
||||
_debug_mesh_instance.set_transform(transform);
|
||||
}
|
||||
}
|
||||
|
||||
void DirectStaticBody::add_shape(Ref<Shape> shape) {
|
||||
ERR_FAIL_COND(!_body.is_valid());
|
||||
PhysicsServer::get_singleton()->body_add_shape(_body, shape->get_rid(), Transform(), false);
|
||||
// No use case for multishape yet
|
||||
_shape = shape;
|
||||
|
||||
if (_debug_mesh_instance.is_valid()) {
|
||||
Ref<Mesh> mesh = _shape->get_debug_mesh();
|
||||
_debug_mesh_instance.set_mesh(mesh);
|
||||
}
|
||||
}
|
||||
|
||||
void DirectStaticBody::remove_shape(int shape_index) {
|
||||
ERR_FAIL_COND(!_body.is_valid());
|
||||
PhysicsServer::get_singleton()->body_remove_shape(_body, shape_index);
|
||||
_shape.unref();
|
||||
|
||||
if (_debug_mesh_instance.is_valid()) {
|
||||
_debug_mesh_instance.set_mesh(Ref<Mesh>());
|
||||
}
|
||||
}
|
||||
|
||||
void DirectStaticBody::set_world(World *world) {
|
||||
ERR_FAIL_COND(!_body.is_valid());
|
||||
PhysicsServer &ps = *PhysicsServer::get_singleton();
|
||||
ps.body_set_space(_body, world != nullptr ? world->get_space() : RID());
|
||||
|
||||
if (_debug_mesh_instance.is_valid()) {
|
||||
_debug_mesh_instance.set_world(world);
|
||||
}
|
||||
}
|
||||
|
||||
void DirectStaticBody::set_shape_enabled(int shape_index, bool enabled) {
|
||||
ERR_FAIL_COND(!_body.is_valid());
|
||||
PhysicsServer &ps = *PhysicsServer::get_singleton();
|
||||
ps.body_set_shape_disabled(_body, shape_index, !enabled);
|
||||
|
||||
if (_debug_mesh_instance.is_valid()) {
|
||||
_debug_mesh_instance.set_visible(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
void DirectStaticBody::set_attached_object(Object *obj) {
|
||||
// Serves in high-level collision query results, `collider` will contain the attached object
|
||||
ERR_FAIL_COND(!_body.is_valid());
|
||||
PhysicsServer::get_singleton()->body_attach_object_instance_id(_body, obj != nullptr ? obj->get_instance_id() : 0);
|
||||
}
|
||||
|
||||
void DirectStaticBody::set_debug(bool enabled, World *world) {
|
||||
|
||||
ERR_FAIL_COND(world == nullptr);
|
||||
|
||||
if (enabled && !_debug_mesh_instance.is_valid()) {
|
||||
|
||||
_debug_mesh_instance.create();
|
||||
_debug_mesh_instance.set_world(world);
|
||||
|
||||
Transform transform = PhysicsServer::get_singleton()->body_get_state(_body, PhysicsServer::BODY_STATE_TRANSFORM);
|
||||
_debug_mesh_instance.set_transform(transform);
|
||||
|
||||
if (_shape.is_valid()) {
|
||||
Ref<Mesh> mesh = _shape->get_debug_mesh();
|
||||
_debug_mesh_instance.set_mesh(mesh);
|
||||
}
|
||||
|
||||
} else if (!enabled && _debug_mesh_instance.is_valid()) {
|
||||
|
||||
_debug_mesh_instance.destroy();
|
||||
}
|
||||
}
|
35
util/direct_static_body.h
Normal file
35
util/direct_static_body.h
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef DIRECT_STATIC_BODY_H
|
||||
#define DIRECT_STATIC_BODY_H
|
||||
|
||||
#include "direct_mesh_instance.h"
|
||||
|
||||
#include <core/rid.h>
|
||||
#include <scene/resources/shape.h>
|
||||
|
||||
class World;
|
||||
|
||||
// Thin wrapper around static body API
|
||||
class DirectStaticBody {
|
||||
public:
|
||||
DirectStaticBody();
|
||||
~DirectStaticBody();
|
||||
|
||||
void create();
|
||||
void destroy();
|
||||
bool is_valid() const;
|
||||
void set_transform(Transform transform);
|
||||
void add_shape(Ref<Shape> shape);
|
||||
void remove_shape(int shape_index);
|
||||
void set_world(World *world);
|
||||
void set_shape_enabled(int shape_index, bool disabled);
|
||||
void set_attached_object(Object *obj);
|
||||
|
||||
void set_debug(bool enabled, World *world);
|
||||
|
||||
private:
|
||||
RID _body;
|
||||
Ref<Shape> _shape;
|
||||
DirectMeshInstance _debug_mesh_instance;
|
||||
};
|
||||
|
||||
#endif // DIRECT_STATIC_BODY_H
|
Loading…
x
Reference in New Issue
Block a user