Advertisement
Guest User

Octree Query - Frustum Search and Recursive Vector Inserts

a guest
Aug 11th, 2024
93
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 25.46 KB | Software | 0 0
  1. namespace Louron {
  2.  
  3.     struct OctreeBoundsConfig {
  4.  
  5.         /// <summary>
  6.         /// This is the minimum node size we can have. If the node attemps to split
  7.         /// this configuration will prevent the Octree from splitting into smaller
  8.         /// child nodes. This is set at 1.0f == 1 unit in worldspace.
  9.         ///
  10.         /// If this is increased to 2.0f, a node cannot split if it's child nodes
  11.         /// will be smaller than 2 units in worldspace.
  12.         /// </summary>
  13.         float MinNodeSize = 1.0f;
  14.  
  15.         /// <summary>
  16.         /// This is the preferred limit of Data Sources per node before the node
  17.         /// splits into child nodes and attempts to reinsert these Data Sources
  18.         /// into its child nodes.
  19.         ///
  20.         /// If we reach the maximum depth, or the minimum node size, we cannot
  21.         /// split any further, which is why this is a preferred limit for
  22.         /// Data Sources per node.
  23.         /// </summary>
  24.         int PreferredDataSourceLimit = 4;
  25.  
  26.         /// <summary>
  27.         /// Looseness of Octree
  28.         ///
  29.         /// Min Value: 1.0f
  30.         /// Max Value: 2.0f
  31.         /// (any other values will be clamped between min and max)
  32.         ///
  33.         /// Standard Octree 1.0f: child boundaries are exactly 100% / 2.0 = 50%
  34.         /// of the parent node boundaries.
  35.         ///
  36.         /// As you increase this to two, this increases how far the octree
  37.         /// overlaps into neighbouring child nodes to reduce edge cases.
  38.         ///
  39.         /// Loose Octree 1.5f: child boundaries are exactly 150% / 2.0 = 75%
  40.         /// of the parent node boundaries.
  41.         ///
  42.         /// </summary>
  43.         float Looseness = 1.0f;
  44.  
  45.         /// <summary>
  46.         /// Initial Bounds of the Octree Root Node.
  47.         /// </summary>
  48.         Bounds_AABB InitialBounds{};
  49.  
  50.         /// <summary>
  51.         /// When a Node is Queried, and itself and it's children
  52.         /// have no data sources, a life count will be incremented
  53.         /// until 8 is reached. This means that if you remove a
  54.         /// data source, it will start the counter. If you query
  55.         /// and the node is empty, we will increment again. If the
  56.         /// life of the node exceeds the MaxLifeIfEmpty, the node
  57.         /// and its children will be deleted. Each Node starts off
  58.         /// with this amount, and doubles each time if it is
  59.         /// saved by something being added up to a max of 64 queries.
  60.         /// </summary>
  61.         uint8_t MaxLifeIfEmpty = 8;
  62.  
  63.     };
  64.  
  65.     template <typename DataType>
  66.     class OctreeBounds {
  67.  
  68.     public:
  69.  
  70.         template <typename DataType>
  71.         struct OctreeDataSource {
  72.  
  73.             DataType Data;
  74.             Bounds_AABB Bounds;
  75.  
  76.             OctreeDataSource() = delete;
  77.             OctreeDataSource(DataType data, Bounds_AABB bounds) : Data(data), Bounds(bounds) { }
  78.  
  79.         };
  80.         using OctreeData = std::shared_ptr<OctreeDataSource<DataType>>;
  81.  
  82.     private:
  83.  
  84.  
  85.         template <typename DataType>
  86.         class OctreeBoundsNode {
  87.  
  88.         private:
  89.  
  90.             using OctreeNode = std::shared_ptr<OctreeBoundsNode<DataType>>;
  91.  
  92.         public:
  93.  
  94.             // Constructor and Destructor
  95.             OctreeBoundsNode() = default;
  96.             OctreeBoundsNode(Bounds_AABB node_bounds, OctreeBounds<DataType>* octree) : m_NodeBounds(node_bounds), m_Octree(octree){
  97.                 m_NodeMat4 = m_NodeBounds.GetGlobalBoundsMat4();
  98.                 if (m_Octree)
  99.                     m_LifeMax = m_Octree->m_Config.MaxLifeIfEmpty;
  100.                 else
  101.                     m_LifeMax = 8;
  102.             }
  103.             ~OctreeBoundsNode() {
  104.                 Clear();
  105.             }
  106.  
  107.             // COPY
  108.             OctreeBoundsNode(const OctreeBoundsNode&) = default;
  109.             OctreeBoundsNode& operator=(const OctreeBoundsNode&) = default;
  110.  
  111.             // MOVE
  112.             OctreeBoundsNode(OctreeBoundsNode&&) = default;
  113.             OctreeBoundsNode& operator=(OctreeBoundsNode&&) = default;
  114.  
  115.             OctreeData Insert(OctreeData data, bool already_checked_contains = false) {
  116.  
  117.                 if (!already_checked_contains) {
  118.                     if (m_NodeBounds.Contains(data->Bounds, IsRootNode() ? 1.0f : m_Octree->m_Config.Looseness) != BoundsContainResult::Contains) {
  119.                         // The data does not fit in the current node, return it to the caller
  120.                         return data;
  121.                     }
  122.                 }
  123.  
  124.                 // 1. First Condition: Check if the current node has fewer elements than the preferred limit
  125.                 if (m_DataSources.size() < m_Octree->m_Config.PreferredDataSourceLimit) {
  126.                     // Check if the data bounds fit within the current node
  127.  
  128.                     if (already_checked_contains) {
  129.                         m_DataSources.push_back(data);
  130.                         return nullptr;
  131.                     }
  132.                     else {
  133.  
  134.                         if (m_NodeBounds.Contains(data->Bounds, m_Octree->m_Config.Looseness) == BoundsContainResult::Contains) {
  135.                             m_DataSources.push_back(data);
  136.                             return nullptr;
  137.                         }
  138.                         else {
  139.                             // The data does not fit in the current node, return it to the caller
  140.                             return data;
  141.                         }
  142.                     }
  143.  
  144.                 }
  145.  
  146.                 // 2. Second Condition: Check if splitting the node is possible
  147.                 glm::vec3 halfSize = m_NodeBounds.Size() * 0.5f;
  148.                 if (halfSize.x < m_Octree->m_Config.MinNodeSize || halfSize.y < m_Octree->m_Config.MinNodeSize || halfSize.z < m_Octree->m_Config.MinNodeSize) {
  149.                     // If the child node sizes would be smaller than the minimum size,
  150.                     // add the data to the current node regardless of preferred limit
  151.                     m_DataSources.push_back(data);
  152.                     return nullptr; // Return an empty OctreeData
  153.                 }
  154.  
  155.                 // 3. Third Condition Split Node!
  156.                 // If we reach this point, we need to split the current node
  157.                 // Calculate the bounding regions for the children
  158.                 std::array<Bounds_AABB, 8> childBounds = this->CalculateChildBounds();
  159.  
  160.                 std::vector<OctreeData> remainingData;
  161.  
  162.                 // Add current data source to current node data sources so we can easily include this in the sorting
  163.                 m_DataSources.push_back(data);
  164.  
  165.                 // Insert the current data sources into the appropriate child nodes
  166.                 for (auto& existingData : m_DataSources) {
  167.                     bool inserted = false;
  168.                     for (int i = 0; i < 8; ++i) {
  169.  
  170.                         if (childBounds[i].Contains(existingData->Bounds, m_Octree->m_Config.Looseness) == BoundsContainResult::Contains) {
  171.                             if (!m_ChildrenNodes[i]) {
  172.                                 m_ChildrenNodes[i] = std::make_shared<OctreeBoundsNode<DataType>>(childBounds[i], m_Octree);
  173.                             }
  174.                             m_ChildrenNodes[i]->Insert(existingData, true);
  175.                             inserted = true;
  176.                             break;
  177.                         }
  178.                     }
  179.                     if (!inserted) {
  180.                         remainingData.push_back(existingData);
  181.                     }
  182.                 }
  183.  
  184.                 // Clear the current node's data as they've been moved to children
  185.                 m_DataSources.clear();
  186.  
  187.                 for (auto& existingData : remainingData) {
  188.                     m_DataSources.push_back(existingData);
  189.                 }
  190.  
  191.                 // Return nullptr (indicating successful insertion)
  192.                 return nullptr;
  193.             }
  194.  
  195.             bool Remove(const DataType& data) {
  196.                 bool removed = false;
  197.  
  198.                 // Try to remove the data from this node's data sources
  199.                 auto it = std::remove_if(m_DataSources.begin(), m_DataSources.end(), [&data](const OctreeData& data_source) { return data_source && data_source->Data == data; });
  200.  
  201.                 if (it != m_DataSources.end()) {
  202.                     m_DataSources.erase(it, m_DataSources.end());
  203.                     removed = true;
  204.                 }
  205.  
  206.                 // If the data wasn't found in this node and the node is split, try to remove from children
  207.                 if (!removed && IsNodeSplit()) {
  208.                     for (const auto& child : m_ChildrenNodes) {
  209.                         if (child) {
  210.                             removed = child->Remove(data);
  211.                             if (removed)
  212.                                 break;  // Early exit if removed from a child
  213.                         }
  214.                     }
  215.                 }
  216.  
  217.                 // Optional: Check if the node can be merged after removal (if needed)
  218.                 // if (removed && ShouldMerge()) {
  219.                 //     Merge();
  220.                 // }
  221.  
  222.                 return removed;
  223.             }
  224.  
  225.             std::vector<DataType> Query(const Bounds_AABB& query_bounds) {
  226.  
  227.                 std::vector<DataType> result;
  228.                 result.reserve(128);
  229.  
  230.                 BoundsContainResult bounds_result = query_bounds.Contains(m_NodeBounds);
  231.  
  232.                 switch (bounds_result) {
  233.  
  234.                 case BoundsContainResult::Contains:
  235.                 {
  236.                     for (const auto& data : GetChildDataSources())
  237.                         result.push_back(data->Data);
  238.  
  239.                     break;
  240.                 }
  241.  
  242.                 case BoundsContainResult::Intersects:
  243.                 {
  244.                     for (const auto& data : m_DataSources) {
  245.                         if (query_bounds.Contains(data->Bounds) == BoundsContainResult::Intersects)
  246.                             result.push_back(data->Data);
  247.                     }
  248.  
  249.                     for (const auto& child : m_ChildrenNodes) {
  250.                         if (child) {
  251.  
  252.                             auto child_result = child->Query(query_bounds);
  253.                             result.insert(result.end(), child_result.begin(), child_result.end());
  254.                         }
  255.                     }
  256.  
  257.                     break;
  258.                 }
  259.  
  260.                 }
  261.  
  262.                 return result;
  263.             }
  264.  
  265.             std::vector<DataType> Query(const Bounds_Sphere& query_bounds) {
  266.  
  267.                 std::vector<DataType> result;
  268.                 result.reserve(128);
  269.  
  270.                 BoundsContainResult bounds_result = query_bounds.Contains(m_NodeBounds);
  271.  
  272.                 switch (bounds_result) {
  273.  
  274.                 case BoundsContainResult::Contains:
  275.                 {
  276.                     for (const auto& data : GetChildDataSources())
  277.                         result.push_back(data->Data);
  278.  
  279.                     break;
  280.                 }
  281.  
  282.                 case BoundsContainResult::Intersects:
  283.                 {
  284.                     for (const auto& data : m_DataSources) {
  285.                         if (query_bounds.Contains(data->Bounds) == BoundsContainResult::Intersects)
  286.                             result.push_back(data->Data);
  287.                     }
  288.  
  289.                     for (const auto& child : m_ChildrenNodes) {
  290.                         if (child) {
  291.  
  292.                             auto child_result = child->Query(query_bounds);
  293.                             result.insert(result.end(), child_result.begin(), child_result.end());
  294.                         }
  295.                     }
  296.  
  297.                     break;
  298.                 }
  299.  
  300.                 }
  301.  
  302.                 return result;
  303.             }
  304.  
  305.             void Query(const Frustum& frustum, std::vector<OctreeData>& result, bool& should_delete_node) {
  306.  
  307.                 // If there are no data sources in itself or children
  308.                 // why bother testing this node?
  309.                 if (Count() == 0) {
  310.                     should_delete_node = CheckShouldDeleteNode();
  311.                     return;
  312.                 }
  313.  
  314.                 FrustumContainResult frustum_result = frustum.Contains(m_NodeBounds);
  315.  
  316.                 switch (frustum_result) {
  317.  
  318.                     case FrustumContainResult::Contains: {
  319.  
  320.                         L_PROFILE_SCOPE_ACCUMULATIVE("Octree Query - GatherAllDataSources");
  321.                         GatherAllDataSources(result);
  322.  
  323.                         break;
  324.                     }
  325.  
  326.                     case FrustumContainResult::Intersects:
  327.                     {
  328.                         for (const auto& data : m_DataSources) {
  329.                             if (frustum.Contains(data->Bounds) != FrustumContainResult::DoesNotContain)
  330.                                 result.push_back(data);
  331.                         }
  332.  
  333.                         if (!IsNodeSplit())
  334.                             break;
  335.  
  336.                         for (int i = 0; i < m_ChildrenNodes.size(); i++) {
  337.  
  338.                             if (m_ChildrenNodes[i])
  339.                             {
  340.                                 bool should_delete = false;
  341.                                 m_ChildrenNodes[i]->Query(frustum, result, should_delete);
  342.                                 if (should_delete && !m_ChildrenNodes[i]->IsRootNode()) {
  343.                                     m_ChildrenNodes[i]->Clear();
  344.                                     m_ChildrenNodes[i].reset();
  345.                                     m_ChildrenNodes[i] = nullptr;
  346.                                 }
  347.                             }
  348.  
  349.                         }
  350.  
  351.                         break;
  352.                     }
  353.  
  354.                 }
  355.             }
  356.  
  357.             size_t Count() const {
  358.  
  359.                 size_t count = m_DataSources.size();
  360.  
  361.                 for (const auto& child_node : m_ChildrenNodes)
  362.                     if (child_node)
  363.                         count += child_node->Count();
  364.  
  365.                 return count;
  366.             }
  367.  
  368.             void Clear() {
  369.                 // Clear data sources
  370.                 m_DataSources.clear();
  371.  
  372.                 // Recursively clear child nodes
  373.                 for (auto& child_node : m_ChildrenNodes) {
  374.                     if (child_node) {
  375.                         child_node->Clear();
  376.                         child_node.reset();
  377.                         child_node = nullptr;
  378.                     }
  379.                 }
  380.  
  381.                 // Reset split status
  382.                 m_IsNodeSplit = false;
  383.             }
  384.  
  385.             bool IsNodeSplit() const {
  386.  
  387.                 for (const auto& child_node : m_ChildrenNodes)
  388.                     if (child_node)
  389.                         return true;
  390.  
  391.                 return false;
  392.             }
  393.  
  394.             bool IsRootNode() const {
  395.  
  396.                 if (!m_Octree) {
  397.                     L_CORE_ERROR("Octree Node Does Not Contain Valid Reference to Octree.");
  398.                     return false;
  399.                 }
  400.  
  401.                 if (m_Octree->GetRootNode().get() == this)
  402.                     return true;
  403.                 return false;
  404.             }
  405.  
  406.             void GatherAllDataSources(std::vector<OctreeData>& out_data) {
  407.  
  408.                 if (Count() == 0) {
  409.                     CheckShouldDeleteNode();
  410.                     return;
  411.                 }
  412.  
  413.                 // Add this node's data to the output vector
  414.                 if (!m_DataSources.empty()) {
  415.                     out_data.insert(out_data.end(), m_DataSources.begin(), m_DataSources.end());
  416.                 }              
  417.  
  418.                 if (!IsNodeSplit())
  419.                     return;
  420.                
  421.                 // Recursively gather data from child nodes
  422.                 for (const auto& child : m_ChildrenNodes) {
  423.                     if (child) {
  424.                         child->GatherAllDataSources(out_data); // Pass the same vector to avoid copying
  425.                     }
  426.                 }
  427.                
  428.             }
  429.  
  430.             std::vector<OctreeData> GetChildDataSources() {
  431.                 std::vector<OctreeData> data_vector;
  432.                 data_vector.reserve(1024);
  433.                 GatherAllDataSources(data_vector);
  434.                 return data_vector;
  435.             }
  436.  
  437.             std::vector<Bounds_AABB> GetChildBounds() {
  438.                 std::vector<Bounds_AABB> bounds_vector;
  439.                 bounds_vector.push_back(GetNodeBounds());
  440.                
  441.                 // Base case: if no children, return this nodes bounds
  442.                 if (!IsNodeSplit()) {
  443.                     return bounds_vector;
  444.                 }
  445.  
  446.                 // Recursively gather bounds from child nodes
  447.                 for (const auto& child : m_ChildrenNodes) {
  448.                     if (child) {
  449.  
  450.                         // Recursively call this on children
  451.                         if(std::vector<Bounds_AABB> child_bounds_vector = child->GetChildBounds(); !child_bounds_vector.empty()) {
  452.                             bounds_vector.insert(bounds_vector.end(), child_bounds_vector.begin(), child_bounds_vector.end());
  453.                         }
  454.                     }
  455.                 }
  456.  
  457.                 return bounds_vector;
  458.             }
  459.  
  460.             std::vector<glm::mat4> GetChildBoundsMat4() {
  461.                 std::vector<glm::mat4> transforms_vector;
  462.                 transforms_vector.push_back(GetNodeBoundsMat4());
  463.  
  464.                 // Base case: if no children, return this nodes mat4
  465.                 if (!IsNodeSplit()) {
  466.                     return transforms_vector;
  467.                 }
  468.  
  469.                 // Recursively gather transforms from child nodes
  470.                 for (const auto& child : m_ChildrenNodes) {
  471.                     if (child) {
  472.  
  473.                         // Recursively call this on children
  474.                         if (std::vector<glm::mat4> child_transforms_vector = child->GetChildBoundsMat4(); !child_transforms_vector.empty()) {
  475.                             transforms_vector.insert(transforms_vector.end(), child_transforms_vector.begin(), child_transforms_vector.end());
  476.                         }
  477.  
  478.                     }
  479.                 }
  480.  
  481.                 return transforms_vector;
  482.             }
  483.  
  484.             bool CheckShouldDeleteNode() {
  485.  
  486.                 // Set a roof of 64 queries it can be empty for before deletion
  487.                 if(m_LifeCount < 64)
  488.                     m_LifeCount++;
  489.  
  490.                 return (m_LifeCount > m_LifeMax);
  491.             }
  492.  
  493.             const Bounds_AABB& GetNodeBounds() { return m_NodeBounds; }
  494.             const glm::mat4& GetNodeBoundsMat4() { return m_NodeMat4; }
  495.             const std::vector<OctreeData>& GetNodeDataSources() const { return m_DataSources; }
  496.  
  497.         private:
  498.  
  499.             std::array<Bounds_AABB, 8> CalculateChildBounds() const {
  500.                 std::array<Bounds_AABB, 8> childBounds{};
  501.  
  502.                 // Calculate the size of each child node's bounding box (half the size of the current node)
  503.                 glm::vec3 halfSize = m_NodeBounds.Size() * 0.5f;
  504.  
  505.                 // Calculate the center of the current node
  506.                 glm::vec3 center = m_NodeBounds.Center();
  507.  
  508.                 // Generate the eight child bounding boxes
  509.                 for (int i = 0; i < 8; ++i) {
  510.                     glm::vec3 offset(
  511.                         (i & 1 ? 0.5f : -0.5f) * halfSize.x,
  512.                         (i & 2 ? 0.5f : -0.5f) * halfSize.y,
  513.                         (i & 4 ? 0.5f : -0.5f) * halfSize.z
  514.                     );
  515.  
  516.                     glm::vec3 childCenter = center + offset;
  517.  
  518.                     childBounds[i].BoundsMin = childCenter - halfSize * 0.5f;
  519.                     childBounds[i].BoundsMax = childCenter + halfSize * 0.5f;
  520.                 }
  521.  
  522.  
  523.  
  524.  
  525.                 return childBounds;
  526.             }
  527.  
  528.             /// <summary>
  529.             /// Vector of pointers to Data Sources in this node.
  530.             /// </summary>
  531.             std::vector<OctreeData> m_DataSources;
  532.  
  533.             /// <summary>
  534.             /// Array of pointers to 8 (oct) child nodes of this node.
  535.             /// </summary>
  536.             std::array<OctreeNode, 8> m_ChildrenNodes{ nullptr };
  537.  
  538.             /// <summary>
  539.             /// This tells us if this node has been split into child nodes or not.
  540.             /// </summary>
  541.             bool m_IsNodeSplit = false;
  542.  
  543.             /// <summary>
  544.             /// The actual bounds of the node unaffected by the looseness.
  545.             ///
  546.             /// Please note, this is not varied when looseness changes. This
  547.             /// will always be the actual bounds of the current node, and the
  548.             /// looseness will be taken into account when attempting to insert
  549.             /// a Data Source.
  550.             /// </summary>
  551.             Bounds_AABB m_NodeBounds;
  552.  
  553.             /// <summary>
  554.             /// This is the mat4 in worldspace of this current node. This is
  555.             /// useful for when we want to draw the debug version of the octree
  556.             /// and to save on performance, we calculate this once when the
  557.             /// node is created, opposed to every frame we calculate all mat4's
  558.             /// for the octree and its child nodes.
  559.             /// </summary>
  560.             glm::mat4 m_NodeMat4;
  561.  
  562.             /// <summary>
  563.             /// Pointer to the Octree holding class that contains the root node.
  564.             /// </summary>
  565.             OctreeBounds<DataType>* m_Octree;
  566.  
  567.             /// <summary>
  568.             /// This is the max life that a node is able to have.
  569.             /// </summary>
  570.             uint8_t m_LifeMax;
  571.  
  572.             /// <summary>
  573.             /// This is the death counter, it will start at 0 and
  574.             /// work its way up to Life Max. If it reaches LifeMax
  575.             /// it will delete the current node and all its children.
  576.             /// If the node is saved and something is placed in it,
  577.             /// then the LifeMax of the node is doubled.
  578.             /// </summary>
  579.             uint8_t m_LifeCount = 0;
  580.  
  581.         };
  582.  
  583.         using OctreeNode = std::shared_ptr<OctreeBoundsNode<DataType>>;
  584.  
  585.     public:
  586.  
  587.         // Constructor and Destructor
  588.         OctreeBounds() {
  589.             BuildOctree();
  590.         }
  591.         OctreeBounds(const OctreeBoundsConfig& config) : m_Config(config) {
  592.             BuildOctree();
  593.         };
  594.         OctreeBounds(const OctreeBoundsConfig& config, std::vector<OctreeData> data_sources) : m_Config(config) {
  595.             BuildOctree(data_sources);
  596.         };
  597.  
  598.         ~OctreeBounds() {
  599.             Clear();
  600.             m_RootNode.reset();
  601.             m_RootNode = nullptr;
  602.         };
  603.        
  604.         // COPY
  605.         OctreeBounds(const OctreeBounds& other) = default;
  606.         OctreeBounds& operator=(const OctreeBounds& other) = default;
  607.  
  608.         // MOVE
  609.         OctreeBounds(OctreeBounds&& other) = default;
  610.         OctreeBounds& operator=(OctreeBounds&& other) = default;
  611.  
  612.         /// <summary>
  613.         /// This will insert the Data Source into the Octree. If it already exists,
  614.         /// it will check call UpdateDataSource instead.
  615.         /// </summary>
  616.         /// <param name="data">This is the key of the data source - typically a UUID or Hashed UUID String e.g., "MeshFilterComponent::012050125", or "PointLightComponent::012050125".</param>
  617.         /// <param name="bounds">This is the Bounds AABB of the data source.</param>
  618.         bool Insert(const DataType& data, const Bounds_AABB& bounds) {
  619.             if (m_RootNode) {
  620.  
  621.                 auto data_source = std::make_shared<OctreeBounds<DataType>::OctreeDataSource<DataType>>(data, bounds);
  622.  
  623.                 // If insert returns anything but a nullptr, then the
  624.                 // data_source did not fit in the current octree!
  625.  
  626.                 int growth_attempts = 0;
  627.                 while (m_RootNode->Insert(data_source)) {
  628.  
  629.                     if (growth_attempts >= 20)
  630.                         return false;
  631.  
  632.                     growth_attempts++;
  633.                     GrowOctree(); // TODO
  634.                     //L_CORE_WARN("Cannot Be Inserted Into Current Octree - Need to Implement Growth!");
  635.                 }
  636.                 return true;
  637.             }
  638.             return false;
  639.         }
  640.  
  641.         /// <summary>
  642.         /// This will insert the Data Source into the Octree. If it already exists,
  643.         /// it will check call UpdateDataSource instead.
  644.         /// </summary>
  645.         /// <param name="data">This is the key of the data source - typically a UUID or Hashed UUID String e.g., "MeshFilterComponent::012050125", or "PointLightComponent::012050125".</param>
  646.         /// <param name="bounds">This is the Bounds AABB of the data source.</param>
  647.         void InsertVector(const std::vector<OctreeData>& data_sources) {
  648.  
  649.             // These are data sources that are out of bounds
  650.             std::vector<OctreeData> remaining_data_sources;
  651.  
  652.             for (const auto& data : data_sources) {
  653.  
  654.                 auto remaining_data = m_RootNode->Insert(data);
  655.  
  656.                 if (remaining_data)
  657.                     remaining_data_sources.push_back(remaining_data);
  658.  
  659.             }
  660.  
  661.             // Now we want to grow the tree to place out of bound data sources into the tree
  662.             for (const auto& data : remaining_data_sources) {
  663.  
  664.                 L_CORE_INFO("Try Growing the Octree!");
  665.  
  666.             }
  667.  
  668.         }
  669.  
  670.         /// <summary>
  671.         /// This will reinsert a pre-existing Data Source into the Octree. It will
  672.         /// remove it from it's current node, then recursively find the most suitable
  673.         /// node of the Octree to hold this Data Source.
  674.         /// </summary>
  675.         /// <param name="data">This is the key of the data source - typically a UUID or Hashed UUID String e.g., "MeshFilterComponent::012050125", or "PointLightComponent::012050125".</param>
  676.         /// <param name="bounds">This is the Bounds AABB of the data source.</param>
  677.         bool Update(const DataType& data, const Bounds_AABB& bounds) {
  678.            
  679.             if (m_RootNode) {
  680.  
  681.                 Remove(data);
  682.  
  683.                 return Insert(data, bounds);
  684.             }
  685.  
  686.             return false;
  687.         }
  688.  
  689.         /// <summary>
  690.         /// This will remove a pre-existing Data Source from the Octree.
  691.         /// </summary>
  692.         /// <param name="data">This is the key of the data source - typically a UUID or Hashed UUID String e.g., "MeshFilterComponent::012050125", or "PointLightComponent::012050125".</param>
  693.         /// <param name="bounds">This is the Bounds AABB of the data source.</param>
  694.         void Remove(const DataType& data) {
  695.             if (m_RootNode && !m_RootNode->Remove(data))
  696.                 L_CORE_WARN("Data Not Contained Within Octree.");
  697.         }
  698.  
  699.         /// <summary>
  700.         /// This will query the Octree for all Data Sources within the given bounds.
  701.         /// </summary>
  702.         /// <param name="bounds">The Bounds AABB to query within.</param>
  703.         /// <returns>A vector of DataType objects that are within the bounds.</returns>
  704.         std::vector<DataType> Query(const Bounds_AABB& bounds) const {
  705.  
  706.  
  707.         }
  708.  
  709.         /// <summary>
  710.         /// This will query the Octree for all Data Sources within the given bounds.
  711.         /// </summary>
  712.         /// <param name="bounds">The Bounds Sphere to query within.</param>
  713.         /// <returns>A vector of DataType objects that are within the bounds.</returns>
  714.         std::vector<DataType> Query(const Bounds_Sphere& bounds) const {
  715.  
  716.  
  717.         }
  718.  
  719.         /// <summary>
  720.         /// This will query the Octree for all Data Sources within the given frustum.
  721.         /// </summary>
  722.         /// <param name="frustum">The frustum to query within. E.g., a Camera Frustum.</param>
  723.         /// <returns>A vector of DataType objects that are within the bounds.</returns>
  724.         const std::vector<OctreeData>& Query(const Frustum& frustum) const {
  725.  
  726.             if (m_RootNode) {
  727.  
  728.                 static std::vector<OctreeData> s_query_data_vector{};
  729.  
  730.                 if (s_query_data_vector.capacity() == 0)
  731.                     s_query_data_vector.reserve(2048);
  732.  
  733.                 s_query_data_vector.clear();
  734.                
  735.                 static bool should_delete = false;
  736.                 m_RootNode->Query(frustum, s_query_data_vector, should_delete);
  737.                 return s_query_data_vector;
  738.             }
  739.             static std::vector<OctreeData> null_return{};
  740.             return null_return;
  741.         }
  742.  
  743.         /// <summary>
  744.         /// This will build the entire Octree.
  745.         /// </summary>
  746.         void BuildOctree();
  747.  
  748.         /// <summary>
  749.         /// This will build the entire Octree.
  750.         /// </summary>
  751.         void BuildOctree(std::vector<OctreeData> data_sources);
  752.  
  753.         size_t Count() const {
  754.             return m_RootNode ? m_RootNode->Count() : 0;
  755.         }
  756.  
  757.         void Clear() {
  758.             if (m_RootNode)
  759.                 m_RootNode->Clear();
  760.         }
  761.  
  762.         bool IsEmpty() const {
  763.             return Count() == 0;
  764.         }
  765.  
  766.         OctreeNode GetRootNode() const {
  767.             return m_RootNode;
  768.         }
  769.  
  770.         void SetConfig(const OctreeBoundsConfig& config) { m_Config = config; }
  771.         const OctreeBoundsConfig& GetConfig() const { return m_Config; }
  772.  
  773.         std::vector<OctreeData> GetAllOctreeDataSources() {
  774.             return m_RootNode ? m_RootNode->GetChildDataSources() : std::vector<OctreeData>();
  775.         }
  776.  
  777.         std::vector<Bounds_AABB> GetAllOctreeBounds() const {
  778.             return m_RootNode ? m_RootNode->GetChildBounds() : std::vector<Bounds_AABB>();
  779.         }
  780.  
  781.         std::vector<glm::mat4> GetAllOctreeBoundsMat4() const {
  782.             return m_RootNode ? m_RootNode->GetChildBoundsMat4() : std::vector<glm::mat4>();
  783.         }
  784.  
  785.     private:
  786.  
  787.         void GrowOctree();
  788.         void ShrinkOctree();
  789.  
  790.         OctreeBoundsConfig m_Config{};
  791.  
  792.         OctreeNode m_RootNode;
  793.  
  794.     };
  795.  
  796.     template<typename DataType>
  797.     inline void OctreeBounds<DataType>::BuildOctree() {
  798.         BuildOctree(std::vector<OctreeData>());
  799.     }
  800.  
  801.     template<typename DataType>
  802.     inline void OctreeBounds<DataType>::BuildOctree(std::vector<OctreeData> data_sources) {
  803.  
  804.         // 1. Delete old Octree
  805.         if (m_RootNode) {
  806.  
  807.             if (data_sources.empty())
  808.                 data_sources = GetAllOctreeDataSources();
  809.  
  810.             m_RootNode.reset();
  811.  
  812.         }
  813.  
  814.         // 2. Calculate Initial Bounds of New Octree
  815.         if (!data_sources.empty()) {
  816.  
  817.             m_Config.InitialBounds.BoundsMin = glm::vec3(FLT_MAX);
  818.             m_Config.InitialBounds.BoundsMax = glm::vec3(-FLT_MAX);
  819.  
  820.             for (const auto& data : data_sources) {
  821.                 if (data) {
  822.                     m_Config.InitialBounds.BoundsMin = glm::min(data->Bounds.BoundsMin, m_Config.InitialBounds.BoundsMin);
  823.                     m_Config.InitialBounds.BoundsMax = glm::max(data->Bounds.BoundsMax, m_Config.InitialBounds.BoundsMax);
  824.                 }
  825.             }
  826.  
  827.             // Step 1: Calculate the initial center and extent
  828.             glm::vec3 center = m_Config.InitialBounds.Center();
  829.             glm::vec3 extent = m_Config.InitialBounds.BoundsMax - m_Config.InitialBounds.BoundsMin;
  830.  
  831.             // Step 2: Find the largest extent to ensure the bounding box is a cube
  832.             float maxExtent = glm::compMax(extent);
  833.  
  834.             // Step 3: Create a cubic bounding box centered on the original center
  835.             glm::vec3 halfExtent = glm::vec3(maxExtent) * 0.5f;
  836.  
  837.             // Step 4: Adjust the bounds to make them uniform and cubic
  838.             m_Config.InitialBounds.BoundsMin = center - halfExtent;
  839.             m_Config.InitialBounds.BoundsMax = center + halfExtent;
  840.  
  841.             // Optional: Apply a scaling factor to add some padding around the entire scene
  842.             float scaleFactor = 1.1f;
  843.             m_Config.InitialBounds.BoundsMin *= scaleFactor;
  844.             m_Config.InitialBounds.BoundsMax *= scaleFactor;
  845.  
  846.         }
  847.         else {
  848.             m_Config.InitialBounds.BoundsMin = glm::vec3(50.0f);
  849.             m_Config.InitialBounds.BoundsMax = glm::vec3(-50.0f);
  850.         }
  851.  
  852.         // 3. Create Root Octree Node
  853.         m_RootNode = std::make_shared<OctreeBoundsNode<DataType>>(m_Config.InitialBounds, this);
  854.  
  855.         // 4. Insert All Data Sources
  856.         if (!data_sources.empty())
  857.             InsertVector(data_sources);
  858.     }
  859.  
  860.     template<typename DataType>
  861.     inline void OctreeBounds<DataType>::GrowOctree()
  862.     {
  863.     }
  864.  
  865.     template<typename DataType>
  866.     inline void OctreeBounds<DataType>::ShrinkOctree()
  867.     {
  868.     }
  869. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement