Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- namespace Louron {
- struct OctreeBoundsConfig {
- /// <summary>
- /// This is the minimum node size we can have. If the node attemps to split
- /// this configuration will prevent the Octree from splitting into smaller
- /// child nodes. This is set at 1.0f == 1 unit in worldspace.
- ///
- /// If this is increased to 2.0f, a node cannot split if it's child nodes
- /// will be smaller than 2 units in worldspace.
- /// </summary>
- float MinNodeSize = 1.0f;
- /// <summary>
- /// This is the preferred limit of Data Sources per node before the node
- /// splits into child nodes and attempts to reinsert these Data Sources
- /// into its child nodes.
- ///
- /// If we reach the maximum depth, or the minimum node size, we cannot
- /// split any further, which is why this is a preferred limit for
- /// Data Sources per node.
- /// </summary>
- int PreferredDataSourceLimit = 4;
- /// <summary>
- /// Looseness of Octree
- ///
- /// Min Value: 1.0f
- /// Max Value: 2.0f
- /// (any other values will be clamped between min and max)
- ///
- /// Standard Octree 1.0f: child boundaries are exactly 100% / 2.0 = 50%
- /// of the parent node boundaries.
- ///
- /// As you increase this to two, this increases how far the octree
- /// overlaps into neighbouring child nodes to reduce edge cases.
- ///
- /// Loose Octree 1.5f: child boundaries are exactly 150% / 2.0 = 75%
- /// of the parent node boundaries.
- ///
- /// </summary>
- float Looseness = 1.0f;
- /// <summary>
- /// Initial Bounds of the Octree Root Node.
- /// </summary>
- Bounds_AABB InitialBounds{};
- /// <summary>
- /// When a Node is Queried, and itself and it's children
- /// have no data sources, a life count will be incremented
- /// until 8 is reached. This means that if you remove a
- /// data source, it will start the counter. If you query
- /// and the node is empty, we will increment again. If the
- /// life of the node exceeds the MaxLifeIfEmpty, the node
- /// and its children will be deleted. Each Node starts off
- /// with this amount, and doubles each time if it is
- /// saved by something being added up to a max of 64 queries.
- /// </summary>
- uint8_t MaxLifeIfEmpty = 8;
- };
- template <typename DataType>
- class OctreeBounds {
- public:
- template <typename DataType>
- struct OctreeDataSource {
- DataType Data;
- Bounds_AABB Bounds;
- OctreeDataSource() = delete;
- OctreeDataSource(DataType data, Bounds_AABB bounds) : Data(data), Bounds(bounds) { }
- };
- using OctreeData = std::shared_ptr<OctreeDataSource<DataType>>;
- private:
- template <typename DataType>
- class OctreeBoundsNode {
- private:
- using OctreeNode = std::shared_ptr<OctreeBoundsNode<DataType>>;
- public:
- // Constructor and Destructor
- OctreeBoundsNode() = default;
- OctreeBoundsNode(Bounds_AABB node_bounds, OctreeBounds<DataType>* octree) : m_NodeBounds(node_bounds), m_Octree(octree){
- m_NodeMat4 = m_NodeBounds.GetGlobalBoundsMat4();
- if (m_Octree)
- m_LifeMax = m_Octree->m_Config.MaxLifeIfEmpty;
- else
- m_LifeMax = 8;
- }
- ~OctreeBoundsNode() {
- Clear();
- }
- // COPY
- OctreeBoundsNode(const OctreeBoundsNode&) = default;
- OctreeBoundsNode& operator=(const OctreeBoundsNode&) = default;
- // MOVE
- OctreeBoundsNode(OctreeBoundsNode&&) = default;
- OctreeBoundsNode& operator=(OctreeBoundsNode&&) = default;
- OctreeData Insert(OctreeData data, bool already_checked_contains = false) {
- if (!already_checked_contains) {
- if (m_NodeBounds.Contains(data->Bounds, IsRootNode() ? 1.0f : m_Octree->m_Config.Looseness) != BoundsContainResult::Contains) {
- // The data does not fit in the current node, return it to the caller
- return data;
- }
- }
- // 1. First Condition: Check if the current node has fewer elements than the preferred limit
- if (m_DataSources.size() < m_Octree->m_Config.PreferredDataSourceLimit) {
- // Check if the data bounds fit within the current node
- if (already_checked_contains) {
- m_DataSources.push_back(data);
- return nullptr;
- }
- else {
- if (m_NodeBounds.Contains(data->Bounds, m_Octree->m_Config.Looseness) == BoundsContainResult::Contains) {
- m_DataSources.push_back(data);
- return nullptr;
- }
- else {
- // The data does not fit in the current node, return it to the caller
- return data;
- }
- }
- }
- // 2. Second Condition: Check if splitting the node is possible
- glm::vec3 halfSize = m_NodeBounds.Size() * 0.5f;
- if (halfSize.x < m_Octree->m_Config.MinNodeSize || halfSize.y < m_Octree->m_Config.MinNodeSize || halfSize.z < m_Octree->m_Config.MinNodeSize) {
- // If the child node sizes would be smaller than the minimum size,
- // add the data to the current node regardless of preferred limit
- m_DataSources.push_back(data);
- return nullptr; // Return an empty OctreeData
- }
- // 3. Third Condition Split Node!
- // If we reach this point, we need to split the current node
- // Calculate the bounding regions for the children
- std::array<Bounds_AABB, 8> childBounds = this->CalculateChildBounds();
- std::vector<OctreeData> remainingData;
- // Add current data source to current node data sources so we can easily include this in the sorting
- m_DataSources.push_back(data);
- // Insert the current data sources into the appropriate child nodes
- for (auto& existingData : m_DataSources) {
- bool inserted = false;
- for (int i = 0; i < 8; ++i) {
- if (childBounds[i].Contains(existingData->Bounds, m_Octree->m_Config.Looseness) == BoundsContainResult::Contains) {
- if (!m_ChildrenNodes[i]) {
- m_ChildrenNodes[i] = std::make_shared<OctreeBoundsNode<DataType>>(childBounds[i], m_Octree);
- }
- m_ChildrenNodes[i]->Insert(existingData, true);
- inserted = true;
- break;
- }
- }
- if (!inserted) {
- remainingData.push_back(existingData);
- }
- }
- // Clear the current node's data as they've been moved to children
- m_DataSources.clear();
- for (auto& existingData : remainingData) {
- m_DataSources.push_back(existingData);
- }
- // Return nullptr (indicating successful insertion)
- return nullptr;
- }
- bool Remove(const DataType& data) {
- bool removed = false;
- // Try to remove the data from this node's data sources
- auto it = std::remove_if(m_DataSources.begin(), m_DataSources.end(), [&data](const OctreeData& data_source) { return data_source && data_source->Data == data; });
- if (it != m_DataSources.end()) {
- m_DataSources.erase(it, m_DataSources.end());
- removed = true;
- }
- // If the data wasn't found in this node and the node is split, try to remove from children
- if (!removed && IsNodeSplit()) {
- for (const auto& child : m_ChildrenNodes) {
- if (child) {
- removed = child->Remove(data);
- if (removed)
- break; // Early exit if removed from a child
- }
- }
- }
- // Optional: Check if the node can be merged after removal (if needed)
- // if (removed && ShouldMerge()) {
- // Merge();
- // }
- return removed;
- }
- std::vector<DataType> Query(const Bounds_AABB& query_bounds) {
- std::vector<DataType> result;
- result.reserve(128);
- BoundsContainResult bounds_result = query_bounds.Contains(m_NodeBounds);
- switch (bounds_result) {
- case BoundsContainResult::Contains:
- {
- for (const auto& data : GetChildDataSources())
- result.push_back(data->Data);
- break;
- }
- case BoundsContainResult::Intersects:
- {
- for (const auto& data : m_DataSources) {
- if (query_bounds.Contains(data->Bounds) == BoundsContainResult::Intersects)
- result.push_back(data->Data);
- }
- for (const auto& child : m_ChildrenNodes) {
- if (child) {
- auto child_result = child->Query(query_bounds);
- result.insert(result.end(), child_result.begin(), child_result.end());
- }
- }
- break;
- }
- }
- return result;
- }
- std::vector<DataType> Query(const Bounds_Sphere& query_bounds) {
- std::vector<DataType> result;
- result.reserve(128);
- BoundsContainResult bounds_result = query_bounds.Contains(m_NodeBounds);
- switch (bounds_result) {
- case BoundsContainResult::Contains:
- {
- for (const auto& data : GetChildDataSources())
- result.push_back(data->Data);
- break;
- }
- case BoundsContainResult::Intersects:
- {
- for (const auto& data : m_DataSources) {
- if (query_bounds.Contains(data->Bounds) == BoundsContainResult::Intersects)
- result.push_back(data->Data);
- }
- for (const auto& child : m_ChildrenNodes) {
- if (child) {
- auto child_result = child->Query(query_bounds);
- result.insert(result.end(), child_result.begin(), child_result.end());
- }
- }
- break;
- }
- }
- return result;
- }
- void Query(const Frustum& frustum, std::vector<OctreeData>& result, bool& should_delete_node) {
- // If there are no data sources in itself or children
- // why bother testing this node?
- if (Count() == 0) {
- should_delete_node = CheckShouldDeleteNode();
- return;
- }
- FrustumContainResult frustum_result = frustum.Contains(m_NodeBounds);
- switch (frustum_result) {
- case FrustumContainResult::Contains: {
- L_PROFILE_SCOPE_ACCUMULATIVE("Octree Query - GatherAllDataSources");
- GatherAllDataSources(result);
- break;
- }
- case FrustumContainResult::Intersects:
- {
- for (const auto& data : m_DataSources) {
- if (frustum.Contains(data->Bounds) != FrustumContainResult::DoesNotContain)
- result.push_back(data);
- }
- if (!IsNodeSplit())
- break;
- for (int i = 0; i < m_ChildrenNodes.size(); i++) {
- if (m_ChildrenNodes[i])
- {
- bool should_delete = false;
- m_ChildrenNodes[i]->Query(frustum, result, should_delete);
- if (should_delete && !m_ChildrenNodes[i]->IsRootNode()) {
- m_ChildrenNodes[i]->Clear();
- m_ChildrenNodes[i].reset();
- m_ChildrenNodes[i] = nullptr;
- }
- }
- }
- break;
- }
- }
- }
- size_t Count() const {
- size_t count = m_DataSources.size();
- for (const auto& child_node : m_ChildrenNodes)
- if (child_node)
- count += child_node->Count();
- return count;
- }
- void Clear() {
- // Clear data sources
- m_DataSources.clear();
- // Recursively clear child nodes
- for (auto& child_node : m_ChildrenNodes) {
- if (child_node) {
- child_node->Clear();
- child_node.reset();
- child_node = nullptr;
- }
- }
- // Reset split status
- m_IsNodeSplit = false;
- }
- bool IsNodeSplit() const {
- for (const auto& child_node : m_ChildrenNodes)
- if (child_node)
- return true;
- return false;
- }
- bool IsRootNode() const {
- if (!m_Octree) {
- L_CORE_ERROR("Octree Node Does Not Contain Valid Reference to Octree.");
- return false;
- }
- if (m_Octree->GetRootNode().get() == this)
- return true;
- return false;
- }
- void GatherAllDataSources(std::vector<OctreeData>& out_data) {
- if (Count() == 0) {
- CheckShouldDeleteNode();
- return;
- }
- // Add this node's data to the output vector
- if (!m_DataSources.empty()) {
- out_data.insert(out_data.end(), m_DataSources.begin(), m_DataSources.end());
- }
- if (!IsNodeSplit())
- return;
- // Recursively gather data from child nodes
- for (const auto& child : m_ChildrenNodes) {
- if (child) {
- child->GatherAllDataSources(out_data); // Pass the same vector to avoid copying
- }
- }
- }
- std::vector<OctreeData> GetChildDataSources() {
- std::vector<OctreeData> data_vector;
- data_vector.reserve(1024);
- GatherAllDataSources(data_vector);
- return data_vector;
- }
- std::vector<Bounds_AABB> GetChildBounds() {
- std::vector<Bounds_AABB> bounds_vector;
- bounds_vector.push_back(GetNodeBounds());
- // Base case: if no children, return this nodes bounds
- if (!IsNodeSplit()) {
- return bounds_vector;
- }
- // Recursively gather bounds from child nodes
- for (const auto& child : m_ChildrenNodes) {
- if (child) {
- // Recursively call this on children
- if(std::vector<Bounds_AABB> child_bounds_vector = child->GetChildBounds(); !child_bounds_vector.empty()) {
- bounds_vector.insert(bounds_vector.end(), child_bounds_vector.begin(), child_bounds_vector.end());
- }
- }
- }
- return bounds_vector;
- }
- std::vector<glm::mat4> GetChildBoundsMat4() {
- std::vector<glm::mat4> transforms_vector;
- transforms_vector.push_back(GetNodeBoundsMat4());
- // Base case: if no children, return this nodes mat4
- if (!IsNodeSplit()) {
- return transforms_vector;
- }
- // Recursively gather transforms from child nodes
- for (const auto& child : m_ChildrenNodes) {
- if (child) {
- // Recursively call this on children
- if (std::vector<glm::mat4> child_transforms_vector = child->GetChildBoundsMat4(); !child_transforms_vector.empty()) {
- transforms_vector.insert(transforms_vector.end(), child_transforms_vector.begin(), child_transforms_vector.end());
- }
- }
- }
- return transforms_vector;
- }
- bool CheckShouldDeleteNode() {
- // Set a roof of 64 queries it can be empty for before deletion
- if(m_LifeCount < 64)
- m_LifeCount++;
- return (m_LifeCount > m_LifeMax);
- }
- const Bounds_AABB& GetNodeBounds() { return m_NodeBounds; }
- const glm::mat4& GetNodeBoundsMat4() { return m_NodeMat4; }
- const std::vector<OctreeData>& GetNodeDataSources() const { return m_DataSources; }
- private:
- std::array<Bounds_AABB, 8> CalculateChildBounds() const {
- std::array<Bounds_AABB, 8> childBounds{};
- // Calculate the size of each child node's bounding box (half the size of the current node)
- glm::vec3 halfSize = m_NodeBounds.Size() * 0.5f;
- // Calculate the center of the current node
- glm::vec3 center = m_NodeBounds.Center();
- // Generate the eight child bounding boxes
- for (int i = 0; i < 8; ++i) {
- glm::vec3 offset(
- (i & 1 ? 0.5f : -0.5f) * halfSize.x,
- (i & 2 ? 0.5f : -0.5f) * halfSize.y,
- (i & 4 ? 0.5f : -0.5f) * halfSize.z
- );
- glm::vec3 childCenter = center + offset;
- childBounds[i].BoundsMin = childCenter - halfSize * 0.5f;
- childBounds[i].BoundsMax = childCenter + halfSize * 0.5f;
- }
- return childBounds;
- }
- /// <summary>
- /// Vector of pointers to Data Sources in this node.
- /// </summary>
- std::vector<OctreeData> m_DataSources;
- /// <summary>
- /// Array of pointers to 8 (oct) child nodes of this node.
- /// </summary>
- std::array<OctreeNode, 8> m_ChildrenNodes{ nullptr };
- /// <summary>
- /// This tells us if this node has been split into child nodes or not.
- /// </summary>
- bool m_IsNodeSplit = false;
- /// <summary>
- /// The actual bounds of the node unaffected by the looseness.
- ///
- /// Please note, this is not varied when looseness changes. This
- /// will always be the actual bounds of the current node, and the
- /// looseness will be taken into account when attempting to insert
- /// a Data Source.
- /// </summary>
- Bounds_AABB m_NodeBounds;
- /// <summary>
- /// This is the mat4 in worldspace of this current node. This is
- /// useful for when we want to draw the debug version of the octree
- /// and to save on performance, we calculate this once when the
- /// node is created, opposed to every frame we calculate all mat4's
- /// for the octree and its child nodes.
- /// </summary>
- glm::mat4 m_NodeMat4;
- /// <summary>
- /// Pointer to the Octree holding class that contains the root node.
- /// </summary>
- OctreeBounds<DataType>* m_Octree;
- /// <summary>
- /// This is the max life that a node is able to have.
- /// </summary>
- uint8_t m_LifeMax;
- /// <summary>
- /// This is the death counter, it will start at 0 and
- /// work its way up to Life Max. If it reaches LifeMax
- /// it will delete the current node and all its children.
- /// If the node is saved and something is placed in it,
- /// then the LifeMax of the node is doubled.
- /// </summary>
- uint8_t m_LifeCount = 0;
- };
- using OctreeNode = std::shared_ptr<OctreeBoundsNode<DataType>>;
- public:
- // Constructor and Destructor
- OctreeBounds() {
- BuildOctree();
- }
- OctreeBounds(const OctreeBoundsConfig& config) : m_Config(config) {
- BuildOctree();
- };
- OctreeBounds(const OctreeBoundsConfig& config, std::vector<OctreeData> data_sources) : m_Config(config) {
- BuildOctree(data_sources);
- };
- ~OctreeBounds() {
- Clear();
- m_RootNode.reset();
- m_RootNode = nullptr;
- };
- // COPY
- OctreeBounds(const OctreeBounds& other) = default;
- OctreeBounds& operator=(const OctreeBounds& other) = default;
- // MOVE
- OctreeBounds(OctreeBounds&& other) = default;
- OctreeBounds& operator=(OctreeBounds&& other) = default;
- /// <summary>
- /// This will insert the Data Source into the Octree. If it already exists,
- /// it will check call UpdateDataSource instead.
- /// </summary>
- /// <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>
- /// <param name="bounds">This is the Bounds AABB of the data source.</param>
- bool Insert(const DataType& data, const Bounds_AABB& bounds) {
- if (m_RootNode) {
- auto data_source = std::make_shared<OctreeBounds<DataType>::OctreeDataSource<DataType>>(data, bounds);
- // If insert returns anything but a nullptr, then the
- // data_source did not fit in the current octree!
- int growth_attempts = 0;
- while (m_RootNode->Insert(data_source)) {
- if (growth_attempts >= 20)
- return false;
- growth_attempts++;
- GrowOctree(); // TODO
- //L_CORE_WARN("Cannot Be Inserted Into Current Octree - Need to Implement Growth!");
- }
- return true;
- }
- return false;
- }
- /// <summary>
- /// This will insert the Data Source into the Octree. If it already exists,
- /// it will check call UpdateDataSource instead.
- /// </summary>
- /// <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>
- /// <param name="bounds">This is the Bounds AABB of the data source.</param>
- void InsertVector(const std::vector<OctreeData>& data_sources) {
- // These are data sources that are out of bounds
- std::vector<OctreeData> remaining_data_sources;
- for (const auto& data : data_sources) {
- auto remaining_data = m_RootNode->Insert(data);
- if (remaining_data)
- remaining_data_sources.push_back(remaining_data);
- }
- // Now we want to grow the tree to place out of bound data sources into the tree
- for (const auto& data : remaining_data_sources) {
- L_CORE_INFO("Try Growing the Octree!");
- }
- }
- /// <summary>
- /// This will reinsert a pre-existing Data Source into the Octree. It will
- /// remove it from it's current node, then recursively find the most suitable
- /// node of the Octree to hold this Data Source.
- /// </summary>
- /// <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>
- /// <param name="bounds">This is the Bounds AABB of the data source.</param>
- bool Update(const DataType& data, const Bounds_AABB& bounds) {
- if (m_RootNode) {
- Remove(data);
- return Insert(data, bounds);
- }
- return false;
- }
- /// <summary>
- /// This will remove a pre-existing Data Source from the Octree.
- /// </summary>
- /// <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>
- /// <param name="bounds">This is the Bounds AABB of the data source.</param>
- void Remove(const DataType& data) {
- if (m_RootNode && !m_RootNode->Remove(data))
- L_CORE_WARN("Data Not Contained Within Octree.");
- }
- /// <summary>
- /// This will query the Octree for all Data Sources within the given bounds.
- /// </summary>
- /// <param name="bounds">The Bounds AABB to query within.</param>
- /// <returns>A vector of DataType objects that are within the bounds.</returns>
- std::vector<DataType> Query(const Bounds_AABB& bounds) const {
- }
- /// <summary>
- /// This will query the Octree for all Data Sources within the given bounds.
- /// </summary>
- /// <param name="bounds">The Bounds Sphere to query within.</param>
- /// <returns>A vector of DataType objects that are within the bounds.</returns>
- std::vector<DataType> Query(const Bounds_Sphere& bounds) const {
- }
- /// <summary>
- /// This will query the Octree for all Data Sources within the given frustum.
- /// </summary>
- /// <param name="frustum">The frustum to query within. E.g., a Camera Frustum.</param>
- /// <returns>A vector of DataType objects that are within the bounds.</returns>
- const std::vector<OctreeData>& Query(const Frustum& frustum) const {
- if (m_RootNode) {
- static std::vector<OctreeData> s_query_data_vector{};
- if (s_query_data_vector.capacity() == 0)
- s_query_data_vector.reserve(2048);
- s_query_data_vector.clear();
- static bool should_delete = false;
- m_RootNode->Query(frustum, s_query_data_vector, should_delete);
- return s_query_data_vector;
- }
- static std::vector<OctreeData> null_return{};
- return null_return;
- }
- /// <summary>
- /// This will build the entire Octree.
- /// </summary>
- void BuildOctree();
- /// <summary>
- /// This will build the entire Octree.
- /// </summary>
- void BuildOctree(std::vector<OctreeData> data_sources);
- size_t Count() const {
- return m_RootNode ? m_RootNode->Count() : 0;
- }
- void Clear() {
- if (m_RootNode)
- m_RootNode->Clear();
- }
- bool IsEmpty() const {
- return Count() == 0;
- }
- OctreeNode GetRootNode() const {
- return m_RootNode;
- }
- void SetConfig(const OctreeBoundsConfig& config) { m_Config = config; }
- const OctreeBoundsConfig& GetConfig() const { return m_Config; }
- std::vector<OctreeData> GetAllOctreeDataSources() {
- return m_RootNode ? m_RootNode->GetChildDataSources() : std::vector<OctreeData>();
- }
- std::vector<Bounds_AABB> GetAllOctreeBounds() const {
- return m_RootNode ? m_RootNode->GetChildBounds() : std::vector<Bounds_AABB>();
- }
- std::vector<glm::mat4> GetAllOctreeBoundsMat4() const {
- return m_RootNode ? m_RootNode->GetChildBoundsMat4() : std::vector<glm::mat4>();
- }
- private:
- void GrowOctree();
- void ShrinkOctree();
- OctreeBoundsConfig m_Config{};
- OctreeNode m_RootNode;
- };
- template<typename DataType>
- inline void OctreeBounds<DataType>::BuildOctree() {
- BuildOctree(std::vector<OctreeData>());
- }
- template<typename DataType>
- inline void OctreeBounds<DataType>::BuildOctree(std::vector<OctreeData> data_sources) {
- // 1. Delete old Octree
- if (m_RootNode) {
- if (data_sources.empty())
- data_sources = GetAllOctreeDataSources();
- m_RootNode.reset();
- }
- // 2. Calculate Initial Bounds of New Octree
- if (!data_sources.empty()) {
- m_Config.InitialBounds.BoundsMin = glm::vec3(FLT_MAX);
- m_Config.InitialBounds.BoundsMax = glm::vec3(-FLT_MAX);
- for (const auto& data : data_sources) {
- if (data) {
- m_Config.InitialBounds.BoundsMin = glm::min(data->Bounds.BoundsMin, m_Config.InitialBounds.BoundsMin);
- m_Config.InitialBounds.BoundsMax = glm::max(data->Bounds.BoundsMax, m_Config.InitialBounds.BoundsMax);
- }
- }
- // Step 1: Calculate the initial center and extent
- glm::vec3 center = m_Config.InitialBounds.Center();
- glm::vec3 extent = m_Config.InitialBounds.BoundsMax - m_Config.InitialBounds.BoundsMin;
- // Step 2: Find the largest extent to ensure the bounding box is a cube
- float maxExtent = glm::compMax(extent);
- // Step 3: Create a cubic bounding box centered on the original center
- glm::vec3 halfExtent = glm::vec3(maxExtent) * 0.5f;
- // Step 4: Adjust the bounds to make them uniform and cubic
- m_Config.InitialBounds.BoundsMin = center - halfExtent;
- m_Config.InitialBounds.BoundsMax = center + halfExtent;
- // Optional: Apply a scaling factor to add some padding around the entire scene
- float scaleFactor = 1.1f;
- m_Config.InitialBounds.BoundsMin *= scaleFactor;
- m_Config.InitialBounds.BoundsMax *= scaleFactor;
- }
- else {
- m_Config.InitialBounds.BoundsMin = glm::vec3(50.0f);
- m_Config.InitialBounds.BoundsMax = glm::vec3(-50.0f);
- }
- // 3. Create Root Octree Node
- m_RootNode = std::make_shared<OctreeBoundsNode<DataType>>(m_Config.InitialBounds, this);
- // 4. Insert All Data Sources
- if (!data_sources.empty())
- InsertVector(data_sources);
- }
- template<typename DataType>
- inline void OctreeBounds<DataType>::GrowOctree()
- {
- }
- template<typename DataType>
- inline void OctreeBounds<DataType>::ShrinkOctree()
- {
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement