Skip to content

Commit

Permalink
Add very basic Bullet physics integration.
Browse files Browse the repository at this point in the history
  • Loading branch information
Hans-Kristian Arntzen committed Mar 17, 2019
1 parent e9cb11a commit 73fe208
Show file tree
Hide file tree
Showing 28 changed files with 684 additions and 66 deletions.
19 changes: 17 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ option(GRANITE_HIDDEN "Declare symbols as hidden by default. Useful if you build
option(GRANITE_SANITIZE_ADDRESS "Sanitize address" OFF)
option(GRANITE_ANDROID_AUDIO_OBOE "Use the Oboe library for audio." ON)
option(GRANITE_TARGET_NATIVE "Target native arch (-march=native)" OFF)
option(GRANITE_BULLET "Enable Bullet support." OFF)

if (GRANITE_HIDDEN)
if (CMAKE_COMPILER_IS_GNUCXX OR (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang"))
Expand Down Expand Up @@ -231,8 +232,8 @@ function(add_granite_offline_tool NAME)
endif()
endfunction()

set(GRANITE_ISPC_LIBRARY_DIR "" CACHE TYPE PATH)
set(GRANITE_ISPC_INCLUDE_DIR "" CACHE TYPE PATH)
set(GRANITE_ISPC_LIBRARY_DIR "" CACHE STRING "ISPC library directory." FORCE)
set(GRANITE_ISPC_INCLUDE_DIR "" CACHE STRING "ISPC include directory." FORCE)

target_include_directories(granite PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/util ${CMAKE_CURRENT_SOURCE_DIR}/vulkan)

Expand Down Expand Up @@ -525,6 +526,20 @@ if (NOT GRANITE_VULKAN_ONLY)
endif()
endif()

if (GRANITE_BULLET)
find_package(Bullet REQUIRED)
message("Found Bullet, enabling support.")
add_library(granite-physics INTERFACE)
target_include_directories(granite-physics INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/physics)
target_link_libraries(granite-physics INTERFACE ${BULLET_LIBRARIES})
target_include_directories(granite-physics INTERFACE ${BULLET_ROOT_DIR}/${BULLET_INCLUDE_DIR})
target_compile_definitions(granite-physics INTERFACE ${BULLET_DEFINITIONS})
target_sources(granite PRIVATE physics/physics_system.cpp physics/physics_system.hpp)
target_include_directories(granite PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/physics)
target_compile_definitions(granite PUBLIC HAVE_GRANITE_PHYSICS=1)
target_link_libraries(granite PRIVATE granite-physics)
endif()

if (GRANITE_ISPC_TEXTURE_COMPRESSION)
find_library(ISPC_LIBRARY NAMES ispc_texcomp HINTS ${GRANITE_ISPC_LIBRARY_DIR})
find_path(ISPC_INCLUDE_DIR NAMES ispc_texcomp.h HINTS ${GRANITE_ISPC_INCLUDE_DIR})
Expand Down
30 changes: 30 additions & 0 deletions application/global_managers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
#include "audio_events.hpp"
#endif

#ifdef HAVE_GRANITE_PHYSICS
#include "physics_system.hpp"
#endif

namespace Granite
{
namespace Global
Expand All @@ -52,6 +56,9 @@ struct GlobalManagers
Audio::Backend *audio_backend;
Audio::Mixer *audio_mixer;
#endif
#ifdef HAVE_GRANITE_PHYSICS
PhysicsSystem *physics;
#endif
};

static GlobalManagers global_managers;
Expand Down Expand Up @@ -126,6 +133,19 @@ void install_audio_system(Audio::Backend *backend, Audio::Mixer *mixer)
}
#endif

#ifdef HAVE_GRANITE_PHYSICS
PhysicsSystem *physics()
{
if (!global_managers.physics)
{
LOGI("Physics system was not initialized. Lazily initializing. This is not thread safe!\n");
global_managers.physics = new PhysicsSystem;
}

return global_managers.physics;
}
#endif

void init(ManagerFeatureFlags flags)
{
if (flags & MANAGER_FEATURE_EVENT_BIT)
Expand Down Expand Up @@ -161,6 +181,12 @@ void init(ManagerFeatureFlags flags)
global_managers.common_renderer_data = new CommonRendererData;
}

if (flags & MANAGER_FEATURE_PHYSICS_BIT)
{
if (!global_managers.physics)
global_managers.physics = new PhysicsSystem;
}

#ifdef HAVE_GRANITE_AUDIO
if (!global_managers.audio_mixer)
global_managers.audio_mixer = new Audio::Mixer;
Expand All @@ -181,6 +207,10 @@ void deinit()
global_managers.audio_mixer = nullptr;
#endif

#ifdef HAVE_GRANITE_PHYSICS
delete global_managers.physics;
#endif

delete global_managers.common_renderer_data;
delete global_managers.ui_manager;
delete global_managers.thread_group;
Expand Down
6 changes: 6 additions & 0 deletions application/global_managers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class Filesystem;
class ThreadGroup;
class EventManager;
class CommonRendererData;
class PhysicsSystem;
namespace UI
{
class UIManager;
Expand All @@ -51,6 +52,7 @@ enum ManagerFeatureFlagBits
MANAGER_FEATURE_UI_MANAGER_BIT = 1 << 3,
MANAGER_FEATURE_AUDIO_BIT = 1 << 4,
MANAGER_FEATURE_COMMON_RENDERER_DATA_BIT = 1 << 5,
MANAGER_FEATURE_PHYSICS_BIT = 1 << 6,
MANAGER_FEATURE_ALL_BITS = 0x7fffffff
};
using ManagerFeatureFlags = uint32_t;
Expand All @@ -71,6 +73,10 @@ Audio::Backend *audio_backend();
Audio::Mixer *audio_mixer();
void install_audio_system(Audio::Backend *backend, Audio::Mixer *mixer);
#endif

#ifdef HAVE_GRANITE_PHYSICS
PhysicsSystem *physics();
#endif
}

}
8 changes: 4 additions & 4 deletions application/scene_viewer_application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -367,9 +367,9 @@ void SceneViewerApplication::rescale_scene(float radius)
AABB aabb(vec3(FLT_MAX), vec3(-FLT_MAX));
auto &objects = scene_loader.get_scene()
.get_entity_pool()
.get_component_group<CachedSpatialTransformComponent, RenderableComponent>();
.get_component_group<RenderInfoComponent, RenderableComponent>();
for (auto &caster : objects)
aabb.expand(get_component<CachedSpatialTransformComponent>(caster)->world_aabb);
aabb.expand(get_component<RenderInfoComponent>(caster)->world_aabb);

