#pragma once #include "aabbox.h" #include "util/refcount.h" #include #include enum PhysItemType { PIT_Object = 0x01, PIT_Node = 0x02, //A group is both the type of PIT_Group, and the type of objects in the group PIT_Group = 0x80, }; class Object; struct PhysicsGrid; struct PhysicsItem; struct PhysicsGroup; class SaveFile; namespace scene { class Node; }; namespace net { struct Message; }; struct PhysicsItem { AABBoxd bound; union { Object* object; scene::Node* node; PhysicsGroup* group; }; unsigned maskID; PhysItemType type; PhysicsGrid* gridLocation; }; struct PhysicsGroup { unsigned itemCount; PhysicsItem* items; }; struct PhysBisect { std::vector items; PhysBisect *a, *b, *parent; unsigned childrenA, childrenB; bool xSplit; double dimSplit, fuzz; unsigned depth; PhysBisect() : a(0), b(0), parent(0), depth(0), childrenA(0), childrenB(0), xSplit(true) {} inline int classify(const AABBoxd& box) const { if(xSplit) { if(box.maximum.x < dimSplit - fuzz) return -1; if(box.minimum.x > dimSplit + fuzz) return 1; return 0; } else { if(box.maximum.z < dimSplit - fuzz) return -1; if(box.minimum.z > dimSplit + fuzz) return 1; return 0; } } int classifyContainer(const AABBoxd& box) const { if(xSplit) { double center = (box.minimum.x + box.maximum.x) * 0.5; if(center >= dimSplit && box.minimum.x > dimSplit - fuzz) return 1; else if(center < dimSplit && box.maximum.x < dimSplit + fuzz) return -1; return 0; } else { double center = (box.minimum.z + box.maximum.z) * 0.5; if(center >= dimSplit && box.minimum.z > dimSplit - fuzz) return 1; else if(center < dimSplit && box.maximum.z < dimSplit + fuzz) return -1; return 0; } } void findInBox(const AABBoxd& box, const std::function& callback, unsigned ownerMask) const; void split(); void fuse(); void fuse(std::vector& into); }; struct PhysicsGrid { PhysBisect groups[32]; mutable threads::ReadWriteMutex mutex; AABBoxd bound, fuzzBound; unsigned itemCount; void removeItem(PhysicsItem* item); void addItem(PhysicsItem* item); void updateItem(PhysicsItem* item, const AABBoxd& newBound); bool empty() const; }; class PhysicsWorld : public AtomicRefCounted { //Physical space that items exist withing PhysicsGrid* grid; //Catch-all for items that do not exist within the grid, or can't fit inside the grid PhysicsGrid outside; //Access mutex threads::ReadWriteMutex mutex; //Size of a grid square double gridSize, halfGridSize; //Grid fuzz margin double gridFuzz; //Number of grid squares on a side unsigned gridCount; unsigned getBox(double x) const; unsigned getLowerBound(double x) const; unsigned getUpperBound(double x) const; void findInBoxInZone(const PhysicsGrid& zone, const AABBoxd& box, const std::function& callback, unsigned ownerMask); public: PhysicsWorld(double GridSize = 3000.0, double GridFuzz = 350.0, unsigned GridCount = 60); ~PhysicsWorld(); //Calls for each of the PhysicsItems within the box //Optionally filters by owner of the item (Mask 1 permits unowned objects) void findInBox(const AABBoxd& box, const std::function& callback, unsigned ownerMask = 0xffffffff); void updateItem(PhysicsItem& item, const AABBoxd& newBox); PhysicsItem* registerItem(const AABBoxd& bounds, Object* obj); PhysicsItem* registerItem(const AABBoxd& bounds, scene::Node* node); void registerItem(PhysicsItem& item); void unregisterItem(PhysicsItem& item); void writeSetup(net::Message& msg); void writeSetup(SaveFile& file); static PhysicsWorld* fromMessage(net::Message& msg); static PhysicsWorld* fromSave(SaveFile& file); };