|
|
@@ -3,7 +3,6 @@
|
|
|
// Code licensed under the BSD License.
|
|
|
// http://www.anki3d.org/LICENSE
|
|
|
|
|
|
-#if 0
|
|
|
#include "anki/scene/Sector.h"
|
|
|
#include "anki/scene/SpatialComponent.h"
|
|
|
#include "anki/scene/SceneNode.h"
|
|
|
@@ -22,410 +21,210 @@ namespace anki {
|
|
|
//==============================================================================
|
|
|
|
|
|
//==============================================================================
|
|
|
-Portal::Portal()
|
|
|
+Portal::~Portal()
|
|
|
{
|
|
|
- sectors[0] = sectors[1] = nullptr;
|
|
|
- open = true;
|
|
|
+ auto alloc = m_group->getAllocator();
|
|
|
+ m_shapeStorage.destroy(alloc);
|
|
|
+
|
|
|
+ if(m_shape)
|
|
|
+ {
|
|
|
+ alloc.deleteInstance(m_shape);
|
|
|
+ m_shape = nullptr;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
//==============================================================================
|
|
|
-// Sector =
|
|
|
-//==============================================================================
|
|
|
+Error Portal::create(const SArray<Vec4>& vertPositions)
|
|
|
+{
|
|
|
+ m_shape = m_group->createConvexHull(vertPositions, m_shapeStorage);
|
|
|
+ return m_shape ? ErrorCode::NONE : ErrorCode::OUT_OF_MEMORY;
|
|
|
+}
|
|
|
|
|
|
//==============================================================================
|
|
|
-Sector::Sector(SectorGroup* group_, const Aabb& box)
|
|
|
- : group(group_),
|
|
|
- portals(group->getSceneGraph().getAllocator())
|
|
|
+Error Portal::addSector(Sector* sector)
|
|
|
{
|
|
|
- // Reserve some space for portals
|
|
|
- portals.reserve(AVERAGE_PORTALS_PER_SECTOR);
|
|
|
+ ANKI_ASSERT(sector);
|
|
|
+ return m_sectors.pushBack(m_group->getAllocator(), sector);
|
|
|
}
|
|
|
|
|
|
//==============================================================================
|
|
|
-Bool Sector::placeSceneNode(SceneNode* sn)
|
|
|
+// Sector =
|
|
|
+//==============================================================================
|
|
|
+
|
|
|
+//==============================================================================
|
|
|
+Sector::~Sector()
|
|
|
{
|
|
|
-#if 0
|
|
|
- // XXX Optimize
|
|
|
+ auto alloc = m_group->getAllocator();
|
|
|
|
|
|
- SpatialComponent* sp = sn->getSpatialComponent();
|
|
|
- ANKI_ASSERT(sp);
|
|
|
- if(!sp->getAabb().collide(octree.getRoot().getAabb()))
|
|
|
+ if(m_shape)
|
|
|
{
|
|
|
- return false;
|
|
|
+ alloc.deleteInstance(m_shape);
|
|
|
+ m_shape = nullptr;
|
|
|
}
|
|
|
|
|
|
- octree.placeSceneNode(sn);
|
|
|
-#endif
|
|
|
- return true;
|
|
|
+ m_portals.destroy(alloc);
|
|
|
+ m_nodes.destroy(alloc);
|
|
|
}
|
|
|
|
|
|
//==============================================================================
|
|
|
-void Sector::addNewPortal(Portal* portal)
|
|
|
+Error Sector::create(const SArray<Vec4>& vertPositions)
|
|
|
{
|
|
|
- ANKI_ASSERT(portal);
|
|
|
- ANKI_ASSERT(
|
|
|
- std::find(portals.begin(), portals.end(), portal) == portals.end()
|
|
|
- && "Portal found in the container");
|
|
|
-
|
|
|
- portals.push_back(portal);
|
|
|
+ m_shape = m_group->createConvexHull(vertPositions, m_shapeStorage);
|
|
|
+ return m_shape ? ErrorCode::NONE : ErrorCode::OUT_OF_MEMORY;
|
|
|
}
|
|
|
|
|
|
//==============================================================================
|
|
|
-void Sector::removePortal(Portal* portal)
|
|
|
+Error Sector::addPortal(Portal* portal)
|
|
|
{
|
|
|
ANKI_ASSERT(portal);
|
|
|
- SceneVector<Portal*>::iterator it =
|
|
|
- std::find(portals.begin(), portals.end(), portal);
|
|
|
-
|
|
|
- ANKI_ASSERT(it != portals.end());
|
|
|
- portals.erase(it);
|
|
|
+ return m_portals.pushBack(m_group->getAllocator(), portal);
|
|
|
}
|
|
|
|
|
|
//==============================================================================
|
|
|
-// SectorGroup =
|
|
|
-//==============================================================================
|
|
|
+Error Sector::addSceneNode(SceneNode* node)
|
|
|
+{
|
|
|
+ ANKI_ASSERT(node);
|
|
|
+ ANKI_ASSERT(findSceneNode(node) == m_nodes.getEnd());
|
|
|
+ return m_nodes.pushBack(m_group->getAllocator(), node);
|
|
|
+}
|
|
|
|
|
|
//==============================================================================
|
|
|
-SectorGroup::SectorGroup(SceneGraph* scene_)
|
|
|
- : scene(scene_),
|
|
|
- sectors(scene->getAllocator()),
|
|
|
- portals(scene->getAllocator())
|
|
|
+void Sector::removeSceneNode(SceneNode* node)
|
|
|
{
|
|
|
- ANKI_ASSERT(scene != nullptr);
|
|
|
+ ANKI_ASSERT(node);
|
|
|
+ auto it = findSceneNode(node);
|
|
|
+ ANKI_ASSERT(it != m_nodes.getEnd());
|
|
|
+ m_nodes.erase(m_group->getAllocator(), it);
|
|
|
}
|
|
|
|
|
|
//==============================================================================
|
|
|
-SectorGroup::~SectorGroup()
|
|
|
+List<SceneNode*>::Iterator Sector::findSceneNode(SceneNode* node)
|
|
|
{
|
|
|
-#if 0
|
|
|
- for(Sector* sector : sectors)
|
|
|
+ ANKI_ASSERT(node);
|
|
|
+ auto it = m_nodes.getBegin();
|
|
|
+ for(; it != m_nodes.getEnd(); ++it)
|
|
|
{
|
|
|
- ANKI_DELETE(sector, scene->getAllocator());
|
|
|
+ if(*it == node)
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- for(Portal* portal : portals)
|
|
|
- {
|
|
|
- ANKI_DELETE(portal, scene->getAllocator());
|
|
|
- }
|
|
|
-#endif
|
|
|
+ return it;
|
|
|
}
|
|
|
|
|
|
//==============================================================================
|
|
|
-Sector* SectorGroup::createNewSector(const Aabb& aabb)
|
|
|
-{
|
|
|
-#if 0
|
|
|
- Sector* out = ANKI_NEW(Sector, scene->getAllocator(), this, aabb);
|
|
|
- sectors.push_back(out);
|
|
|
- return out;
|
|
|
-#endif
|
|
|
- return nullptr;
|
|
|
-}
|
|
|
-
|
|
|
+// SectorGroup =
|
|
|
//==============================================================================
|
|
|
-Portal* SectorGroup::createNewPortal(Sector* a, Sector* b,
|
|
|
- const Obb& collisionShape)
|
|
|
-{
|
|
|
-#if 0
|
|
|
- ANKI_ASSERT(a && b);
|
|
|
- Portal* out = ANKI_NEW_0(Portal, scene->getAllocator());
|
|
|
-
|
|
|
- out->sectors[0] = a;
|
|
|
- out->sectors[1] = b;
|
|
|
- out->shape = collisionShape;
|
|
|
-
|
|
|
- portals.push_back(out);
|
|
|
-
|
|
|
- a->addNewPortal(out);
|
|
|
- b->addNewPortal(out);
|
|
|
-
|
|
|
- return out;
|
|
|
-#endif
|
|
|
- return nullptr;
|
|
|
-}
|
|
|
|
|
|
//==============================================================================
|
|
|
-void SectorGroup::placeSceneNode(SceneNode* sn)
|
|
|
+ConvexHullShape* SectorGroup::createConvexHull(
|
|
|
+ const SArray<Vec4>& vertPositions, DArray<Vec4>& shapeStorage)
|
|
|
{
|
|
|
-#if 0
|
|
|
- ANKI_ASSERT(sn != nullptr);
|
|
|
- SpatialComponent* sp = sn->getSpatial();
|
|
|
- ANKI_ASSERT(sp);
|
|
|
- const Vec3& spPos = sp->getSpatialOrigin();
|
|
|
-
|
|
|
- // Find the candidates first. Sectors overlap, chose the smaller(??!!??)
|
|
|
- Sector* sector = nullptr;
|
|
|
- for(Sector* s : sectors)
|
|
|
- {
|
|
|
- // Find if the spatia's position is inside the sector
|
|
|
- Bool inside = true;
|
|
|
- for(U i = 0; i < 3; i++)
|
|
|
- {
|
|
|
- if(spPos[i] > s->getAabb().getMax()[i]
|
|
|
- || spPos[i] < s->getAabb().getMin()[i])
|
|
|
- {
|
|
|
- inside = false;
|
|
|
- continue;
|
|
|
- }
|
|
|
- }
|
|
|
+ Error err = ErrorCode::NONE;
|
|
|
|
|
|
- if(!inside)
|
|
|
- {
|
|
|
- // continue
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- // No other candidate?
|
|
|
- if(sector == nullptr)
|
|
|
- {
|
|
|
- sector = s;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- // Other candidata so chose the smaller
|
|
|
- F32 lengthSqA = (sector->getAabb().getMax()
|
|
|
- - sector->getAabb().getMin()).getLengthSquared();
|
|
|
-
|
|
|
- F32 lengthSqB = (s->getAabb().getMax()
|
|
|
- - s->getAabb().getMin()).getLengthSquared();
|
|
|
+ auto alloc = getAllocator();
|
|
|
+ U vertCount = vertPositions.getSize();
|
|
|
+ ANKI_ASSERT(vertCount >= 4 && "Minimum shape should be tetrahedron");
|
|
|
|
|
|
- if(lengthSqB < lengthSqA)
|
|
|
- {
|
|
|
- sector = s;
|
|
|
- }
|
|
|
- }
|
|
|
- } // end if inside
|
|
|
+ // Create hull
|
|
|
+ ConvexHullShape* hull = alloc.newInstance<ConvexHullShape>();
|
|
|
+ if(!hull)
|
|
|
+ {
|
|
|
+ err = ErrorCode::OUT_OF_MEMORY;
|
|
|
}
|
|
|
|
|
|
- // Ask the octree to place it
|
|
|
- if(sector != nullptr)
|
|
|
+ // Alloc storage
|
|
|
+ if(!err)
|
|
|
{
|
|
|
- sector->octree.placeSceneNode(sn);
|
|
|
+ err = shapeStorage.create(alloc, vertCount);
|
|
|
}
|
|
|
- else
|
|
|
+
|
|
|
+ // Assign storage to hull
|
|
|
+ if(!err)
|
|
|
{
|
|
|
- ANKI_LOGW("Spatial outside all sectors");
|
|
|
+ memcpy(&shapeStorage[0], &vertPositions[0], sizeof(Vec4) * vertCount);
|
|
|
+ hull->initStorage(&shapeStorage[0], vertCount);
|
|
|
}
|
|
|
-#endif
|
|
|
-}
|
|
|
|
|
|
-//==============================================================================
|
|
|
-void SectorGroup::doVisibilityTests(SceneNode& sn, VisibilityTest test,
|
|
|
- Renderer* r)
|
|
|
-{
|
|
|
-#if 0
|
|
|
- // Set all sectors to not visible
|
|
|
- for(Sector* sector : sectors)
|
|
|
+ // Cleanup on error
|
|
|
+ if(err)
|
|
|
{
|
|
|
- sector->visibleBy = VB_NONE;
|
|
|
+ shapeStorage.destroy(alloc);
|
|
|
+
|
|
|
+ if(hull)
|
|
|
+ {
|
|
|
+ alloc.deleteInstance(hull);
|
|
|
+ hull = nullptr;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- doVisibilityTestsInternal(sn, test, r, VB_CAMERA);
|
|
|
-#endif
|
|
|
+ return hull;
|
|
|
}
|
|
|
|
|
|
//==============================================================================
|
|
|
-void SectorGroup::doVisibilityTestsInternal(SceneNode& sn, VisibilityTest test,
|
|
|
- Renderer* r, VisibleBy visibleBy)
|
|
|
+Sector* SectorGroup::createNewSector(const SArray<Vec4>& vertexPositions)
|
|
|
{
|
|
|
-#if 0
|
|
|
- Frustumable* fr = sn.getFrustumable();
|
|
|
- ANKI_ASSERT(fr != nullptr && "sn should be frustumable");
|
|
|
- fr->visible = nullptr;
|
|
|
-
|
|
|
- SpatialComponent* sp = sn.getSpatial();
|
|
|
- ANKI_ASSERT(sp != nullptr && "sn should be spatial as well");
|
|
|
-
|
|
|
- // sn is not placed in any octree
|
|
|
- if(sp->getOctreeNode() == nullptr)
|
|
|
+ Sector* sector = getAllocator().newInstance<Sector>(this);
|
|
|
+ if(sector)
|
|
|
{
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- //
|
|
|
- // Find the visible sectors
|
|
|
- //
|
|
|
-
|
|
|
- SceneFrameVector<Sector*> visibleSectors(scene->getFrameAllocator());
|
|
|
-
|
|
|
- // Find the sector that contains the frustumable
|
|
|
- Sector& containerSector = sp->getOctreeNode()->getOctree().getSector();
|
|
|
- containerSector.visibleBy |= visibleBy;
|
|
|
- visibleSectors.push_back(&containerSector);
|
|
|
-
|
|
|
- // Loop all sector portals and add other sectors
|
|
|
- for(Portal* portal : containerSector.portals)
|
|
|
- {
|
|
|
- if(ANKI_UNLIKELY(!portal->open))
|
|
|
- {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- // Get the "other" sector of that portal
|
|
|
- Sector* testAgainstSector;
|
|
|
-
|
|
|
- if(portal->sectors[0] == &containerSector)
|
|
|
- {
|
|
|
- testAgainstSector = portal->sectors[1];
|
|
|
- }
|
|
|
- else
|
|
|
+ Error err = sector->create(vertexPositions);
|
|
|
+ if(err)
|
|
|
{
|
|
|
- ANKI_ASSERT(portal->sectors[1] == &containerSector);
|
|
|
- testAgainstSector = portal->sectors[0];
|
|
|
+ getAllocator().deleteInstance(sector);
|
|
|
+ sector = nullptr;
|
|
|
}
|
|
|
- ANKI_ASSERT(testAgainstSector != nullptr);
|
|
|
+ }
|
|
|
|
|
|
- // Search if portal is in the container from another portal
|
|
|
- SceneVector<Sector*>::iterator it = std::find(visibleSectors.begin(),
|
|
|
- visibleSectors.end(), testAgainstSector);
|
|
|
+ return sector;
|
|
|
+}
|
|
|
|
|
|
- if(it == visibleSectors.end())
|
|
|
+//==============================================================================
|
|
|
+Portal* SectorGroup::createNewPortal(const SArray<Vec4>& vertexPositions)
|
|
|
+{
|
|
|
+ Portal* portal = getAllocator().newInstance<Portal>(this);
|
|
|
+ if(portal)
|
|
|
+ {
|
|
|
+ Error err = portal->create(vertexPositions);
|
|
|
+ if(err)
|
|
|
{
|
|
|
- // Not found so test the portal
|
|
|
-
|
|
|
- // Portal is visible
|
|
|
- if(fr->insideFrustum(portal->shape))
|
|
|
- {
|
|
|
- /*if(r == nullptr || r->doVisibilityTests(portal->shape))*/
|
|
|
- {
|
|
|
- testAgainstSector->visibleBy |= visibleBy;
|
|
|
- visibleSectors.push_back(testAgainstSector);
|
|
|
- }
|
|
|
- }
|
|
|
+ getAllocator().deleteInstance(portal);
|
|
|
+ portal = nullptr;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- //
|
|
|
- // For all visible sectors do the tests
|
|
|
- //
|
|
|
-
|
|
|
- // Create a few VisibilityTestResults to pass to every octree
|
|
|
- SceneVector<VisibilityTestResults> testResults(scene->getFrameAllocator());
|
|
|
- testResults.resize(visibleSectors.size(),
|
|
|
- VisibilityTestResults(scene->getFrameAllocator()));
|
|
|
- U renderablesCount = 0;
|
|
|
- U lightsCount = 0;
|
|
|
+ return portal;
|
|
|
+}
|
|
|
|
|
|
- // Run tests for every octree
|
|
|
- for(U i = 0; i < visibleSectors.size(); i++)
|
|
|
+//==============================================================================
|
|
|
+Error SectorGroup::bake()
|
|
|
+{
|
|
|
+ // Connect portals with sectors
|
|
|
+ auto it = m_portals.getBegin();
|
|
|
+ auto end = m_portals.getEnd();
|
|
|
+ for(; it != end; ++it)
|
|
|
{
|
|
|
- Sector* sector = visibleSectors[i];
|
|
|
-
|
|
|
- sector->octree.doVisibilityTests(sn, test, testResults[i]);
|
|
|
-
|
|
|
- renderablesCount += testResults[i].renderables.size();
|
|
|
- lightsCount += testResults[i].lights.size();
|
|
|
- }
|
|
|
-
|
|
|
- // If you don't want lights then the octree should do tests with them
|
|
|
- ANKI_ASSERT(!((test & VT_LIGHTS) == 0 && lightsCount != 0));
|
|
|
- // Same as ^
|
|
|
- ANKI_ASSERT(!((test & VT_RENDERABLES) == 0 && renderablesCount != 0));
|
|
|
+ Portal& portal = *(*it);
|
|
|
|
|
|
- //
|
|
|
- // Combine test results and try doing renderer tests
|
|
|
- //
|
|
|
+ auto sit = m_sectors.getBegin();
|
|
|
+ auto send = m_sectors.getEnd();
|
|
|
|
|
|
- // Create the visibility container
|
|
|
- VisibilityTestResults* visible =
|
|
|
- ANKI_NEW(VisibilityTestResults, scene->getFrameAllocator(),
|
|
|
- scene->getFrameAllocator());
|
|
|
-
|
|
|
- // Set the sizes to save some moves
|
|
|
- visible->renderables.reserve(renderablesCount);
|
|
|
- visible->lights.reserve(lightsCount);
|
|
|
-
|
|
|
- // Iterate previous test results and append to the combined one
|
|
|
- if(r == nullptr)
|
|
|
- {
|
|
|
- for(VisibilityTestResults& testResult : testResults)
|
|
|
- {
|
|
|
- visible->renderables.insert(
|
|
|
- visible->renderables.end(),
|
|
|
- testResult.renderables.begin(),
|
|
|
- testResult.renderables.end());
|
|
|
-
|
|
|
- visible->lights.insert(
|
|
|
- visible->lights.end(),
|
|
|
- testResult.lights.begin(),
|
|
|
- testResult.lights.end());
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- for(VisibilityTestResults& testResult : testResults)
|
|
|
+ for(; sit != send; ++sit)
|
|
|
{
|
|
|
- // First the renderables
|
|
|
- for(VisibleNode& renderable : testResult.renderables)
|
|
|
- {
|
|
|
- /*XXX if(r->getTiler().test2(
|
|
|
- renderable->getSpatial()->getOptimalCollisionShape()))*/
|
|
|
- {
|
|
|
- visible->renderables.push_back(renderable);
|
|
|
- }
|
|
|
- }
|
|
|
+ Sector& sector = *(*sit);
|
|
|
|
|
|
- // Then the lights
|
|
|
- for(VisibleNode& light : testResult.lights)
|
|
|
+ Bool collide = testCollisionShapes(
|
|
|
+ portal.getCollisionShape(), sector.getCollisionShape());
|
|
|
+
|
|
|
+ if(collide)
|
|
|
{
|
|
|
- /*XXX if(r->doVisibilityTests(
|
|
|
- light->getSpatial()->getOptimalCollisionShape()))*/
|
|
|
- {
|
|
|
- visible->lights.push_back(light);
|
|
|
- }
|
|
|
+ ANKI_CHECK(portal.addSector(§or));
|
|
|
+ ANKI_CHECK(sector.addPortal(&portal));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // The given frustumable is finished
|
|
|
- fr->visible = visible;
|
|
|
-
|
|
|
- //
|
|
|
- // Sort
|
|
|
- //
|
|
|
-
|
|
|
- Threadpool.h& threadPool = Threadpool.hSingleton::get();
|
|
|
-
|
|
|
- // Sort the renderables in a another thread
|
|
|
- DistanceSortJob dsjob;
|
|
|
- dsjob.nodes = visible->renderables.begin();
|
|
|
- dsjob.nodesCount = visible->renderables.size();
|
|
|
- dsjob.origin = sp->getSpatialOrigin();
|
|
|
- threadPool.assignNewJob(0, &dsjob);
|
|
|
-
|
|
|
- // The rest of the jobs are dummy
|
|
|
- Array<ThreadJobDummy, Threadpool.h::MAX_THREADS> dummyjobs;
|
|
|
- for(U i = 1; i < threadPool.getThreadsCount(); i++)
|
|
|
- {
|
|
|
- threadPool.assignNewJob(i, &dummyjobs[i]);
|
|
|
- }
|
|
|
-
|
|
|
- // Sort the lights in the main thread
|
|
|
- std::sort(visible->lights.begin(),
|
|
|
- visible->lights.end(), DistanceSortFunctor{sp->getSpatialOrigin()});
|
|
|
-
|
|
|
- threadPool.waitForAllJobsToFinish();
|
|
|
-
|
|
|
- //
|
|
|
- // Continue by testing the lights
|
|
|
- //
|
|
|
-
|
|
|
- for(VisibleNode& lsn : visible->lights)
|
|
|
- {
|
|
|
- Light* l = lsn.node->getLight();
|
|
|
- ANKI_ASSERT(l != nullptr);
|
|
|
-
|
|
|
- if(l->getShadowEnabled())
|
|
|
- {
|
|
|
- ANKI_ASSERT(lsn.node->getFrustumable() != nullptr);
|
|
|
-
|
|
|
- doVisibilityTestsInternal(*lsn.node,
|
|
|
- (VisibilityTest)(VT_RENDERABLES | VT_ONLY_SHADOW_CASTERS),
|
|
|
- nullptr, VB_LIGHT);
|
|
|
- }
|
|
|
- }
|
|
|
-#endif
|
|
|
+ return ErrorCode::NONE;
|
|
|
}
|
|
|
|
|
|
} // end namespace anki
|
|
|
-
|
|
|
-#endif
|