diff --git a/CMakeLists.txt b/CMakeLists.txt index 01bcf6679..d2384a453 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -237,26 +237,6 @@ elseif (MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fp:fast") endif() - option(USE_MSVC_STRING_POOLING "Use MSVC /GF string pooling option" ON) - if (USE_MSVC_STRING_POOLING) - set(CMAKE_C_FLAGS "/GF ${CMAKE_C_FLAGS}") - set(CMAKE_CXX_FLAGS "/GF ${CMAKE_CXX_FLAGS}") - endif() - - option(USE_MSVC_FUNCTION_LEVEL_LINKING "Use MSVC /Gy function level linking option" ON) - if (USE_MSVC_FUNCTION_LEVEL_LINKING) - set(CMAKE_C_FLAGS "/Gy ${CMAKE_C_FLAGS}") - set(CMAKE_CXX_FLAGS "/Gy ${CMAKE_CXX_FLAGS}") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /OPT:REF") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /OPT:REF") - endif() - - option(USE_MSVC_COMDAT_FOLDING "Use MSVC /OPT:ICF COMDAT folding option" ON) - if (USE_MSVC_COMDAT_FOLDING) - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /OPT:ICF") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /OPT:ICF") - endif() - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /MANIFEST:NO") endif() diff --git a/Projects/Editor/CMakeLists.txt b/Projects/Editor/CMakeLists.txt index 8cd7979e7..da55a1b9e 100644 --- a/Projects/Editor/CMakeLists.txt +++ b/Projects/Editor/CMakeLists.txt @@ -5,11 +5,12 @@ include_directories( ${SKYLICHT_ENGINE_SOURCE_DIR}/Projects/Skylicht/System/Source ${SKYLICHT_ENGINE_SOURCE_DIR}/Projects/Skylicht/Engine/Source ${SKYLICHT_ENGINE_SOURCE_DIR}/Projects/Skylicht/Components/Source + ${SKYLICHT_ENGINE_SOURCE_DIR}/Projects/Skylicht/Collision/Source + ${SKYLICHT_ENGINE_SOURCE_DIR}/Projects/Skylicht/Physics/Source ${SKYLICHT_ENGINE_SOURCE_DIR}/Projects/Skylicht/Client/Source ${SKYLICHT_ENGINE_SOURCE_DIR}/Projects/Skylicht/Lightmapper/Source ${SKYLICHT_ENGINE_SOURCE_DIR}/Projects/Skylicht/Audio/Source ${SKYLICHT_ENGINE_SOURCE_DIR}/Projects/Skylicht/Crypto/Source - #${SKYLICHT_ENGINE_SOURCE_DIR}/Projects/Skylicht/Physics/Source ) add_definitions(-DSKYLICHT_EDITOR) diff --git a/Projects/Editor/Source/Editor/CEditor.cpp b/Projects/Editor/Source/Editor/CEditor.cpp index 2b936bfb9..4437160ff 100644 --- a/Projects/Editor/Source/Editor/CEditor.cpp +++ b/Projects/Editor/Source/Editor/CEditor.cpp @@ -38,6 +38,7 @@ This file is part of the "Skylicht Engine". #include "SpaceController/CSceneController.h" #include "SpaceController/CPropertyController.h" +#include "SpaceController/CCollisionController.h" #include "AssetManager/CAssetManager.h" #include "Selection/CSelection.h" @@ -72,6 +73,7 @@ namespace Skylicht m_spriteIcon->updateTexture(); // init controller + CCollisionController::createGetInstance(); CSceneController::createGetInstance()->initContextMenu(m_canvas); CPropertyController::createGetInstance(); CSelection::createGetInstance(); @@ -84,6 +86,7 @@ namespace Skylicht CEditorActivator::releaseInstance(); CPropertyController::releaseInstance(); CSceneController::releaseInstance(); + CCollisionController::releaseInstance(); CSelection::releaseInstance(); CProjectSettings::releaseInstance(); diff --git a/Projects/Editor/Source/Editor/SpaceController/CCollisionController.cpp b/Projects/Editor/Source/Editor/SpaceController/CCollisionController.cpp new file mode 100644 index 000000000..ad61de22c --- /dev/null +++ b/Projects/Editor/Source/Editor/SpaceController/CCollisionController.cpp @@ -0,0 +1,47 @@ +/* +!@ +MIT License + +Copyright (c) 2021 Skylicht Technology CO., LTD + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files +(the "Software"), to deal in the Software without restriction, including without limitation the Rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +This file is part of the "Skylicht Engine". +https://github.com/skylicht-lab/skylicht-engine +!# +*/ + +#include "pch.h" +#include "CCollisionController.h" + +namespace Skylicht +{ + namespace Editor + { + CCollisionController::CCollisionController() + { + m_bbCollision = new CBBCollisionManager(); + } + + CCollisionController::~CCollisionController() + { + delete m_bbCollision; + } + + void CCollisionController::clear() + { + m_bbCollision->clear(); + } + } +} \ No newline at end of file diff --git a/Projects/Editor/Source/Editor/SpaceController/CCollisionController.h b/Projects/Editor/Source/Editor/SpaceController/CCollisionController.h new file mode 100644 index 000000000..8840e810d --- /dev/null +++ b/Projects/Editor/Source/Editor/SpaceController/CCollisionController.h @@ -0,0 +1,53 @@ +/* +!@ +MIT License + +Copyright (c) 2021 Skylicht Technology CO., LTD + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files +(the "Software"), to deal in the Software without restriction, including without limitation the Rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +This file is part of the "Skylicht Engine". +https://github.com/skylicht-lab/skylicht-engine +!# +*/ + +#pragma once + +#include "Utils/CGameSingleton.h" + +#include "Collision/CBBCollisionManager.h" + +namespace Skylicht +{ + namespace Editor + { + class CCollisionController :public CGameSingleton + { + protected: + CBBCollisionManager* m_bbCollision; + + public: + CCollisionController(); + + virtual ~CCollisionController(); + + inline CBBCollisionManager* getBBCollision() + { + return m_bbCollision; + } + + void clear(); + }; + } +} \ No newline at end of file diff --git a/Projects/Editor/Source/Editor/SpaceController/CSceneController.cpp b/Projects/Editor/Source/Editor/SpaceController/CSceneController.cpp index 8423c3e16..6682f6720 100644 --- a/Projects/Editor/Source/Editor/SpaceController/CSceneController.cpp +++ b/Projects/Editor/Source/Editor/SpaceController/CSceneController.cpp @@ -23,8 +23,11 @@ This file is part of the "Skylicht Engine". */ #include "pch.h" + #include "CSceneController.h" #include "CPropertyController.h" +#include "CCollisionController.h" + #include "Selection/CSelection.h" #include "Scene/CSceneExporter.h" @@ -137,6 +140,8 @@ namespace Skylicht // clear current scene gui CSelection::getInstance()->clear(); CPropertyController::getInstance()->setProperty(NULL); + CCollisionController::getInstance()->clear(); + CHandles::getInstance()->end(); CHandles::getInstance()->setNullRenderer(); diff --git a/Projects/Editor/Source/EditorComponents/DirectionLight/CGDirectionLight.cpp b/Projects/Editor/Source/EditorComponents/DirectionLight/CGDirectionLight.cpp index 80291ceca..7b5a4dfad 100644 --- a/Projects/Editor/Source/EditorComponents/DirectionLight/CGDirectionLight.cpp +++ b/Projects/Editor/Source/EditorComponents/DirectionLight/CGDirectionLight.cpp @@ -32,6 +32,8 @@ This file is part of the "Skylicht Engine". #include "Editor/CEditor.h" #include "Handles/CHandles.h" +#include "Editor/SpaceController/CCollisionController.h" + namespace Skylicht { namespace Editor @@ -48,7 +50,8 @@ namespace Skylicht CGDirectionLight::~CGDirectionLight() { - + CBBCollisionManager* bbCollision = CCollisionController::getInstance()->getBBCollision(); + bbCollision->removeCollision(&m_collisionNode, 1); } void CGDirectionLight::initComponent() @@ -63,6 +66,9 @@ namespace Skylicht m_sprite->setEnableSerializable(false); addLinkComponent(m_sprite); + + CBBCollisionManager* bbCollision = CCollisionController::getInstance()->getBBCollision(); + m_collisionNode = bbCollision->addBBCollision(m_gameObject, core::aabbox3df(core::vector3df(-2.0f, -2.0f, -2.0f), core::vector3df(2.0f, 2.0f, 2.0f))); } void CGDirectionLight::updateComponent() diff --git a/Projects/Editor/Source/EditorComponents/DirectionLight/CGDirectionLight.h b/Projects/Editor/Source/EditorComponents/DirectionLight/CGDirectionLight.h index f8c81207d..1904f0ff3 100644 --- a/Projects/Editor/Source/EditorComponents/DirectionLight/CGDirectionLight.h +++ b/Projects/Editor/Source/EditorComponents/DirectionLight/CGDirectionLight.h @@ -27,6 +27,7 @@ This file is part of the "Skylicht Engine". #include "EditorComponents/CGizmosComponent.h" #include "Lighting/CDirectionalLight.h" #include "SpriteDraw/CSprite.h" +#include "Collision/CCollisionNode.h" namespace Skylicht { @@ -37,6 +38,7 @@ namespace Skylicht protected: CDirectionalLight* m_directionLight; CSprite* m_sprite; + CCollisionNode* m_collisionNode; public: CGDirectionLight(); diff --git a/Projects/Editor/Source/GUI/Controls/CText.cpp b/Projects/Editor/Source/GUI/Controls/CText.cpp index 20b417a53..78f1babba 100644 --- a/Projects/Editor/Source/GUI/Controls/CText.cpp +++ b/Projects/Editor/Source/GUI/Controls/CText.cpp @@ -81,7 +81,7 @@ namespace Skylicht if (s.length() > 0 && s[s.length() - 1] != '\n') s += '\n'; - for (u32 i = 0, n = s.length(); i < n; i++) + for (u32 i = 0, n = (u32)s.length(); i < n; i++) { float distance = fabs(x - point.X); diff --git a/Projects/Editor/Source/GUI/Controls/CTextContainer.cpp b/Projects/Editor/Source/GUI/Controls/CTextContainer.cpp index 310698765..f620b271e 100644 --- a/Projects/Editor/Source/GUI/Controls/CTextContainer.cpp +++ b/Projects/Editor/Source/GUI/Controls/CTextContainer.cpp @@ -342,7 +342,7 @@ namespace Skylicht } m_string.insert(m_caretBegin, string); - m_caretBegin += string.length(); + m_caretBegin += (u32)string.length(); m_textChange = true; @@ -357,7 +357,7 @@ namespace Skylicht void CTextContainer::setCaretBegin(u32 line, u32 c) { if (line > m_lines.size()) - line = m_lines.size() - 1; + line = (u32)m_lines.size() - 1; m_caretBeginLine = line; m_caretBeginPosition = c; @@ -369,7 +369,7 @@ namespace Skylicht void CTextContainer::setCaretEnd(u32 line, u32 c) { if (line > m_lines.size()) - line = m_lines.size() - 1; + line = (u32)m_lines.size() - 1; m_caretEndLine = line; m_caretEndPosition = c; @@ -533,7 +533,7 @@ namespace Skylicht if (foundLine != NULL) { const std::wstring& s = foundLine->getString(); - u32 length = s.length(); + u32 length = (u32)s.length(); if (charPosition < length) { bool isCharacterGroup = isCharacter(s[charPosition]); @@ -771,7 +771,7 @@ namespace Skylicht { if (m_lines.size() > 0) { - u32 caretLineID = m_lines.size() - 1; + u32 caretLineID = (u32)m_lines.size() - 1; u32 caretCharID = m_lines.back()->getLength(); setCaretBegin(caretLineID, caretCharID); diff --git a/Projects/Skylicht/Collision/Source/Collision/CBBCollisionManager.cpp b/Projects/Skylicht/Collision/Source/Collision/CBBCollisionManager.cpp index 4875cab2c..450fb1725 100644 --- a/Projects/Skylicht/Collision/Source/Collision/CBBCollisionManager.cpp +++ b/Projects/Skylicht/Collision/Source/Collision/CBBCollisionManager.cpp @@ -28,211 +28,24 @@ This file is part of the "Skylicht Engine". namespace Skylicht { - CBBCollisionManager::CBBCollisionManager() : - m_root(NULL), - m_minimalPolysPerNode(128), - m_nodeCount(0) + CBBCollisionManager::CBBCollisionManager() { } CBBCollisionManager::~CBBCollisionManager() { - if (m_root != NULL) - delete m_root; - for (u32 i = 0, n = m_nodes.size(); i < n; i++) - { - delete m_nodes[i]->Selector; - delete m_nodes[i]; - } - m_nodes.clear(); } - void CBBCollisionManager::addBBCollision(CGameObject* object, const core::aabbox3df& bbox) + CCollisionNode* CBBCollisionManager::addBBCollision(CGameObject* object, const core::aabbox3df& bbox) { CEntity* entity = object->getEntity(); - m_nodes.push_back(new CCollisionNode(object, entity, new CTriangleBB(entity, bbox))); - rebuildOctree(); - } - - void CBBCollisionManager::removeBBCollision(CGameObject* object) - { - for (u32 i = 0, n = m_nodes.size(); i < n; i++) - { - if (m_nodes[i]->GameObject == object) - { - if (n >= 2) - { - // swap to last and delete - CCollisionNode* t = m_nodes[n - 1]; - m_nodes[n - 1] = m_nodes[i]; - m_nodes[i] = t; - delete m_nodes[n - 1]; - m_nodes.erase(n - 1); - return; - } - else - { - delete m_nodes[i]; - m_nodes.erase(i); - return; - } - } - } + CCollisionNode* node = new CCollisionNode(object, entity, new CTriangleBB(entity, bbox)); + m_nodes.push_back(node); rebuildOctree(); - } - - void CBBCollisionManager::rebuildOctree() - { - if (m_root != NULL) - delete m_root; - - m_root = NULL; - m_root = new COctreeNode(); - - const u32 start = os::Timer::getRealTime(); - u32 numPoly = 0; - - // step 1: update transform and triangles - for (u32 i = 0, n = m_nodes.size(); i < n; i++) - { - m_nodes[i]->updateTransform(); - numPoly += m_nodes[i]->Triangles.size(); - } - - m_root->Triangles.set_used(numPoly); - m_root->Collisions.set_used(numPoly); - - // step 2: index triangle & bbox - u32 idx = 0; - - m_root->Box.reset(m_nodes[0]->Triangles[0].pointA); - - for (u32 i = 0, n = m_nodes.size(); i < n; i++) - { - CCollisionNode* node = m_nodes[i]; - - u32 numTris = node->Triangles.size(); - core::triangle3df* tris = node->Triangles.pointer(); - - for (u32 j = 0; j < numTris; j++) - { - m_root->Triangles[idx] = j; - m_root->Collisions[idx] = node; - - m_root->Box.addInternalPoint(tris[j].pointA); - m_root->Box.addInternalPoint(tris[j].pointB); - m_root->Box.addInternalPoint(tris[j].pointC); - - idx++; - } - } - - m_root->OctreeBox = m_root->Box; - m_root->Level = 0; - - // step 3 build octree child node - constructOctree(m_root); - - c8 tmp[256]; - sprintf(tmp, "Needed %ums to CBBCollisionManager::rebuildOctree (%d nodes, %u polys)", os::Timer::getRealTime() - start, m_nodeCount, numPoly); - os::Printer::log(tmp, ELL_INFORMATION); - } - - void CBBCollisionManager::constructOctree(COctreeNode* node) - { - const core::vector3df& middle = node->Box.getCenter(); - core::vector3df edges[8]; - node->Box.getEdges(edges); - - core::aabbox3d box; - core::array keepTriangles; - core::array keepCollisions; - - // calculate children - if (!node->Box.isEmpty() && node->Triangles.size() > m_minimalPolysPerNode) - { - for (s32 ch = 0; ch < 8; ++ch) - { - box.reset(middle); - box.addInternalPoint(edges[ch]); - - node->Childs[ch] = new COctreeNode(); - - // step 1 new child octree - COctreeNode* currentNode = node->Childs[ch]; - currentNode->Parent = node; - currentNode->OctreeBox = box; - currentNode->Level = node->Level + 1; - - s32 numTri = (s32)node->Triangles.size(); - - // step 2 collect triangle in side the child - for (s32 i = 0; i < numTri; ++i) - { - int triID = node->Triangles[i]; - CCollisionNode* collision = node->Collisions[i]; - - core::triangle3df* triangles = collision->Triangles.pointer(); - core::triangle3df* tri = &triangles[triID]; - - if (tri->isTotalOutsideBox(box) == false && tri->isTotalInsideBox(node->OctreeBox) == true) - { - // move triangles to child - currentNode->Triangles.push_back(triID); - currentNode->Collisions.push_back(collision); - - if (i == 0) - { - currentNode->Box.reset(tri->pointA); - currentNode->Box.addInternalPoint(tri->pointB); - currentNode->Box.addInternalPoint(tri->pointC); - } - else - { - currentNode->Box.addInternalPoint(tri->pointA); - currentNode->Box.addInternalPoint(tri->pointB); - currentNode->Box.addInternalPoint(tri->pointC); - } - } - else - { - // keep on parent node - keepTriangles.push_back(triID); - keepCollisions.push_back(collision); - } - } - - // update current triangles - node->Triangles.clear(); - node->Collisions.clear(); - - node->Triangles.set_used(keepTriangles.size()); - node->Collisions.set_used(keepCollisions.size()); - - for (int i = 0, n = (int)keepTriangles.size(); i < n; i++) - { - node->Triangles[i] = keepTriangles[i]; - node->Collisions[i] = keepCollisions[i]; - } - - keepTriangles.clear(); - keepCollisions.clear(); - if (currentNode->Triangles.empty()) - { - // if no triangle - delete node->Childs[ch]; - node->Childs[ch] = NULL; - } - else - { - // construct on childs - constructOctree(node->Childs[ch]); - } - } - } + return node; } } \ No newline at end of file diff --git a/Projects/Skylicht/Collision/Source/Collision/CBBCollisionManager.h b/Projects/Skylicht/Collision/Source/Collision/CBBCollisionManager.h index 0eba47bb1..1a6707663 100644 --- a/Projects/Skylicht/Collision/Source/Collision/CBBCollisionManager.h +++ b/Projects/Skylicht/Collision/Source/Collision/CBBCollisionManager.h @@ -27,35 +27,22 @@ This file is part of the "Skylicht Engine". #include "CTriangleSelector.h" #include "CCollisionNode.h" #include "COctreeNode.h" +#include "COctreeBuilder.h" #include "GameObject/CGameObject.h" #include "Utils/CGameSingleton.h" namespace Skylicht { - class CBBCollisionManager + class CBBCollisionManager : public COctreeBuilder { protected: - core::array m_nodes; - - COctreeNode* m_root; - - u32 m_minimalPolysPerNode; - u32 m_nodeCount; public: CBBCollisionManager(); virtual ~CBBCollisionManager(); - void addBBCollision(CGameObject* object, const core::aabbox3df& bbox); - - void removeBBCollision(CGameObject* object); - - protected: - - void rebuildOctree(); - - void constructOctree(COctreeNode* node); + CCollisionNode* addBBCollision(CGameObject* object, const core::aabbox3df& bbox); }; } \ No newline at end of file diff --git a/Projects/Skylicht/Collision/Source/Collision/COctreeBuilder.cpp b/Projects/Skylicht/Collision/Source/Collision/COctreeBuilder.cpp new file mode 100644 index 000000000..18d035a7a --- /dev/null +++ b/Projects/Skylicht/Collision/Source/Collision/COctreeBuilder.cpp @@ -0,0 +1,281 @@ +/* +!@ +MIT License + +Copyright (c) 2021 Skylicht Technology CO., LTD + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files +(the "Software"), to deal in the Software without restriction, including without limitation the Rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +This file is part of the "Skylicht Engine". +https://github.com/skylicht-lab/skylicht-engine +!# +*/ + +#include "pch.h" +#include "COctreeBuilder.h" + +namespace Skylicht +{ + COctreeBuilder::COctreeBuilder() : + m_root(NULL), + m_minimalPolysPerNode(128), + m_nodeCount(0) + { + + } + + COctreeBuilder::~COctreeBuilder() + { + clear(); + } + + void COctreeBuilder::clear() + { + if (m_root != NULL) + delete m_root; + + m_root = NULL; + + for (u32 i = 0, n = m_nodes.size(); i < n; i++) + { + delete m_nodes[i]->Selector; + delete m_nodes[i]; + } + m_nodes.clear(); + } + + void COctreeBuilder::removeCollision(CGameObject* object) + { + for (u32 i = 0, n = m_nodes.size(); i < n; i++) + { + if (m_nodes[i]->GameObject == object) + { + if (n >= 2) + { + // swap to last and delete + CCollisionNode* t = m_nodes[n - 1]; + m_nodes[n - 1] = m_nodes[i]; + m_nodes[i] = t; + delete m_nodes[n - 1]; + m_nodes.erase(n - 1); + return; + } + else + { + delete m_nodes[i]; + m_nodes.erase(i); + return; + } + } + } + + rebuildOctree(); + } + + void COctreeBuilder::removeCollision(CCollisionNode** nodes, int count) + { + for (u32 i = 0, n = m_nodes.size(); i < n; i++) + { + int id = findNode(m_nodes[i], nodes, count); + if (id >= 0) + { + nodes[id] = NULL; + + if (n >= 2) + { + // swap to last and delete + CCollisionNode* t = m_nodes[n - 1]; + m_nodes[n - 1] = m_nodes[i]; + m_nodes[i] = t; + delete m_nodes[n - 1]; + m_nodes.erase(n - 1); + return; + } + else + { + delete m_nodes[i]; + m_nodes.erase(i); + return; + } + } + } + + rebuildOctree(); + } + + int COctreeBuilder::findNode(CCollisionNode* node, CCollisionNode** nodes, int count) + { + for (int i = 0; i < count; i++) + { + if (nodes[i] == node) + { + return i; + } + } + + return -1; + } + + void COctreeBuilder::rebuildOctree() + { + if (m_root != NULL) + delete m_root; + + m_root = NULL; + m_root = new COctreeNode(); + + const u32 start = os::Timer::getRealTime(); + u32 numPoly = 0; + + // step 1: update transform and triangles + for (u32 i = 0, n = m_nodes.size(); i < n; i++) + { + m_nodes[i]->updateTransform(); + numPoly += m_nodes[i]->Triangles.size(); + } + + m_root->Triangles.set_used(numPoly); + m_root->Collisions.set_used(numPoly); + + // step 2: index triangle & bbox + u32 idx = 0; + + m_root->Box.reset(m_nodes[0]->Triangles[0].pointA); + + for (u32 i = 0, n = m_nodes.size(); i < n; i++) + { + CCollisionNode* node = m_nodes[i]; + + u32 numTris = node->Triangles.size(); + core::triangle3df* tris = node->Triangles.pointer(); + + for (u32 j = 0; j < numTris; j++) + { + m_root->Triangles[idx] = j; + m_root->Collisions[idx] = node; + + m_root->Box.addInternalPoint(tris[j].pointA); + m_root->Box.addInternalPoint(tris[j].pointB); + m_root->Box.addInternalPoint(tris[j].pointC); + + idx++; + } + } + + m_root->OctreeBox = m_root->Box; + m_root->Level = 0; + + // step 3 build octree child node + constructOctree(m_root); + + c8 tmp[256]; + sprintf(tmp, "Needed %ums to COctreeBuilder::rebuildOctree (%d nodes, %u polys)", os::Timer::getRealTime() - start, m_nodeCount, numPoly); + os::Printer::log(tmp, ELL_INFORMATION); + } + + void COctreeBuilder::constructOctree(COctreeNode* node) + { + const core::vector3df& middle = node->Box.getCenter(); + core::vector3df edges[8]; + node->Box.getEdges(edges); + + core::aabbox3d box; + core::array keepTriangles; + core::array keepCollisions; + + // calculate children + if (!node->Box.isEmpty() && node->Triangles.size() > m_minimalPolysPerNode) + { + for (s32 ch = 0; ch < 8; ++ch) + { + box.reset(middle); + box.addInternalPoint(edges[ch]); + + node->Childs[ch] = new COctreeNode(); + + // step 1 new child octree + COctreeNode* currentNode = node->Childs[ch]; + currentNode->Parent = node; + currentNode->OctreeBox = box; + currentNode->Level = node->Level + 1; + + s32 numTri = (s32)node->Triangles.size(); + + // step 2 collect triangle in side the child + for (s32 i = 0; i < numTri; ++i) + { + int triID = node->Triangles[i]; + CCollisionNode* collision = node->Collisions[i]; + + core::triangle3df* triangles = collision->Triangles.pointer(); + core::triangle3df* tri = &triangles[triID]; + + if (tri->isTotalOutsideBox(box) == false && tri->isTotalInsideBox(node->OctreeBox) == true) + { + // move triangles to child + currentNode->Triangles.push_back(triID); + currentNode->Collisions.push_back(collision); + + if (i == 0) + { + currentNode->Box.reset(tri->pointA); + currentNode->Box.addInternalPoint(tri->pointB); + currentNode->Box.addInternalPoint(tri->pointC); + } + else + { + currentNode->Box.addInternalPoint(tri->pointA); + currentNode->Box.addInternalPoint(tri->pointB); + currentNode->Box.addInternalPoint(tri->pointC); + } + } + else + { + // keep on parent node + keepTriangles.push_back(triID); + keepCollisions.push_back(collision); + } + } + + // update current triangles + node->Triangles.clear(); + node->Collisions.clear(); + + node->Triangles.set_used(keepTriangles.size()); + node->Collisions.set_used(keepCollisions.size()); + + for (int i = 0, n = (int)keepTriangles.size(); i < n; i++) + { + node->Triangles[i] = keepTriangles[i]; + node->Collisions[i] = keepCollisions[i]; + } + + keepTriangles.clear(); + keepCollisions.clear(); + + if (currentNode->Triangles.empty()) + { + // if no triangle + delete node->Childs[ch]; + node->Childs[ch] = NULL; + } + else + { + // construct on childs + constructOctree(node->Childs[ch]); + } + } + } + } +} \ No newline at end of file diff --git a/Projects/Skylicht/Collision/Source/Collision/COctreeBuilder.h b/Projects/Skylicht/Collision/Source/Collision/COctreeBuilder.h new file mode 100644 index 000000000..7bae3485a --- /dev/null +++ b/Projects/Skylicht/Collision/Source/Collision/COctreeBuilder.h @@ -0,0 +1,60 @@ +/* +!@ +MIT License + +Copyright (c) 2021 Skylicht Technology CO., LTD + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files +(the "Software"), to deal in the Software without restriction, including without limitation the Rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +This file is part of the "Skylicht Engine". +https://github.com/skylicht-lab/skylicht-engine +!# +*/ + +#pragma once + +#include "COctreeNode.h" + +namespace Skylicht +{ + class COctreeBuilder + { + protected: + core::array m_nodes; + + COctreeNode* m_root; + + u32 m_minimalPolysPerNode; + u32 m_nodeCount; + + public: + COctreeBuilder(); + + virtual ~COctreeBuilder(); + + void clear(); + + void rebuildOctree(); + + void removeCollision(CGameObject* object); + + void removeCollision(CCollisionNode** nodes, int count); + + protected: + + void constructOctree(COctreeNode* node); + + int findNode(CCollisionNode* node, CCollisionNode** nodes, int count); + }; +} \ No newline at end of file diff --git a/Projects/Skylicht/Collision/Source/Collision/COctreeNode.h b/Projects/Skylicht/Collision/Source/Collision/COctreeNode.h index 6ed359c2f..9cce47476 100644 --- a/Projects/Skylicht/Collision/Source/Collision/COctreeNode.h +++ b/Projects/Skylicht/Collision/Source/Collision/COctreeNode.h @@ -34,7 +34,7 @@ namespace Skylicht //! Triangle index of CCollisionNode core::array Triangles; - //! CollisionNode + //! CollisionNode of Triangles core::array Collisions; COctreeNode* Childs[8];