#include "behavior.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include EXT_BEHAVIOR_REGISTER_CPP(SceneCollisionBehavior) #define this ((uf::Scene*) &self) void ext::SceneCollisionBehavior::initialize( uf::Object& self ) { { auto& mutexPointer = this->getComponent(); mutexPointer = new std::mutex; } } void ext::SceneCollisionBehavior::tick( uf::Object& self ) { auto& metadata = this->getComponent(); if ( !metadata["system"]["physics"]["collision"].asBool() ) return; auto& mutex = *(this->getComponent()); mutex.lock(); bool threaded = !metadata["system"]["physics"]["single threaded"].asBool(); bool sort = metadata["system"]["physics"]["sort"].asBool(); bool useStrongest = metadata["system"]["physics"]["use"]["strongest"].asBool(); bool queued = metadata["system"]["physics"]["use"]["queue"].asBool(); bool updatePhysics = !metadata["system"]["physics"]["optimizations"]["entity-local update"].asBool(); bool ignoreStaticEntities = metadata["system"]["physics"]["optimizations"]["ignore static entities"].asBool(); bool ignoreDuplicateTests = metadata["system"]["physics"]["optimizations"]["ignore duplicate tests"].asBool(); bool useWorkers = metadata["system"]["physics"]["use"]["worker"].asBool(); pod::Thread& thread = uf::thread::has("Physics") ? uf::thread::get("Physics") : uf::thread::create( "Physics", true, false ); auto function = [&]() -> int { std::vector entities; // update physics if ( updatePhysics ) { std::function filter = [&]( uf::Entity* entity ) { if ( !entity->hasComponent() ) return; auto& metadata = entity->getComponent(); auto& transform = entity->getComponent>(); auto& physics = entity->getComponent(); if ( metadata["system"]["physics"]["gravity"] != Json::nullValue ) { physics.linear.acceleration.x = metadata["system"]["physics"]["gravity"][0].asFloat(); physics.linear.acceleration.y = metadata["system"]["physics"]["gravity"][1].asFloat(); physics.linear.acceleration.z = metadata["system"]["physics"]["gravity"][2].asFloat(); } if ( !metadata["system"]["physics"]["collision"].asBool() ) { physics.linear.acceleration.x = 0; physics.linear.acceleration.y = 0; physics.linear.acceleration.z = 0; } transform = uf::physics::update( transform, physics ); }; this->process(filter); } { std::function filter = [&]( uf::Entity* entity ) { auto& metadata = entity->getComponent(); if ( !metadata["system"]["physics"]["collision"].isNull() && !metadata["system"]["physics"]["collision"].asBool() ) return; if ( entity->hasComponent() ) entities.push_back((uf::Object*) entity); }; this->process(filter); } auto onCollision = []( pod::Collider::Manifold& manifold, uf::Object* a, uf::Object* b, bool queued ){ uf::Serializer payload; payload["normal"][0] = manifold.normal.x; payload["normal"][1] = manifold.normal.y; payload["normal"][2] = manifold.normal.z; payload["entity"] = b->getUid(); payload["depth"] = -manifold.depth; if ( queued ) a->queueHook("world:Collision.%UID%", payload); else a->callHook("world:Collision.%UID%", payload); payload["entity"] = a->getUid(); payload["depth"] = manifold.depth; if ( queued ) b->queueHook("world:Collision.%UID%", payload); else b->callHook("world:Collision.%UID%", payload); }; auto testColliders = [&]( uf::Collider& colliderA, uf::Collider& colliderB, uf::Object* a, uf::Object* b, bool useStrongest ){ pod::Collider::Manifold strongest; auto manifolds = colliderA.intersects(colliderB); for ( auto manifold : manifolds ) { if ( manifold.colliding && manifold.depth > 0 ) { if ( !useStrongest ) onCollision(manifold, a, b, queued); else if ( strongest.depth < manifold.depth ) strongest = manifold; } } if ( useStrongest && strongest.colliding ) onCollision(strongest, a, b, queued); }; // collide with others if ( ignoreDuplicateTests ) { struct Pair { uf::Object* a = NULL; uf::Object* b = NULL; }; std::unordered_map queued; for ( auto* _a : entities ) { uf::Object& entityA = *_a; if ( ignoreStaticEntities && !entityA.hasComponent() ) continue; for ( auto* _b : entities ) { if ( _a == _b ) continue; uf::Object& entityB = *_b; std::string hash = std::to_string(std::min( entityA.getUid(), entityB.getUid() )) + ":" + std::to_string(std::max( entityA.getUid(), entityB.getUid() )); if ( queued.count(hash) > 0 ) continue; queued[hash] = { .a = _a, .b = _b, }; } } for ( auto& pair : queued ) { auto* entityA = pair.second.a; auto* entityB = pair.second.b; auto& colliderA = entityA->getComponent(); auto& colliderB = entityB->getComponent(); auto function = [&]() -> int { testColliders( colliderA, colliderB, entityA, entityB, useStrongest ); return 0; }; if ( threaded && useWorkers ) uf::thread::add( uf::thread::fetchWorker(), function, true ); else function(); } } else { for ( auto* _a : entities ) { uf::Object& entityA = *_a; if ( ignoreStaticEntities && !entityA.hasComponent() ) continue; for ( auto* _b : entities ) { if ( _a == _b ) continue; uf::Object& entityB = *_b; auto& colliderA = entityA.getComponent(); auto& colliderB = entityB.getComponent(); auto function = [&]() -> int { testColliders( colliderA, colliderB, &entityA, &entityB, useStrongest ); return 0; }; if ( threaded && useWorkers ) uf::thread::add( uf::thread::fetchWorker(), function, true ); else function(); } } } mutex.unlock(); return 0; }; if ( threaded ) uf::thread::add( thread, function, true ); else function(); } void ext::SceneCollisionBehavior::render( uf::Object& self ){} void ext::SceneCollisionBehavior::destroy( uf::Object& self ){ { auto& mutexPointer = this->getComponent(); delete mutexPointer; mutexPointer = NULL; } } #undef this