Пример #1
0
void Octant::InsertDrawable(Drawable* drawable)
{
    const BoundingBox& box = drawable->GetWorldBoundingBox();

    // If root octant, insert all non-occludees here, so that octant occlusion does not hide the drawable.
    // Also if drawable is outside the root octant bounds, insert to root
    bool insertHere;
    if (this == root_)
        insertHere = !drawable->IsOccludee() || cullingBox_.IsInside(box) != INSIDE || CheckDrawableFit(box);
    else
        insertHere = CheckDrawableFit(box);

    if (insertHere)
    {
        Octant* oldOctant = drawable->octant_;
        if (oldOctant != this)
        {
            // Add first, then remove, because drawable count going to zero deletes the octree branch in question
            AddDrawable(drawable);
            if (oldOctant)
                oldOctant->RemoveDrawable(drawable, false);
        }
    }
    else
    {
        Vector3 boxCenter = box.Center();
        unsigned x = boxCenter.x_ < center_.x_ ? 0 : 1;
        unsigned y = boxCenter.y_ < center_.y_ ? 0 : 2;
        unsigned z = boxCenter.z_ < center_.z_ ? 0 : 4;

        GetOrCreateChild(x + y + z)->InsertDrawable(drawable);
    }
}
Пример #2
0
Octant* Octree::CreateChildOctant(Octant* octant, size_t index)
{
    if (octant->children[index])
        return octant->children[index];

    Vector3 newMin = octant->worldBoundingBox.min;
    Vector3 newMax = octant->worldBoundingBox.max;
    const Vector3& oldCenter = octant->center;

    if (index & 1)
        newMin.x = oldCenter.x;
    else
        newMax.x = oldCenter.x;

    if (index & 2)
        newMin.y = oldCenter.y;
    else
        newMax.y = oldCenter.y;

    if (index & 4)
        newMin.z = oldCenter.z;
    else
        newMax.z = oldCenter.z;

    Octant* child = allocator.Allocate();
    child->Initialize(octant, BoundingBox(newMin, newMax), octant->level - 1);
    octant->children[index] = child;

    return child;
}
Пример #3
0
    void Octant::Insert(SceneNode* obj)
    {
        const BoundingBox& box = obj->GetWorldBoundingBox();

        // If drawable is outside the root octant bounds, insert to root
        bool insertHere;
        if (this == root_)
            insertHere = cullingBox_.IsInside(box) != Intersection::INSIDE || CheckFit(box);
        else
            insertHere = CheckFit(box);

        if (insertHere)
        {
            Octant* oldOctant = obj->GetOctant();
            if (oldOctant != this)
            {
                // Add first, then remove, because drawable count going to zero deletes the octree branch in question
                Add(obj);
                if (oldOctant)
                    oldOctant->Remove(obj, false);
            }
        }
        else
        {
            Vector3 boxCenter = box.Center();
            unsigned x = boxCenter.x < center_.x ? 0 : 1;
            unsigned y = boxCenter.y < center_.y ? 0 : 2;
            unsigned z = boxCenter.z < center_.z ? 0 : 4;

            GetOrCreateChild(x + y + z)->Insert(obj);
        }
    }
Пример #4
0
void Octree::RemoveManualDrawable(Drawable* drawable)
{
    if (!drawable)
        return;

    Octant* octant = drawable->GetOctant();
    if (octant && octant->GetRoot() == this)
        octant->RemoveDrawable(drawable);
}
Пример #5
0
    void Octree::Remove(SceneNode* obj)
    {
        Octant* octant = obj->GetOctant();
        if (octant)
        {
            octant->Remove(obj);
			allDrawablesSet_.erase(obj);
			allDrawables_.erase(std::remove(allDrawables_.begin(), allDrawables_.end(), obj), allDrawables_.end());
        }
    }
Пример #6
0
    void Octant::DecDrawableCount()
    {
        Octant* parent = parent_;

        --numDrawables_;
        if (!numDrawables_)
        {
            if (parent)
                parent->DeleteChild(index_);
        }

        if (parent)
            parent->DecDrawableCount();
    }
Пример #7
0
void Octree::RemoveNode(OctreeNode* node, Octant* octant)
{
    // Do not set the node's octant pointer to zero, as the node may already be added into another octant
    octant->nodes.Remove(node);
    
    // Decrement the node count in the whole parent branch and erase empty octants as necessary
    while (octant)
    {
        --octant->numNodes;
        Octant* next = octant->parent;
        if (!octant->numNodes && next)
            DeleteChildOctant(next, next->ChildIndex(octant->center));
        octant = next;
    }
}
Пример #8
0
    void Octree::InsertUpdate(SceneNode* obj)
    {
        Octant* octant = obj->GetOctant();

        const BoundingBox& box = obj->GetWorldBoundingBox();
        // Skip if still fits the current octant
        if (octant && octant->GetCullingBox().IsInside(box) == Intersection::INSIDE && octant->CheckFit(box))
            return;

        Insert(obj);

        // Verify that the obj will be culled correctly
        CHECK_ASSERT(obj->GetOctant() == this || obj->GetOctant()->GetCullingBox().IsInside(box) == Intersection::INSIDE);

		if (allDrawablesSet_.end() == allDrawablesSet_.find(obj))
		{
			allDrawablesSet_.insert(obj);
			allDrawables_.push_back(obj);
		}
    }
