Add optional physics collision through node-less static bodies

This commit is contained in:
Marc Gilleron 2019-08-25 13:04:49 +01:00
parent 332041751d
commit 4ff5f91e6f
8 changed files with 228 additions and 10 deletions

View File

@ -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;
}

View File

@ -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;

View File

@ -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");
}

View File

@ -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;

View File

@ -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);

View File

@ -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
View 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
View 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