Skip to content

Commit

Permalink
feat(engine): add voxel collision shape
Browse files Browse the repository at this point in the history
  • Loading branch information
joaomanita authored and joaomanita committed Oct 9, 2024
1 parent 4ebb4a3 commit 9a654da
Show file tree
Hide file tree
Showing 14 changed files with 852 additions and 60 deletions.
2 changes: 1 addition & 1 deletion core/include/cubos/core/geom/box.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ namespace cubos::core::geom
}

/// @brief Computes four corners of the box, one for each diagonal.
/// @param corners Array to store the three corners in.
/// @param corners Array to store the four corners in.
void corners4(glm::vec3 corners[4]) const
{
corners[0] = {halfSize.x, -halfSize.y, -halfSize.z};
Expand Down
1 change: 0 additions & 1 deletion core/src/geom/intersections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@ bool cubos::core::geom::intersects(const Box& box1, const glm::mat4& localToWorl
}
}
}

return true;
}

Expand Down
1 change: 1 addition & 0 deletions engine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ set(CUBOS_ENGINE_SOURCE
"src/collisions/interface/shapes/box.cpp"
"src/collisions/interface/raycast.cpp"
"src/collisions/interface/shapes/capsule.cpp"
"src/collisions/interface/shapes/voxel.cpp"
"src/collisions/broad_phase/plugin.cpp"
"src/collisions/broad_phase/sweep_and_prune.cpp"
"src/collisions/broad_phase/potentially_colliding_with.cpp"
Expand Down
97 changes: 97 additions & 0 deletions engine/include/cubos/engine/collisions/shapes/voxel.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/// @file
/// @brief Component @ref cubos::engine::VoxelCollisionShape.
/// @ingroup collisions-plugin

#pragma once

#include <glm/glm.hpp>

#include <cubos/core/geom/box.hpp>
#include <cubos/core/log.hpp>
#include <cubos/core/reflection/external/glm.hpp>
#include <cubos/core/reflection/reflect.hpp>

#include <cubos/engine/assets/asset.hpp>
#include <cubos/engine/voxels/grid.hpp>

namespace cubos::engine
{
/// @brief Component which adds a collision shape corresponding to a given voxel grid to an entity, used with a @ref
/// Collider component.
/// @ingroup collisions-plugin
class VoxelCollisionShape
{

public:
CUBOS_REFLECT;

/// @brief Struct which holds a sub-box of the voxel collision shape, and its shift from the center of the
/// shape.
/// @ingroup collisions-plugin
struct BoxShiftPair
{
cubos::core::geom::Box box;
glm::vec3 shift;
};

/// @brief Entities voxel grid.
Asset<VoxelGrid> grid;

/// @brief Constructs voxel shape with no grid.
VoxelCollisionShape() = default;

/// @brief Constructs voxel shape with voxel grid.
/// @param grid VoxelGrid given in constructor.
VoxelCollisionShape(Asset<VoxelGrid> grid)
{
setGrid(grid);
}

/// @brief Default destructor.
~VoxelCollisionShape() = default;

/// @brief Move constructor.
/// @param other VoxelCollisionShape to move.
VoxelCollisionShape(VoxelCollisionShape&& other) noexcept
{
this->grid = std::move(other.grid);
this->mBoxes = std::move(other.mBoxes);
}

/// @brief Copy constructor.
/// @param shape VoxelCollisionSHape to copy.
VoxelCollisionShape(const VoxelCollisionShape& shape)
{
this->grid = shape.grid;
this->mBoxes = shape.mBoxes;
}

/// @brief Sets the grid.
/// @param grid to set.
void setGrid(Asset<VoxelGrid>& grid)
{
this->grid = grid;
}

/// @brief Inserts a new @ref BoxShiftPair to the list of the class.
/// @param box Box to insert.
/// @param shift Shift vector of the box.
void insertBox(const cubos::core::geom::Box& box, const glm::vec3& shift)
{
BoxShiftPair pair;
pair.box = box;
pair.shift = shift;
this->mBoxes.push_back(pair);
}

/// @brief Getter for the list of @ref BoxShiftPair of the class.
std::vector<BoxShiftPair> getBoxes() const
{
return this->mBoxes;
}

private:
/// @brief List of pairs composing the shape.
std::vector<BoxShiftPair> mBoxes; ///< List of boxes.
};
} // namespace cubos::engine
1 change: 1 addition & 0 deletions engine/samples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ make_sample(DIR "render/main" ASSETS)
make_sample(DIR "render/shadows" ASSETS)
make_sample(DIR "imgui")
make_sample(DIR "collisions" ASSETS)
make_sample(DIR "voxel-shape-collisions" ASSETS)
make_sample(DIR "scene" ASSETS)
make_sample(DIR "voxels" ASSETS)
make_sample(DIR "gizmos")
Expand Down
Binary file not shown.
3 changes: 3 additions & 0 deletions engine/samples/voxel-shape-collisions/assets/car.grd.meta
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"id": "059c16e7-a439-44c7-9bdc-6e069dba0c75"
}
Binary file not shown.
3 changes: 3 additions & 0 deletions engine/samples/voxel-shape-collisions/assets/main.pal.meta
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"id": "1aa5e234-28cb-4386-99b4-39386b0fc215"
}
169 changes: 169 additions & 0 deletions engine/samples/voxel-shape-collisions/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
#include <glm/gtc/random.hpp>