float scale_factor = radius / aabb.get_radius();
auto root_node = scene_loader.get_scene().get_root_node();
Expand Down Expand Up @@ -1008,10 +1008,10 @@ void SceneViewerApplication::update_shadow_scene_aabb()
auto &scene = scene_loader.get_scene();
auto &shadow_casters =
scene.get_entity_pool()
.get_component_group<CachedSpatialTransformComponent, RenderableComponent, CastsStaticShadowComponent>();
.get_component_group<RenderInfoComponent, RenderableComponent, CastsStaticShadowComponent>();
AABB aabb(vec3(FLT_MAX), vec3(-FLT_MAX));
for (auto &caster : shadow_casters)
aabb.expand(get_component<CachedSpatialTransformComponent>(caster)->world_aabb);
aabb.expand(get_component<RenderInfoComponent>(caster)->world_aabb);
shadow_scene_aabb = aabb;
}

Expand Down
169 changes: 169 additions & 0 deletions physics/physics_system.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/* Copyright (c) 2017-2019 Hans-Kristian Arntzen
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include "physics_system.hpp"
#include <btBulletDynamicsCommon.h>
#include <btBulletCollisionCommon.h>

using namespace std;

namespace Granite
{
struct PhysicsHandle
{
Scene::Node *node = nullptr;
btCollisionObject *bt_object = nullptr;
btCollisionShape *bt_shape = nullptr;
};

PhysicsSystem::PhysicsSystem()
{
collision_config = make_unique<btDefaultCollisionConfiguration>();
dispatcher = make_unique<btCollisionDispatcher>(collision_config.get());
broadphase = make_unique<btDbvtBroadphase>();
solver = make_unique<btSequentialImpulseConstraintSolver>();
world = make_unique<btDiscreteDynamicsWorld>(dispatcher.get(), broadphase.get(),
solver.get(), collision_config.get());

world->setGravity(btVector3(0.0f, -9.81f, 0.0f));
}

PhysicsSystem::~PhysicsSystem()
{
for (int i = world->getNumCollisionObjects() - 1; i >= 0; i--)
{
auto *obj = world->getCollisionObjectArray()[i];
btRigidBody *body = btRigidBody::upcast(obj);
if (body && body->getMotionState())
delete body->getMotionState();
world->removeCollisionObject(obj);
}

for (auto *handle : handles)
delete handle->bt_shape;
}

void PhysicsSystem::iterate(double frame_time)
{
world->stepSimulation(btScalar(frame_time), 10);

for (auto *handle : handles)
{
if (!handle->node)
continue;

auto *obj = handle->bt_object;
auto *body = btRigidBody::upcast(obj);
btTransform t;
if (body && body->getMotionState())
body->getMotionState()->getWorldTransform(t);
else
t = obj->getWorldTransform();

auto rot = t.getRotation();
auto &transform = handle->node->transform;
transform.rotation.x = rot.x();
transform.rotation.y = rot.y();
transform.rotation.z = rot.z();
transform.rotation.w = rot.w();

auto orig = t.getOrigin();
transform.translation.x = orig.x();
transform.translation.y = orig.y();
transform.translation.z = orig.z();

handle->node->invalidate_cached_transform();
}
}

void PhysicsSystem::remove_body(PhysicsHandle *handle)
{
auto *obj = handle->bt_object;
btRigidBody *body = btRigidBody::upcast(obj);
if (body && body->getMotionState())
delete body->getMotionState();
world->removeCollisionObject(obj);
delete handle->bt_shape;
handle_pool.free(handle);

// TODO: Avoid O(n).
auto itr = find(begin(handles), end(handles), handle);
if (itr != end(handles))
handles.erase(itr);
}

PhysicsHandle *PhysicsSystem::add_shape(Scene::Node *node, float mass, btCollisionShape *shape)
{
btTransform t;
t.setIdentity();
btVector3 local_inertia(0, 0, 0);
if (mass != 0.0f)
shape->calculateLocalInertia(mass, local_inertia);

if (node)
{
t.setOrigin(btVector3(
node->transform.translation.x,
node->transform.translation.y,
node->transform.translation.z));
t.setRotation(btQuaternion(
node->transform.rotation.x,
node->transform.rotation.y,
node->transform.rotation.z,
node->transform.rotation.w));
}

auto *motion = new btDefaultMotionState(t);
btRigidBody::btRigidBodyConstructionInfo rb_info(mass, motion, shape, local_inertia);

auto *body = new btRigidBody(rb_info);
world->addRigidBody(body);

auto *handle = handle_pool.allocate();
handle->node = node;
handle->bt_object = body;
handle->bt_shape = shape;
handles.push_back(handle);
return handle;
}

PhysicsHandle *PhysicsSystem::add_cube(Scene::Node *node, float mass)
{
auto *shape = new btBoxShape(btVector3(node->transform.scale.x,
node->transform.scale.y,
node->transform.scale.z));
return add_shape(node, mass, shape);
}

PhysicsHandle *PhysicsSystem::add_sphere(Scene::Node *node, float mass)
{
auto *shape = new btSphereShape(btScalar(node->transform.scale.x));
return add_shape(node, mass, shape);
}

PhysicsHandle *PhysicsSystem::add_infinite_plane(const vec4 &plane)
{
auto *shape = new btStaticPlaneShape(btVector3(plane.x, plane.y, plane.z), plane.w);
return add_shape(nullptr, 0.0f, shape);
}

}
66 changes: 66 additions & 0 deletions physics/physics_system.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/* Copyright (c) 2017-2019 Hans-Kristian Arntzen
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#pragma once

#include "scene.hpp"
#include "object_pool.hpp"
#include "math.hpp"
#include <memory>

class btDefaultCollisionConfiguration;
class btCollisionDispatcher;
class btDbvtBroadphase;
class btSequentialImpulseConstraintSolver;
class btDiscreteDynamicsWorld;
class btCollisionShape;

namespace Granite
{
struct PhysicsHandle;

class PhysicsSystem
{
public:
PhysicsSystem();
~PhysicsSystem();

PhysicsHandle *add_cube(Scene::Node *node, float mass);
PhysicsHandle *add_sphere(Scene::Node *node, float mass);
PhysicsHandle *add_infinite_plane(const vec4 &plane);
void remove_body(PhysicsHandle *handle);

void iterate(double frame_time);

private:
std::unique_ptr<btDefaultCollisionConfiguration> collision_config;
std::unique_ptr<btCollisionDispatcher> dispatcher;
std::unique_ptr<btDbvtBroadphase> broadphase;
std::unique_ptr<btSequentialImpulseConstraintSolver> solver;
std::unique_ptr<btDiscreteDynamicsWorld> world;

Util::ObjectPool<PhysicsHandle> handle_pool;
std::vector<PhysicsHandle *> handles;

PhysicsHandle *add_shape(Scene::Node *node, float mass, btCollisionShape *shape);
};
}
Loading

0 comments on commit 73fe208

Please sign in to comment.