#pragma once #include "utils/Entity.h" #include "utils/EntityInstance.h" #include "utils/SingleInstanceComponentManager.h" #include "filament/TransformManager.h" #include "gltfio/FilamentAsset.h" #include "gltfio/FilamentInstance.h" #include "Log.hpp" namespace thermion_filament { typedef void(*CollisionCallback)(int32_t entityId1, int32_t entityId2) ; class CollisionComponentManager : public utils::SingleInstanceComponentManager { const filament::TransformManager& _transformManager; public: CollisionComponentManager(const filament::TransformManager& transformManager) : _transformManager(transformManager) {} std::vector collides(utils::Entity transformingEntity, filament::Aabb sourceBox) { auto sourceCorners = sourceBox.getCorners(); std::vector collisionAxes; for(auto it = begin(); it < end(); it++) { auto entity = getEntity(it); if(entity == transformingEntity) { continue; } auto targetXformInstance = _transformManager.getInstance(entity); auto targetXform = _transformManager.getWorldTransform(targetXformInstance); auto targetBox = elementAt<0>(it).transform(targetXform); auto targetCorners = targetBox.getCorners(); // Log("Checking collision for entity %d with aabb extent %f %f %f against source entity %d with aabb extent %f %f %f", entity, targetBox.extent().x, targetBox.extent().y, targetBox.extent().z, transformingEntityId, sourceBox.extent().x, sourceBox.extent().y, sourceBox.extent().z); bool collided = false; // iterate over every vertex in the source/target AABB for(int i = 0; i < 8; i++) { auto intersecting = sourceCorners.vertices[i]; auto min = targetBox.min; auto max = targetBox.max; // if the vertex has insersected with the target/source AABB if(targetBox.contains(sourceCorners.vertices[i]) <= 0) { collided = true; // Log("targetBox %f %f %f contains source vertex %f %f %f", targetBox.extent().x, targetBox.extent().y, targetBox.extent().z, sourceCorners.vertices[i].x, sourceCorners.vertices[i].y, sourceCorners.vertices[i].z); } else if(sourceBox.contains(targetCorners.vertices[i]) <= 0) { // Log("sourceBox %f %f %f contains target vertex %f %f %f", sourceBox.extent().x, sourceBox.extent().y, sourceBox.extent().z, targetCorners.vertices[i].x, targetCorners.vertices[i].y, targetCorners.vertices[i].z); collided = true; intersecting = targetCorners.vertices[i]; min = sourceBox.min; max = sourceBox.max; } else { continue; } auto affectsTransform = elementAt<2>(it); if(affectsTransform) { float xmin = min.x - intersecting.x; float ymin = min.y - intersecting.y; float zmin = min.z - intersecting.z; float xmax = intersecting.x - max.x; float ymax = intersecting.y - max.y; float zmax = intersecting.z - max.z; auto maxD = std::max(xmin,std::max(ymin,std::max(zmin,std::max(xmax,std::max(ymax,zmax))))); filament::math::float3 axis; if(maxD == xmin) { axis = {-1.0f,0.0f, 0.0f}; } else if(maxD == ymin) { axis = {0.0f,-1.0f, 0.0f}; } else if(maxD == zmin) { axis = {0.0f,0.0f, -1.0f}; } else if(maxD == xmax) { axis = {1.0f,0.0f, 0.0f}; } else if(maxD == ymax) { axis = {0.0f,1.0f, 0.0f}; } else { axis = { 0.0f, 0.0f, 1.0f}; } collisionAxes.push_back(axis); } break; } if(collided) { auto callback = elementAt<1>(it); if(callback) { callback(utils::Entity::smuggle(entity), utils::Entity::smuggle(transformingEntity)); } } } return collisionAxes; } }; }