#include <cubos/core/geom/box.hpp>
#include <cubos/core/log.hpp>

#include <cubos/engine/assets/plugin.hpp>
#include <cubos/engine/collisions/collider.hpp>
#include <cubos/engine/collisions/colliding_with.hpp>
#include <cubos/engine/collisions/plugin.hpp>
#include <cubos/engine/collisions/shapes/box.hpp>
#include <cubos/engine/collisions/shapes/capsule.hpp>
#include <cubos/engine/collisions/shapes/voxel.hpp>
#include <cubos/engine/defaults/plugin.hpp>
#include <cubos/engine/fixed_step/plugin.hpp>
#include <cubos/engine/gizmos/plugin.hpp>
#include <cubos/engine/gizmos/target.hpp>
#include <cubos/engine/physics/plugin.hpp>
#include <cubos/engine/physics/solver/plugin.hpp>
#include <cubos/engine/render/camera/camera.hpp>
#include <cubos/engine/render/camera/draws_to.hpp>
#include <cubos/engine/render/camera/perspective.hpp>
#include <cubos/engine/render/defaults/plugin.hpp>
#include <cubos/engine/render/defaults/target.hpp>
#include <cubos/engine/render/picker/plugin.hpp>
#include <cubos/engine/render/tone_mapping/plugin.hpp>
#include <cubos/engine/render/voxels/grid.hpp>
#include <cubos/engine/render/voxels/palette.hpp>
#include <cubos/engine/settings/plugin.hpp>
#include <cubos/engine/settings/settings.hpp>
#include <cubos/engine/transform/plugin.hpp>
#include <cubos/engine/voxels/plugin.hpp>
#include <cubos/engine/window/plugin.hpp>

using cubos::core::geom::Box;
using cubos::core::io::Key;
using cubos::core::io::Modifiers;
using cubos::core::io::MouseButton;

using namespace cubos::engine;

static CUBOS_DEFINE_TAG(collisionsSampleUpdated);

struct State
{
CUBOS_ANONYMOUS_REFLECT(State);

bool collided = false;

Entity a;
Entity b;

glm::vec3 aRotationAxis;
glm::vec3 bRotationAxis;
};

/// [Get handles to assets]
static const Asset<VoxelGrid> CarAsset = AnyAsset("059c16e7-a439-44c7-9bdc-6e069dba0c75");
static const Asset<VoxelPalette> PaletteAsset = AnyAsset("1aa5e234-28cb-4386-99b4-39386b0fc215");
/// [Get handles to assets]

int main()
{
auto cubos = Cubos();

cubos.plugin(defaultsPlugin);
cubos.tag(gizmosDrawTag).after(toneMappingTag);

cubos.resource<State>();

cubos.startupSystem("setup camera").call([](Commands commands) {
auto targetEnt = commands.create().add(RenderTargetDefaults{}).add(GizmosTarget{}).entity();
commands.create()
.relatedTo(targetEnt, DrawsTo{})
.add(Camera{.zNear = 0.1F, .zFar = 100.0F})
.add(PerspectiveCamera{.fovY = 60.0F})
.add(LocalToWorld{})
.add(Position{{-35.0F, 1.5F, 0.0F}})
.add(Rotation::lookingAt({3.0F, -1.0F, 0.0F}, glm::vec3{0.0F, 1.0F, 0.0F}));
});

cubos.startupSystem("configure Assets").tagged(settingsTag).call([](Settings& settings) {
settings.setString("assets.io.path", SAMPLE_ASSETS_FOLDER);
});

/// [Set palette]
cubos.startupSystem("set palette").call([](RenderPalette& palette) { palette.asset = PaletteAsset; });
/// [Set palette]

cubos.startupSystem("create colliders").tagged(assetsTag).call([](State& state, Commands commands, Assets& assets) {
auto car = assets.read(CarAsset);
glm::vec3 offset = glm::vec3(car->size().x, car->size().y, car->size().z) / -2.0F;
state.a = commands.create()
.add(Collider{})
.add(RenderVoxelGrid{CarAsset, offset})
.add(VoxelCollisionShape(CarAsset))
.add(LocalToWorld{})
.add(Position{glm::vec3{0.0F, 0.0F, -30.0F}})
.add(Rotation{})
.add(PhysicsBundle{.mass = 500.0F, .velocity = {0.0F, 0.0F, 1.0F}})
.entity();
state.aRotationAxis = glm::sphericalRand(1.0F);

state.b = commands.create()
.add(Collider{})
.add(RenderVoxelGrid{CarAsset, offset})
.add(VoxelCollisionShape(CarAsset))
.add(LocalToWorld{})
.add(Position{glm::vec3{0.0F, 0.0F, 10.0F}})
.add(Rotation{})
.add(PhysicsBundle{.mass = 500.0F, .velocity = {0.0F, 0.0F, -1.0F}})
.entity();
state.bRotationAxis = glm::sphericalRand(1.0F);
});

cubos.system("move colliders")
.before(transformUpdateTag)
.call([](State& state, Query<Position&, Rotation&, Velocity&> query) {
auto [aPos, aRot, aVel] = *query.at(state.a);
auto [bPos, bRot, bVel] = *query.at(state.b);

aRot.quat = glm::rotate(aRot.quat, 0.001F, state.aRotationAxis);
aVel.vec += glm::vec3{0.0F, 0.0F, 0.01F};

bRot.quat = glm::rotate(bRot.quat, 0.001F, state.bRotationAxis);
bVel.vec -= glm::vec3{0.0F, 0.0F, 0.01F};
});

cubos.tag(collisionsSampleUpdated);

cubos.system("render voxel")
.after(collisionsSampleUpdated)
.call([](Gizmos& gizmos, Query<const LocalToWorld&, const Collider&, const VoxelCollisionShape&> query) {
for (auto [localToWorld, collider, shape] : query)
{
for (const auto box : shape.getBoxes())
{
// Get the current position from the localToWorld matrix
glm::mat4 pos = localToWorld.mat; // Store the matrix

// Create a translation matrix for the shift
glm::mat4 shiftMatrix = glm::translate(glm::mat4(1.0F), -box.shift);

// Combine the matrices (note: order matters)
pos = pos * shiftMatrix;
auto size = box.box.halfSize * 2.0F;
glm::mat4 transform = glm::scale(pos * collider.transform, size);
gizmos.drawWireBox("subboxes", transform);
}
}
});

cubos.system("render")
.after(collisionsSampleUpdated)
.call([](Gizmos& gizmos, Query<const LocalToWorld&, const Collider&> query) {
for (auto [localToWorld, collider] : query)
{
auto size = collider.localAABB.box().halfSize * 2.0F;
glm::mat4 transform = glm::scale(localToWorld.mat * collider.transform, size);
gizmos.color({1.0F, 1.0F, 1.0F});
gizmos.drawWireBox("local AABB", transform);

gizmos.color({1.0F, 0.0F, 0.0F});
gizmos.drawWireBox("world AABB", collider.worldAABB.min(), collider.worldAABB.max());
}
});

cubos.run();
return 0;
}
2 changes: 2 additions & 0 deletions engine/src/collisions/interface/plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
#include <cubos/engine/collisions/contact_manifold.hpp>
#include <cubos/engine/collisions/shapes/box.hpp>
#include <cubos/engine/collisions/shapes/capsule.hpp>
#include <cubos/engine/collisions/shapes/voxel.hpp>

void cubos::engine::interfaceCollisionsPlugin(Cubos& cubos)
{
cubos.component<Collider>();
cubos.component<BoxCollisionShape>();
cubos.component<CapsuleCollisionShape>();
cubos.component<VoxelCollisionShape>();

cubos.relation<CollidingWith>();
cubos.relation<ContactManifold>();
Expand Down
10 changes: 10 additions & 0 deletions engine/src/collisions/interface/shapes/voxel.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include <cubos/core/ecs/reflection.hpp>

#include <cubos/engine/collisions/shapes/voxel.hpp>

CUBOS_REFLECT_IMPL(cubos::engine::VoxelCollisionShape)
{
return core::ecs::TypeBuilder<VoxelCollisionShape>("cubos::engine::VoxelCollisionShape")
.withField("grid", &VoxelCollisionShape::grid)
.build();
}
Loading

0 comments on commit 9a654da

Please sign in to comment.