Пример #9
0
void Octree::Update()
{
    PROFILE(UpdateOctree);

    for (auto it = updateQueue.Begin(); it != updateQueue.End(); ++it)
    {
        OctreeNode* node = *it;
        // If node was removed before update could happen, a null pointer will be in its place
        if (node)
        {
            node->SetFlag(NF_OCTREE_UPDATE_QUEUED, false);

            // Do nothing if still fits the current octant
            const BoundingBox& box = node->WorldBoundingBox();
            Vector3 boxSize = box.Size();
            Octant* oldOctant = node->octant;
            if (oldOctant && oldOctant->cullingBox.IsInside(box) == INSIDE && oldOctant->FitBoundingBox(box, boxSize))
                continue;

            // Begin reinsert process. Start from root and check what level child needs to be used
            Octant* newOctant = &root;
            Vector3 boxCenter = box.Center();

            for (;;)
            {
                bool insertHere;
                // If node does not fit fully inside root octant, must remain in it
                if (newOctant == &root)
                    insertHere = newOctant->cullingBox.IsInside(box) != INSIDE || newOctant->FitBoundingBox(box, boxSize);
                else
                    insertHere = newOctant->FitBoundingBox(box, boxSize);

                if (insertHere)
                {
                    if (newOctant != oldOctant)
                    {
                        // Add first, then remove, because node count going to zero deletes the octree branch in question
                        AddNode(node, newOctant);
                        if (oldOctant)
                            RemoveNode(node, oldOctant);
                    }
                    break;
                }
                else
                    newOctant = CreateChildOctant(newOctant, newOctant->ChildIndex(boxCenter));
            }
        }
    }

    updateQueue.Clear();
}
Пример #10
0
void Octree::Update(const FrameInfo& frame)
{
    // Let drawables update themselves before reinsertion. This can be used for animation
    if (!drawableUpdates_.Empty())
    {
        PROFILE(UpdateDrawables);

        // Perform updates in worker threads. Notify the scene that a threaded update is going on and components
        // (for example physics objects) should not perform non-threadsafe work when marked dirty
        Scene* scene = GetScene();
        WorkQueue* queue = GetSubsystem<WorkQueue>();
        scene->BeginThreadedUpdate();

        int numWorkItems = queue->GetNumThreads() + 1; // Worker threads + main thread
        int drawablesPerItem = Max((int)(drawableUpdates_.Size() / numWorkItems), 1);

        PODVector<Drawable*>::Iterator start = drawableUpdates_.Begin();
        // Create a work item for each thread
        for (int i = 0; i < numWorkItems; ++i)
        {
            SharedPtr<WorkItem> item = queue->GetFreeItem();
            item->priority_ = M_MAX_UNSIGNED;
            item->workFunction_ = UpdateDrawablesWork;
            item->aux_ = const_cast<FrameInfo*>(&frame);

            PODVector<Drawable*>::Iterator end = drawableUpdates_.End();
            if (i < numWorkItems - 1 && end - start > drawablesPerItem)
                end = start + drawablesPerItem;

            item->start_ = &(*start);
            item->end_ = &(*end);
            queue->AddWorkItem(item);

            start = end;
        }

        queue->Complete(M_MAX_UNSIGNED);
        scene->EndThreadedUpdate();
    }

    // Notify drawable update being finished. Custom animation (eg. IK) can be done at this point
    Scene* scene = GetScene();
    if (scene)
    {
        using namespace SceneDrawableUpdateFinished;

        VariantMap& eventData = GetEventDataMap();
        eventData[P_SCENE] = scene;
        eventData[P_TIMESTEP] = frame.timeStep_;
        scene->SendEvent(E_SCENEDRAWABLEUPDATEFINISHED, eventData);
    }

    // Reinsert drawables that have been moved or resized, or that have been newly added to the octree and do not sit inside
    // the proper octant yet
    if (!drawableUpdates_.Empty())
    {
        PROFILE(ReinsertToOctree);

        for (PODVector<Drawable*>::Iterator i = drawableUpdates_.Begin(); i != drawableUpdates_.End(); ++i)
        {
            Drawable* drawable = *i;
            drawable->updateQueued_ = false;
            Octant* octant = drawable->GetOctant();
            const BoundingBox& box = drawable->GetWorldBoundingBox();

            // Skip if no octant or does not belong to this octree anymore
            if (!octant || octant->GetRoot() != this)
                continue;
            // Skip if still fits the current octant
            if (drawable->IsOccludee() && octant->GetCullingBox().IsInside(box) == INSIDE && octant->CheckDrawableFit(box))
                continue;

            InsertDrawable(drawable);

#ifdef _DEBUG
            // Verify that the drawable will be culled correctly
            octant = drawable->GetOctant();
            if (octant != this && octant->GetCullingBox().IsInside(box) != INSIDE)
            {
                LOGERROR("Drawable is not fully inside its octant's culling bounds: drawable box " + box.ToString() +
                         " octant box " + octant->GetCullingBox().ToString());
            }
#endif
        }
    }

    drawableUpdates_.Clear();
}