π³ Spatial Partitioning
Fase: 5 β TransiΓ§Γ£o Dimensional
Namespace: Caffeine::Spatial
Arquivo: src/spatial/Octree.hpp
Status: π
Planejado
RF: RF5.4
VisΓ£o Geral
Spatial partitioning divide o espaΓ§o 3D em regiΓ΅es para acelerar queries espaciais (broad-phase collision, frustum culling, raycasts). Migra do Quadtree 2D (Fase 4, se implementado) para Octree 3D.
API Planejada
namespace Caffeine::Spatial {
// ============================================================================
// @brief AABB 3D β caixa alinhada por eixos.
// ============================================================================
struct AABB3D {
Vec3 min = {0, 0, 0};
Vec3 max = {0, 0, 0};
Vec3 center() const { return (min + max) * 0.5f; }
Vec3 size() const { return max - min; }
bool contains(Vec3 p) const;
bool intersects(const AABB3D& o) const;
bool intersectsRay(Vec3 origin, Vec3 dir, f32* tMin = nullptr) const;
};
// ============================================================================
// @brief Frustum para culling.
//
// Derivado da view-projection matrix.
// Planos: Near, Far, Left, Right, Top, Bottom.
// ============================================================================
struct Frustum {
Vec4 planes[6]; // Ax + By + Cz + D = 0 (normalizados)
static Frustum fromMatrix(const Math::Mat4& viewProj);
bool contains(Vec3 point) const;
bool intersects(const AABB3D& aabb) const;
bool containsSphere(Vec3 center, f32 radius) const;
};
// ============================================================================
// @brief Octree para particionamento 3D.
//
// - Cada nΓ³ divide em 8 sub-nΓ³s quando excede maxEntities
// - Depth mΓ‘xima configurΓ‘vel (default: 8)
// - Entidades inseridas por AABB
// ============================================================================
class Octree {
public:
struct Config {
AABB3D rootBounds;
u32 maxEntitiesPerNode = 8;
u32 maxDepth = 8;
};
explicit Octree(const Config& cfg);
// ββ Insert / Remove ββββββββββββββββββββββββββββββββββββββββ
void insert(ECS::Entity entity, const AABB3D& bounds);
void remove(ECS::Entity entity);
void update(ECS::Entity entity, const AABB3D& newBounds);
void rebuild(); // rebuild completo (para quando muitas entidades moveram)
// ββ Queries ββββββββββββββββββββββββββββββββββββββββββββββββ
void queryAABB(const AABB3D& bounds,
std::vector<ECS::Entity>& out) const;
void queryFrustum(const Frustum& frustum,
std::vector<ECS::Entity>& out) const; // RF5.6
void queryRay(Vec3 origin, Vec3 dir, f32 maxDist,
std::vector<ECS::Entity>& out) const;
void queryRadius(Vec3 center, f32 radius,
std::vector<ECS::Entity>& out) const;
// ββ Stats ββββββββββββββββββββββββββββββββββββββββββββββββββ
u32 nodeCount() const;
u32 entityCount() const;
u32 depth() const;
private:
struct Node {
AABB3D bounds;
std::vector<ECS::Entity> entities;
std::unique_ptr<Node> children[8];
bool isLeaf = true;
u32 depth = 0;
void subdivide();
int childIndex(Vec3 point) const;
};
Node m_root;
Config m_config;
};
} // namespace Caffeine::Spatial
Frustum Culling (RF5.6)
Camera3D
βββ viewProjectionMatrix()
β
βΌ
Frustum::fromMatrix(viewProj)
β
βΌ
Octree::queryFrustum(frustum, visibleEntities)
β
βΌ
Apenas entidades DENTRO do frustum sΓ£o enviadas ao MeshSystem
(entidades fora nΓ£o geram draw calls)
BenefΓcio: Em uma cena com 10K objetos, apenas os ~100 visΓveis geram draw calls.
Quadtree 2D (Fase 4 β 5)
A Fase 4 pode usar um Quadtree 2D para broad-phase collision 2D (prΓ©-requisito do Octree):
// Fase 4 (opcional, para otimizar Physics2D com muitas entidades):
class Quadtree {
void insert(ECS::Entity, Rect2D bounds);
void query(Rect2D area, std::vector<ECS::Entity>& out);
};
// Fase 5: Octree substitui/estende para 3D
Exemplos de Uso
// ββ Setup βββββββββββββββββββββββββββββββββββββββββββββββββββββ
Caffeine::Spatial::Octree octree({
.rootBounds = { {-1000,-1000,-1000}, {1000,1000,1000} },
.maxEntitiesPerNode = 8,
.maxDepth = 8
});
// ββ Popular com entidades βββββββββββββββββββββββββββββββββββββ
world.query(meshQuery, [&](ECS::Entity e, Position3D& pos, MeshRenderer& mr) {
AABB3D bounds = computeBounds(mr.mesh, pos);
octree.insert(e, bounds);
});
// ββ Frustum culling βββββββββββββββββββββββββββββββββββββββββββ
Frustum frustum = Frustum::fromMatrix(camera3D.viewProjectionMatrix());
std::vector<ECS::Entity> visible;
octree.queryFrustum(frustum, visible);
// Apenas renderizar entidades visΓveis:
for (ECS::Entity e : visible) {
auto* mr = world.get<MeshRenderer>(e);
meshSystem.submitMesh(*mr->mesh, getWorldMatrix(e), cmd);
}
// ββ Update quando entidades movem ββββββββββββββββββββββββββββ
if (entity.get<Position3D>().changed) {
octree.update(entity, newBounds);
}
CritΓ©rio de AceitaΓ§Γ£o
DependΓͺncias
ReferΓͺncias
π³ Spatial Partitioning
VisΓ£o Geral
Spatial partitioning divide o espaΓ§o 3D em regiΓ΅es para acelerar queries espaciais (broad-phase collision, frustum culling, raycasts). Migra do Quadtree 2D (Fase 4, se implementado) para Octree 3D.
API Planejada
Frustum Culling (RF5.6)
BenefΓcio: Em uma cena com 10K objetos, apenas os ~100 visΓveis geram draw calls.
Quadtree 2D (Fase 4 β 5)
A Fase 4 pode usar um Quadtree 2D para broad-phase collision 2D (prΓ©-requisito do Octree):
Exemplos de Uso
CritΓ©rio de AceitaΓ§Γ£o
queryFrustumexclui corretamente entidades fora do frustuminsert/removecorretos sem corrupΓ§Γ£oDependΓͺncias
ReferΓͺncias
docs/architecture_specs.mdβ RF5.4 Octree spatialdocs/fase5/camera3d.mdβ fornece Frustum para queryFrustum