Initial commit: Final state of the master project
This commit is contained in:
51
Research/scene/Octree/BaseTree.cpp
Normal file
51
Research/scene/Octree/BaseTree.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
#include "BaseTree.h"
|
||||
|
||||
std::vector<BaseTree*> BaseTree::mTreePool = std::vector<BaseTree*>();
|
||||
|
||||
BaseTree::BaseTree()
|
||||
{
|
||||
mTreeIndex = AssignRootIndex();
|
||||
}
|
||||
BaseTree::~BaseTree()
|
||||
{
|
||||
mTreePool[mTreeIndex] = NULL;
|
||||
}
|
||||
|
||||
// Returns the number of bytes per pointer for each level as a vector
|
||||
std::vector<unsigned8> BaseTree::GetAdditionalBytesPerPointer() const
|
||||
{
|
||||
std::vector<unsigned8> res(GetMaxLevel() + 1);
|
||||
for (unsigned8 level = 0; level <= GetMaxLevel(); level++)
|
||||
res[level] = GetAdditionalBytesPerPointer(level);
|
||||
return res;
|
||||
}
|
||||
// Returns the number of bytes per node for each level as a vector.
|
||||
std::vector<unsigned8> BaseTree::GetAdditionalBytesPerNode() const
|
||||
{
|
||||
std::vector<unsigned8> res(GetMaxLevel() + 1);
|
||||
for (unsigned8 level = 0; level <= GetMaxLevel(); level++)
|
||||
res[level] = GetAdditionalBytesPerNode(level);
|
||||
return res;
|
||||
}
|
||||
|
||||
unsigned16 BaseTree::AssignRootIndex()
|
||||
{
|
||||
if (!mTreePool.empty())
|
||||
{
|
||||
// Try and find an empty spot in the RootPool:
|
||||
for (size_t i = 0; i < mTreePool.size(); i++)
|
||||
{
|
||||
if (mTreePool[i] == NULL)
|
||||
{
|
||||
mTreePool[i] = this;
|
||||
return (unsigned16)i;
|
||||
}
|
||||
if (mTreePool[i] == this) return (unsigned16)i; // Already assigned
|
||||
}
|
||||
}
|
||||
// If no empty spot was found, increase the size of the root
|
||||
mTreePool.push_back(this);
|
||||
assert(mTreePool.size() < BitHelper::Exp2(16));
|
||||
return (unsigned16)(mTreePool.size() - 1);
|
||||
}
|
||||
67
Research/scene/Octree/BaseTree.h
Normal file
67
Research/scene/Octree/BaseTree.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include "../../core/Defines.h"
|
||||
#include "Node.h"
|
||||
|
||||
class BaseTree
|
||||
{
|
||||
public:
|
||||
BaseTree();
|
||||
virtual ~BaseTree();
|
||||
|
||||
virtual const Node* GetNode(const unsigned32 index) const = 0;
|
||||
virtual Node* GetNode(const unsigned32 index) = 0;
|
||||
virtual unsigned32 GetNodeCount() const = 0;
|
||||
virtual unsigned8 GetMaxLevel() const = 0;
|
||||
virtual bool IsEmpty() const = 0;
|
||||
virtual void Clear() = 0;
|
||||
virtual Node* Create(unsigned8 level) = 0;
|
||||
// Destroy the node at the given index. Note that this is not required to fix references to this node, so it is quite unsafe to call!
|
||||
// This is only to save memory when, for example, moving nodes from one tree to the other
|
||||
virtual void Destroy(unsigned32 index) = 0;
|
||||
|
||||
// Algorithms that can be done on any tree:
|
||||
virtual void Shave(unsigned8 depth) = 0;
|
||||
virtual std::vector<unsigned32> SortOnLevel() = 0;
|
||||
|
||||
// General information about the tree
|
||||
virtual std::vector<size_t> GetParentCounts() const = 0;
|
||||
virtual std::vector<size_t> GetNodesPerLevel() const = 0;
|
||||
virtual std::vector<size_t> GetOctreeNodesPerLevel() const = 0;
|
||||
virtual unsigned64 GetPointerCount() const = 0;
|
||||
virtual void PrintDebugInfo() const = 0;
|
||||
|
||||
// Reading and writing to/from files
|
||||
virtual bool ReadTree(const char* fileName) = 0;
|
||||
virtual bool WriteTree(const char* fileName) = 0;
|
||||
virtual bool VerifyTree(const char* fileName) = 0;
|
||||
virtual bool ReadAdditionalPool(const char* fileName) = 0;
|
||||
virtual bool WriteAdditionalPool(const char* fileName) = 0;
|
||||
|
||||
// Pool building information
|
||||
virtual unsigned8 GetAdditionalTreeInfoSize() const = 0;
|
||||
virtual std::vector<unsigned8> GetAdditionalTreeInfo(const std::vector<size_t>& nodePointers) const = 0;
|
||||
virtual unsigned8 GetAdditionalBytesPerNode(unsigned8 level) const = 0;
|
||||
virtual std::vector<unsigned8> GetAdditionalNodeBytes(const Node* node) const = 0;
|
||||
virtual bool LastChildHasAdditionalBytes() const = 0;
|
||||
virtual unsigned8 GetAdditionalBytesPerPointer(unsigned8 level) const = 0;
|
||||
virtual std::vector<unsigned8> GetAdditionalPointerBytes(const Node* node, ChildIndex child) const = 0;
|
||||
|
||||
// Returns the number of bytes per pointer for each level as a vector
|
||||
std::vector<unsigned8> GetAdditionalBytesPerPointer() const;
|
||||
// Returns the number of bytes per node for each level as a vector.
|
||||
std::vector<unsigned8> GetAdditionalBytesPerNode() const;
|
||||
|
||||
inline unsigned16 GetTreeIndex() const { return mTreeIndex; }
|
||||
inline static BaseTree* Get(unsigned16 rootIndex) { return mTreePool[rootIndex]; }
|
||||
|
||||
private:
|
||||
unsigned16 AssignRootIndex();
|
||||
|
||||
static std::vector<BaseTree*> mTreePool;
|
||||
unsigned16 mTreeIndex;
|
||||
};
|
||||
|
||||
52
Research/scene/Octree/ChildMask.h
Normal file
52
Research/scene/Octree/ChildMask.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
#include "../../core/Defines.h"
|
||||
#include "../../core/BitHelper.h"
|
||||
|
||||
struct ChildBits
|
||||
{
|
||||
unsigned8 c0 : 1, c1 : 1, c2 : 1, c3 : 1, c4 : 1, c5 : 1, c6 : 1, c7 : 1;
|
||||
};
|
||||
union ChildMask
|
||||
{
|
||||
ChildBits children;
|
||||
unsigned8 mask;
|
||||
|
||||
ChildMask() : ChildMask(0) {}
|
||||
ChildMask(unsigned8 mask) { this->mask = mask; }
|
||||
|
||||
inline bool Get(ChildIndex index) const {
|
||||
return BitHelper::GetLS(mask, index);
|
||||
//switch (index)
|
||||
//{
|
||||
//case 0: return children.c0;
|
||||
//case 1: return children.c1;
|
||||
//case 2: return children.c2;
|
||||
//case 3: return children.c3;
|
||||
//case 4: return children.c4;
|
||||
//case 5: return children.c5;
|
||||
//case 6: return children.c6;
|
||||
//case 7: return children.c7;
|
||||
//}
|
||||
//return false;
|
||||
}
|
||||
|
||||
inline void Set(ChildIndex index, bool value)
|
||||
{
|
||||
BitHelper::SetLS(mask, index, value);
|
||||
//switch (index)
|
||||
//{
|
||||
//case 0: children.c0 = value; break;
|
||||
//case 1: children.c1 = value; break;
|
||||
//case 2: children.c2 = value; break;
|
||||
//case 3: children.c3 = value; break;
|
||||
//case 4: children.c4 = value; break;
|
||||
//case 5: children.c5 = value; break;
|
||||
//case 6: children.c6 = value; break;
|
||||
//case 7: children.c7 = value; break;
|
||||
//}
|
||||
}
|
||||
|
||||
// Use bit-tricks to count the number of set bits before a certain positions
|
||||
inline unsigned8 GetSetBefore(ChildIndex pos) const { return BitHelper::GetHSSetBefore(mask, pos); }
|
||||
inline unsigned8 GetSet() const { return BitHelper::GetSet(mask); }
|
||||
};
|
||||
5
Research/scene/Octree/ColorChannelMultiRootTree.h
Normal file
5
Research/scene/Octree/ColorChannelMultiRootTree.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
#include "../Material/ColorChannel.h"
|
||||
#include "LeafMaterialMultiRootTree.h"
|
||||
|
||||
typedef LeafMaterialMultiRootTree<ColorChannel> ColorChannelMultiRootTree;
|
||||
95
Research/scene/Octree/EdgeMaterialNode.h
Normal file
95
Research/scene/Octree/EdgeMaterialNode.h
Normal file
@@ -0,0 +1,95 @@
|
||||
#pragma once
|
||||
#include <fstream>
|
||||
#include "Node.h"
|
||||
#include "BaseTree.h"
|
||||
#include "../../core/Util/SmallDynamicArray.h"
|
||||
#include "../../inc/glm/glm.hpp"
|
||||
#include "../../core/Defines.h"
|
||||
|
||||
template<typename T, typename Comparer = std::less<T>>
|
||||
class EdgeMaterialNode : public Node
|
||||
{
|
||||
private:
|
||||
SmallDynamicArray<T> mEdgeMaterials;
|
||||
public:
|
||||
EdgeMaterialNode(BaseTree* root, unsigned8 level = 0) : Node(root, level), mEdgeMaterials(SmallDynamicArray<T>()) {}
|
||||
//EdgeMaterialNode(const EdgeMaterialNode<T>& node) : Node(node) { SetEdgeMaterials(node->GetEdgeMaterials()); }
|
||||
EdgeMaterialNode(EdgeMaterialNode&& node) : Node(std::move(node))
|
||||
{
|
||||
mEdgeMaterials = std::move(node.mEdgeMaterials);
|
||||
}
|
||||
~EdgeMaterialNode() {}
|
||||
|
||||
EdgeMaterialNode& operator=(EdgeMaterialNode&& node)
|
||||
{
|
||||
mEdgeMaterials = std::move(node.mEdgeMaterials);
|
||||
Node::operator=(std::move(node));
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Takes a pointer to an array in memory containing at least as many entries as there are children for this node.
|
||||
// Copies that array as the edge materials for this node
|
||||
void SetEdgeMaterials(T* edgeMaterials)
|
||||
{
|
||||
mEdgeMaterials.Clear();
|
||||
unsigned8 childCount = GetChildCount();
|
||||
mEdgeMaterials.Resize(0, childCount);
|
||||
mEdgeMaterials.SetRange(edgeMaterials, 0, childCount);
|
||||
}
|
||||
T* GetEdgeMaterials() const { return &mEdgeMaterials[0]; }
|
||||
T GetEdgeMaterial(ChildIndex child) const { return mEdgeMaterials[GetChildmask().GetSetBefore(child)]; }
|
||||
void SetEdgeMaterial(ChildIndex child) const { assert(GetChildmask().Get(child)); mEdgeMaterials[GetChildmask().GetSetBefore(child)]; }
|
||||
|
||||
bool Compare(const EdgeMaterialNode& node) const
|
||||
{
|
||||
if (!mEdgeMaterials.IsEmpty() && !node.mEdgeMaterials.IsEmpty())
|
||||
{
|
||||
// DEBUG: Compare the shifts.
|
||||
// This should lead to the same results as comparing the pointers but you never know
|
||||
for (unsigned8 i = 0; i < GetChildCount(); i++)
|
||||
{
|
||||
if (node.mEdgeMaterials[i] != this->mEdgeMaterials[i])
|
||||
return node.mEdgeMaterials[i] < this->mEdgeMaterials[i];
|
||||
}
|
||||
}
|
||||
|
||||
return Node::Compare(node);
|
||||
}
|
||||
bool Equals(const EdgeMaterialNode& node) const
|
||||
{
|
||||
if (!Node::Equals(node))
|
||||
return false;
|
||||
|
||||
if (this->mEdgeMaterials.IsEmpty() || node.mEdgeMaterials.IsEmpty())
|
||||
return this->mEdgeMaterials.IsEmpty() && node.mEdgeMaterials.IsEmpty();
|
||||
else
|
||||
{
|
||||
// DEBUG: Check if the shifts are equal
|
||||
// This should lead to the same results as comparing the pointers but you never know
|
||||
for (unsigned8 i = 0; i < GetChildmask().GetSet(); i++)
|
||||
{
|
||||
if (node.mEdgeMaterials[i] != this->mEdgeMaterials[i])
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void WriteProperties(std::ostream& file)
|
||||
{
|
||||
assert(!mEdgeMaterials.IsEmpty() || GetChildCount() == 0);
|
||||
Serializer<T*>::Serialize(&mEdgeMaterials[0], GetChildCount(), file);
|
||||
Node::WriteProperties(file);
|
||||
}
|
||||
void ReadProperties(std::istream& file)
|
||||
{
|
||||
mEdgeMaterials.Clear();
|
||||
mEdgeMaterials.Resize(0, GetChildCount());
|
||||
Serializer<T*>::Deserialize(&mEdgeMaterials[0], GetChildCount(), file);
|
||||
Node::ReadProperties(file);
|
||||
}
|
||||
void CopyProperties(EdgeMaterialNode* source)
|
||||
{
|
||||
SetEdgeMaterials(source->GetEdgeMaterials());
|
||||
}
|
||||
};
|
||||
252
Research/scene/Octree/HierarchicalColorsOnlyTree.cpp
Normal file
252
Research/scene/Octree/HierarchicalColorsOnlyTree.cpp
Normal file
@@ -0,0 +1,252 @@
|
||||
#include "HierarchicalColorsOnlyTree.h"
|
||||
#include "MaterialLibraryTree.h"
|
||||
#include "../../core/Defines.h"
|
||||
#include "../../scene/Material/MaterialQuantizer/ColorQuantizer/BaseColorQuantizer.h"
|
||||
|
||||
HierarchicalColorsOnlyTree::HierarchicalColorsOnlyTree(unsigned8 maxLevel) : MaterialLibraryTree<Color, ColorCompare>(maxLevel)
|
||||
{
|
||||
}
|
||||
|
||||
void HierarchicalColorsOnlyTree::ToDAG(BaseColorQuantizer* quantizer)
|
||||
{
|
||||
if (this->GetNodeCount() == 1)
|
||||
return; // Empty tree = DAG
|
||||
|
||||
// Sort the nodes on level (for quick access of parent nodes)
|
||||
auto levelIndices = SortOnLevel();
|
||||
|
||||
this->mMaxLevel = (unsigned8)(levelIndices.size() - 2);
|
||||
|
||||
// Run through the layers in reverse order and compress them bottom up
|
||||
for (unsigned8 level = GetMaxLevel(); level > 0; --level)
|
||||
{
|
||||
printf(".");
|
||||
unsigned32 levelStart = levelIndices[level];
|
||||
unsigned32 levelEnd = levelIndices[level + 1];
|
||||
unsigned32 levelNodeCount = levelIndices[level + 1] - levelIndices[level];
|
||||
|
||||
//// Sort the nodes in the current level on material properties and child pointers
|
||||
//tbb::parallel_sort(levelStart, levelEnd, [](Node* a, Node* b) { return a->Compare(b); });
|
||||
SortBetween(levelStart, levelEnd, NodeComparer());
|
||||
|
||||
// Find all equal nodes
|
||||
MaterialLibraryNode* cur = NULL;
|
||||
std::unordered_map<unsigned, unsigned> replacements;
|
||||
std::vector<unsigned> uniqueNodes;
|
||||
for (unsigned32 i = levelStart; i < levelEnd; i++)
|
||||
{
|
||||
auto node = GetTypedNode(i);
|
||||
if (!node->Equals(*cur))
|
||||
{
|
||||
cur = node;
|
||||
uniqueNodes.push_back(cur->GetIndex());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make sure that all nodes are replaced by their equals
|
||||
replacements.insert(std::pair<unsigned, unsigned>(node->GetIndex(), cur->GetIndex()));
|
||||
}
|
||||
}
|
||||
|
||||
// From the list of unique nodes, find out which ones can be merged.
|
||||
// Get all colors
|
||||
std::unordered_map<unsigned, glm::u8vec3> uniqueNodeColors;
|
||||
for (size_t i = 0; i < uniqueNodes.size(); i++)
|
||||
{
|
||||
unsigned node = uniqueNodes[i];
|
||||
uniqueNodeColors.insert(std::pair<unsigned, glm::u8vec3>(node, GetMaterial(GetTypedNode(node)).GetColor()));
|
||||
}
|
||||
|
||||
if (level != GetMaxLevel())
|
||||
{
|
||||
// Get the unique colors of the unique nodes
|
||||
std::vector<glm::u8vec3> colorsToCompress(uniqueNodeColors.size());
|
||||
size_t k = 0;
|
||||
for (auto uniqueColor : uniqueNodeColors)
|
||||
colorsToCompress[k++] = uniqueColor.second;
|
||||
|
||||
tbb::parallel_sort(colorsToCompress.begin(), colorsToCompress.end(), ColorCompare());
|
||||
colorsToCompress.erase(std::unique(colorsToCompress.begin(), colorsToCompress.end()), colorsToCompress.end());
|
||||
colorsToCompress.shrink_to_fit();
|
||||
// Quantize these colors
|
||||
if (quantizer != NULL)
|
||||
{
|
||||
auto quantizedColors = quantizer->QuantizeColors(colorsToCompress);
|
||||
|
||||
// Replace the unique node colors by their quantized counterparts
|
||||
for (size_t i = 0; i < uniqueNodes.size(); i++)
|
||||
{
|
||||
auto originalColor = uniqueNodeColors.find(uniqueNodes[i]);
|
||||
auto quantizedColor = quantizedColors->find(originalColor->second)->second;
|
||||
originalColor->second = quantizedColor;
|
||||
}
|
||||
}
|
||||
|
||||
// Find out which nodes can be merged:
|
||||
for (auto i = uniqueNodes.begin(); i != uniqueNodes.end(); i++)
|
||||
{
|
||||
Node* cur = GetTypedNode(*i);
|
||||
// Find the list of potential merges
|
||||
auto mergeScores = GetMergeScores(uniqueNodeColors, i , i + 1, uniqueNodes.end());
|
||||
// Keep merging as long as more nodes can be merged with the current one
|
||||
while (!mergeScores.empty())
|
||||
{
|
||||
// From the list of potential merges, find the best one (the one with the highest score)
|
||||
unsigned bestMerge = mergeScores.begin()->first;
|
||||
unsigned bestScore = 0;
|
||||
for (auto merge : mergeScores)
|
||||
{
|
||||
if (merge.second > bestScore)
|
||||
{
|
||||
bestScore = merge.second;
|
||||
bestMerge = merge.first;
|
||||
}
|
||||
}
|
||||
|
||||
Node* otherNode = GetTypedNode(bestMerge);
|
||||
// Merge the other node into this node
|
||||
ChildMask childrenToMerge((cur->GetChildmask().mask ^ otherNode->GetChildmask().mask) & otherNode->GetChildmask().mask);
|
||||
for (ChildIndex c = 0; c < 8; c++)
|
||||
{
|
||||
if (childrenToMerge.Get(c))
|
||||
cur->SetChild(c, otherNode->GetChild(c));
|
||||
}
|
||||
// Update the replacers that pointed to other to point to cur
|
||||
for (auto replacement = replacements.begin(); replacement != replacements.end(); replacement++)
|
||||
{
|
||||
if (replacement->second == bestMerge)
|
||||
replacement->second = *i;
|
||||
}
|
||||
// Also replace the other node by the current in the replacements
|
||||
replacements.insert(std::pair<unsigned, unsigned>(bestMerge, *i));
|
||||
|
||||
// Remove other from uniqueNodes (since it is merged and will be removed completely)
|
||||
auto bestMergeIt = std::find(uniqueNodes.begin(), uniqueNodes.end(), bestMerge);
|
||||
uniqueNodes.erase(bestMergeIt);
|
||||
|
||||
// Calculate what merges might still be available after this one
|
||||
std::vector<unsigned> potentialMerges(mergeScores.size() - 1);
|
||||
// TODO: make this a parallel for?
|
||||
size_t k = 0;
|
||||
for (auto merge : mergeScores)
|
||||
{
|
||||
if (merge.first != bestMerge)
|
||||
{
|
||||
potentialMerges[k++] = merge.first;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if any other merges are still possible
|
||||
mergeScores = GetMergeScores(uniqueNodeColors, i, potentialMerges.begin(), potentialMerges.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mark nodes that are not in uniquenodes for deletion
|
||||
tbb::parallel_sort(uniqueNodes);
|
||||
unsigned32 uniqueNodesIndex = 0;
|
||||
for (unsigned32 i = levelStart; i < levelEnd; i++)
|
||||
{
|
||||
while (uniqueNodes[uniqueNodesIndex] < i) uniqueNodesIndex++;
|
||||
if (uniqueNodes[uniqueNodesIndex] != i) Destroy(i);
|
||||
}
|
||||
|
||||
|
||||
printf(".");
|
||||
|
||||
auto parentLevelStart = levelIndices[level - 1];
|
||||
auto parentLevelEnd = levelIndices[level];
|
||||
// Point all parents of nodes to the new replacement node
|
||||
// Note that this is only necessary if some nodes are replaced by others
|
||||
if (uniqueNodes.size() < levelNodeCount)
|
||||
{
|
||||
|
||||
for (unsigned32 j = parentLevelStart; j < parentLevelEnd; j++)
|
||||
{
|
||||
Node* parent = GetTypedNode(j);
|
||||
for (ChildIndex child = FRONT_BOTTOM_LEFT; child <= BACK_TOP_RIGHT; ++child)
|
||||
{
|
||||
if (parent->HasChild(child))
|
||||
{
|
||||
unsigned childPtr = parent->GetChildIndex(child);
|
||||
auto replacer = replacements.find(childPtr);
|
||||
if (replacer != replacements.end())
|
||||
{
|
||||
auto newChild = GetNode((*replacer).second);
|
||||
if (newChild == NULL)
|
||||
printf("Empty child being set?");
|
||||
parent->SetChild(child, newChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
printf(".");
|
||||
|
||||
//// Remove the old (replaced) nodes
|
||||
//if (uniqueNodes.size() < levelNodeCount)
|
||||
//{
|
||||
// tbb::parallel_for_each(replacements.begin(), replacements.end(), [&](std::pair<unsigned, unsigned> node)
|
||||
// {
|
||||
// Destroy(mNodePool[node.first]);
|
||||
// mNodePool[node.first] = NULL;
|
||||
// });
|
||||
//}
|
||||
//replacements.clear();
|
||||
|
||||
printf(" ");
|
||||
printf("Layer %2u compressed, %7u out of %7u nodes left\n", level, (unsigned32)uniqueNodes.size(), levelNodeCount);
|
||||
}
|
||||
|
||||
Clean();
|
||||
}
|
||||
|
||||
std::vector<std::pair<unsigned, unsigned>> HierarchicalColorsOnlyTree::GetMergeScores(const std::unordered_map<unsigned, glm::u8vec3> uniqueNodeColors,
|
||||
std::vector<unsigned>::iterator i, std::vector<unsigned>::iterator begin, std::vector<unsigned>::iterator end)
|
||||
{
|
||||
Node* cur = GetTypedNode(*i);
|
||||
glm::u8vec3 curColor = uniqueNodeColors.at(*i);
|
||||
ChildMask curChildMask = cur->GetChildmask();
|
||||
std::vector<std::pair<unsigned, unsigned>> mergeScores;
|
||||
for (auto j = begin; j != end; j++)
|
||||
{
|
||||
auto othColor = uniqueNodeColors.at(*j);
|
||||
// If the average colors are not the same, they can't be mergeds
|
||||
if (othColor != curColor)
|
||||
continue;
|
||||
// The nodes can be merged if all children that exist are the same.
|
||||
// Effectively this means that we need to check if the pointers to the children that both nodes have are the same
|
||||
auto other = GetTypedNode(*j);
|
||||
ChildMask otherChildMask = other->GetChildmask();
|
||||
// If the childmasks are the same, then, if the children are also the same, these nodes would already have been merged.
|
||||
// Therefore, there is no need to check for merge possibilities
|
||||
if (curChildMask.mask == otherChildMask.mask)
|
||||
continue;
|
||||
|
||||
ChildMask bothNodesHave = ChildMask(curChildMask.mask & otherChildMask.mask);
|
||||
// Initialize the merge score as the number of set nodes that are not set in both (e.g. XOR(mask1, mask2).GetSet())
|
||||
unsigned mergeScore = ChildMask(curChildMask.mask ^ otherChildMask.mask).GetSet();
|
||||
bool potentialMerge = true;
|
||||
|
||||
// Check if merging is possible and calculate the merge score
|
||||
for (ChildIndex c = 0; c < 8; c++)
|
||||
{
|
||||
if (bothNodesHave.Get(c))
|
||||
{
|
||||
if (cur->GetChild(c) == other->GetChild(c))
|
||||
{
|
||||
mergeScore += 2; // We can merge 2 nodes :D
|
||||
}
|
||||
else
|
||||
{
|
||||
potentialMerge = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (potentialMerge)
|
||||
mergeScores.push_back(std::pair<unsigned, unsigned>(*j, mergeScore));
|
||||
}
|
||||
return mergeScores;
|
||||
}
|
||||
21
Research/scene/Octree/HierarchicalColorsOnlyTree.h
Normal file
21
Research/scene/Octree/HierarchicalColorsOnlyTree.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include <fstream>
|
||||
#include "MaterialLibraryTree.h"
|
||||
#include "../../inc/glm/glm.hpp"
|
||||
#include "../../core/Defines.h"
|
||||
#include "../Material/Color.h"
|
||||
|
||||
class Root;
|
||||
class BaseColorQuantizer;
|
||||
|
||||
class HierarchicalColorsOnlyTree : public MaterialLibraryTree<Color, ColorCompare>
|
||||
{
|
||||
public:
|
||||
HierarchicalColorsOnlyTree(unsigned8 maxLevel);
|
||||
|
||||
// Special DAG conversion: also merges nodes that are different if the only difference is that one of the nodes has a child with some color and the other node doesn't
|
||||
void ToDAG(BaseColorQuantizer* quantizer);
|
||||
private:
|
||||
std::vector<std::pair<unsigned, unsigned>> GetMergeScores(const std::unordered_map<unsigned, glm::u8vec3> uniqueNodeColors,
|
||||
std::vector<unsigned>::iterator i, std::vector<unsigned>::iterator begin, std::vector<unsigned>::iterator end);
|
||||
};
|
||||
99
Research/scene/Octree/HierarchicalMaterialMultiRoot.h
Normal file
99
Research/scene/Octree/HierarchicalMaterialMultiRoot.h
Normal file
@@ -0,0 +1,99 @@
|
||||
#pragma once
|
||||
#include "MultiRootTree.h"
|
||||
#include "MaterialNode.h"
|
||||
|
||||
template<typename T, typename Comparer = std::less<T>>
|
||||
class MultiRootMaterialNode : public MaterialNode<T, Comparer>
|
||||
{
|
||||
private:
|
||||
bool mIsGeometry;
|
||||
|
||||
public:
|
||||
MultiRootMaterialNode(BaseTree* root, unsigned8 level = 0) : MaterialNode<T, Comparer>(root, level) {}
|
||||
MultiRootMaterialNode(BaseTree* root, T material, unsigned8 level = 0) : MaterialNode<T, Comparer>(root, material, level) {}
|
||||
MultiRootMaterialNode(BaseTree* root, T material, bool isGeometry, unsigned8 level = 0) : MultiRootMaterialNode(root, material, level) { mIsGeometry = isGeometry; }
|
||||
MultiRootMaterialNode(MultiRootMaterialNode&& node) : MaterialNode<T, Comparer>(std::move(node)) // Move ctor
|
||||
{
|
||||
mIsGeometry = std::move(node.mIsGeometry);
|
||||
}
|
||||
|
||||
~MultiRootMaterialNode() {}
|
||||
|
||||
MultiRootMaterialNode& operator=(MultiRootMaterialNode&& node) // Move assignment operator
|
||||
{
|
||||
mIsGeometry = std::move(node.mIsGeometry);
|
||||
MaterialNode<T, Comparer>::operator=(std::move(node));
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool GetIsGeometry() const { return mIsGeometry; }
|
||||
void SetIsGeometry(bool value) { mIsGeometry = value; }
|
||||
|
||||
bool Compare(const MultiRootMaterialNode<T, Comparer>& node) const
|
||||
{
|
||||
if (this->mIsGeometry != node.mIsGeometry) return !mIsGeometry;
|
||||
return MaterialNode<T, Comparer>::Compare(node);
|
||||
}
|
||||
|
||||
bool Equals(const MultiRootMaterialNode<T, Comparer>& node) const
|
||||
{
|
||||
return node.mIsGeometry == this->mIsGeometry && MaterialNode<T, Comparer>::Equals(node);
|
||||
}
|
||||
|
||||
void WriteProperties(std::ostream& file)
|
||||
{
|
||||
Serializer<bool>::Serialize(mIsGeometry, file);
|
||||
MaterialNode<T, Comparer>::WriteProperties(file);
|
||||
}
|
||||
void ReadProperties(std::istream& file)
|
||||
{
|
||||
Serializer<bool>::Deserialize(mIsGeometry, file);
|
||||
MaterialNode<T, Comparer>::ReadProperties(file);
|
||||
}
|
||||
|
||||
void CopyProperties(MultiRootMaterialNode* node)
|
||||
{
|
||||
this->SetIsGeometry(node->GetIsGeometry());
|
||||
MaterialNode<T, Comparer>::CopyProperties(node);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename Comparer = std::less<T>>
|
||||
class HierarchicalMaterialMultiRoot : public MultiRootTree<MultiRootMaterialNode<T, Comparer>>
|
||||
{
|
||||
private:
|
||||
T mMaterial; // Material for the root
|
||||
public:
|
||||
HierarchicalMaterialMultiRoot(unsigned8 maxLevel, unsigned32 slaveRootCount) :
|
||||
MultiRootTree<MultiRootMaterialNode<T, Comparer>>(maxLevel, slaveRootCount)
|
||||
{
|
||||
mLeafsAreEqual = false;
|
||||
}
|
||||
|
||||
~HierarchicalMaterialMultiRoot() override {}
|
||||
|
||||
T GetMaterial(unsigned32 nodeIndex) const
|
||||
{
|
||||
return GetTypedNode(nodeIndex)->GetMaterial();
|
||||
}
|
||||
|
||||
virtual unsigned8 GetAdditionalBytesPerNode(unsigned8 level) const override { return sizeof(T); }
|
||||
virtual std::vector<unsigned8> GetAdditionalNodeBytes(const Node* node) const override { return GetMaterial(node->GetIndex()).Serialize(); }
|
||||
|
||||
MultiRootMaterialNode<T, Comparer>* AddLeafNode(glm::uvec3 coordinate)
|
||||
{
|
||||
MultiRootMaterialNode<T, Comparer>* node = MultiRootTree<MultiRootMaterialNode<T, Comparer>>::AddLeafNode(coordinate);
|
||||
node->SetIsGeometry(true);
|
||||
return node;
|
||||
// TODO: Mark all nodes along the path as "IsGeometry" = True
|
||||
}
|
||||
|
||||
MultiRootMaterialNode<T, Comparer>* AddLeafNode(glm::uvec3 coordinate, size_t slaveRootID, T material)
|
||||
{
|
||||
MultiRootMaterialNode<T, Comparer>* node = MultiRootTree<MultiRootMaterialNode<T, Comparer>>::AddLeafNode(coordinate, slaveRootID);
|
||||
node->SetMaterial(material);
|
||||
node->SetIsGeometry(false);
|
||||
return node;
|
||||
// TODO: Mark all nodes along the path as "IsGeometry" = False
|
||||
}
|
||||
};
|
||||
95
Research/scene/Octree/HierarchicalShiftingColoredTree.cpp
Normal file
95
Research/scene/Octree/HierarchicalShiftingColoredTree.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
#include "HierarchicalShiftingColoredTree.h"
|
||||
|
||||
HierarchicalShiftingColoredTree::HierarchicalShiftingColoredTree(unsigned8 maxLevel) : MaterialLibraryTree<Color, ColorCompare>(maxLevel)
|
||||
{
|
||||
colorsReplaced = false;
|
||||
}
|
||||
|
||||
glm::i8vec3 GetShift(glm::u8vec3 source, glm::u8vec3 dest)
|
||||
{
|
||||
glm::i8vec3 shift(0);
|
||||
for (unsigned8 i = 0; i < 3; i++)
|
||||
{
|
||||
std::int16_t diff = (std::int16_t)dest[i] - (std::int16_t)source[i];
|
||||
shift[i] = diff >> 1;
|
||||
}
|
||||
return shift;
|
||||
}
|
||||
|
||||
glm::u8vec3 AccumulateShift(glm::u8vec3 source, glm::i8vec3 shift)
|
||||
{
|
||||
glm::u8vec3 dest(0);
|
||||
for (unsigned8 i = 0; i < 3; i++)
|
||||
{
|
||||
dest[i] = source[i] + shift[i] * 2;
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
Color GetShiftMaterial(glm::i8vec3 shift)
|
||||
{
|
||||
return Color(glm::u8vec3(
|
||||
127 + shift.x,
|
||||
127 + shift.y,
|
||||
127 + shift.z
|
||||
));
|
||||
}
|
||||
|
||||
void HierarchicalShiftingColoredTree::ReplaceColorsByShifts()
|
||||
{
|
||||
// Make sure we have an octree (not a DAG)
|
||||
this->ToOctree();
|
||||
auto levelIndices = SortOnLevel();
|
||||
|
||||
std::vector<glm::u8vec3> accumulatedShiftPerNode(GetNodeCount());
|
||||
std::vector<Color> shiftMaterials(GetNodeCount());
|
||||
|
||||
// Top down go through the tree, and for each node, assign the children some shift and calculate the accumulated shift.
|
||||
// The accumulated shift per node will in turn be used to calculate the shift per node without causing rounding errors.
|
||||
glm::u8vec3 rootColor = GetMaterial(GetRoot()).GetColor();
|
||||
glm::i8vec3 rootShift = GetShift(glm::u8vec3(0), rootColor);
|
||||
accumulatedShiftPerNode[0] = AccumulateShift(glm::u8vec3(0), rootShift);
|
||||
shiftMaterials[0] = GetShiftMaterial(rootShift);
|
||||
|
||||
// Calculate the shifts, top-down.
|
||||
for (unsigned8 level = 0; level < GetMaxLevel(); level++)
|
||||
{
|
||||
auto levelStart = levelIndices[level];
|
||||
auto levelEnd = levelIndices[level + 1];
|
||||
// Calculate the shift per child
|
||||
tbb::parallel_for(levelStart, levelEnd, [&](const unsigned32 i)
|
||||
{
|
||||
glm::u8vec3 parentColor = accumulatedShiftPerNode[i];
|
||||
Node* node = GetTypedNode(i);
|
||||
for (ChildIndex c = 0; c < 8; c++)
|
||||
{
|
||||
if (node->HasChild(c))
|
||||
{
|
||||
unsigned childIndex = node->GetChildIndex(c);
|
||||
glm::u8vec3 childColor = GetMaterial(GetTypedNode(childIndex)).GetColor();
|
||||
glm::i8vec3 shift = GetShift(parentColor, childColor);
|
||||
accumulatedShiftPerNode[childIndex] = AccumulateShift(parentColor, shift);
|
||||
shiftMaterials[childIndex] = GetShiftMaterial(shift);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Create a new material library containing the shifts
|
||||
if (mOwnsLibrary) delete mMaterialLibrary;
|
||||
mMaterialLibrary = new MaterialLibrary<Color, ColorCompare>();
|
||||
for (auto material : shiftMaterials)
|
||||
{
|
||||
mMaterialLibrary->AddMaterial(material);
|
||||
}
|
||||
mMaterialLibrary->Finalize();
|
||||
|
||||
// Replace all current node materials by their representative shift materials
|
||||
tbb::parallel_for((unsigned32)0, GetNodeCount(), [&](unsigned32 i)
|
||||
{
|
||||
MaterialLibraryNode* node = GetTypedNode(i);
|
||||
node->SetMaterial(mMaterialLibrary->GetTextureIndex(shiftMaterials[i]));
|
||||
});
|
||||
|
||||
colorsReplaced = true;
|
||||
}
|
||||
17
Research/scene/Octree/HierarchicalShiftingColoredTree.h
Normal file
17
Research/scene/Octree/HierarchicalShiftingColoredTree.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include <fstream>
|
||||
#include "../../inc/glm/glm.hpp"
|
||||
#include "../../core/Defines.h"
|
||||
#include "../Material/Color.h"
|
||||
#include "MaterialLibraryTree.h"
|
||||
|
||||
class Root;
|
||||
|
||||
class HierarchicalShiftingColoredTree : public MaterialLibraryTree<Color, ColorCompare>
|
||||
{
|
||||
private:
|
||||
bool colorsReplaced;
|
||||
public:
|
||||
HierarchicalShiftingColoredTree(unsigned8 maxLevel);
|
||||
void ReplaceColorsByShifts();
|
||||
};
|
||||
10
Research/scene/Octree/IAdditionalProperties.h
Normal file
10
Research/scene/Octree/IAdditionalProperties.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
#include "../../core/Defines.h"
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
class IAdditionalProperties
|
||||
{
|
||||
public:
|
||||
virtual std::map<std::string, std::string> GetAdditionalProperties() = 0;
|
||||
};
|
||||
12
Research/scene/Octree/IBlockTexture.h
Normal file
12
Research/scene/Octree/IBlockTexture.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include "../../core/Defines.h"
|
||||
|
||||
class IBlockTexture
|
||||
{
|
||||
public:
|
||||
virtual std::vector<unsigned8> GetBlockPointerPool() = 0;
|
||||
virtual size_t GetBlockPointerPoolSize() = 0;
|
||||
|
||||
virtual std::vector<unsigned8> GetBlockPool() = 0;
|
||||
virtual size_t GetBlockPoolSize() = 0;
|
||||
};
|
||||
10
Research/scene/Octree/IMaterialTexture.h
Normal file
10
Research/scene/Octree/IMaterialTexture.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
#include "../../core/Defines.h"
|
||||
|
||||
class IMaterialTexture
|
||||
{
|
||||
public:
|
||||
virtual std::vector<unsigned8> GetMaterialTexture() = 0;
|
||||
virtual unsigned GetMaterialTextureSize() = 0;
|
||||
virtual unsigned8 GetMaterialTextureChannelsPerPixel() = 0;
|
||||
};
|
||||
41
Research/scene/Octree/LeafMaterialMultiRootTree.h
Normal file
41
Research/scene/Octree/LeafMaterialMultiRootTree.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
#include "MultiRootTree.h"
|
||||
#include "MaterialNode.h"
|
||||
|
||||
template<typename T>
|
||||
class LeafMaterialMultiRootTree : public MultiRootTree<MaterialNode<T>>
|
||||
{
|
||||
public:
|
||||
LeafMaterialMultiRootTree(unsigned8 maxLevel, unsigned32 slaveRootCount)
|
||||
: MultiRootTree(maxLevel, slaveRootCount) { mLeafsAreEqual = false; }
|
||||
|
||||
~LeafMaterialMultiRootTree() override {}
|
||||
|
||||
unsigned8 GetAdditionalBytesPerNode(unsigned8 level) const override
|
||||
{
|
||||
if (level == GetMaxLevel()) return sizeof(T);
|
||||
else return Tree<MaterialNode<T>>::GetAdditionalBytesPerNode(level);
|
||||
}
|
||||
std::vector<unsigned8> GetAdditionalNodeBytes(const Node* node) const override
|
||||
{
|
||||
if (node->GetLevel() == GetMaxLevel())
|
||||
{
|
||||
auto matNode = (MaterialNode<T>*)node;
|
||||
return matNode->GetMaterial().Serialize();
|
||||
}
|
||||
else
|
||||
return Tree<MaterialNode<T>>::GetAdditionalNodeBytes(node);
|
||||
}
|
||||
|
||||
void AddLeafNode(glm::uvec3 coordinate, unsigned32 slaveRootID, T material)
|
||||
{
|
||||
MaterialNode<T>* node = MultiRootTree<MaterialNode<T>>::AddLeafNode(coordinate, slaveRootID);
|
||||
node->SetMaterial(material);
|
||||
}
|
||||
|
||||
void AddLeafNode(glm::uvec3 coordinate, T material)
|
||||
{
|
||||
MaterialNode<T>* node = MultiRootTree<MaterialNode<T>>::AddLeafNode(coordinate);
|
||||
node->SetMaterial(material);
|
||||
}
|
||||
};
|
||||
487
Research/scene/Octree/MaterialLibraryMultiRootTree.h
Normal file
487
Research/scene/Octree/MaterialLibraryMultiRootTree.h
Normal file
@@ -0,0 +1,487 @@
|
||||
#pragma once
|
||||
#include "HierarchicalMaterialMultiRoot.h"
|
||||
#include "../Material/BitsMaterial.h"
|
||||
#include "IMaterialTexture.h"
|
||||
#include "../../inc/tbb/parallel_for_each.h"
|
||||
#include "../Material/BlockBasedMaterialLibrary.h"
|
||||
#include "../../core/BitHelper.h"
|
||||
#include "../../core/CollectionHelper.h"
|
||||
#include "../../core/Util/BoolArray.h"
|
||||
#include "NodeReplacementFinder.h"
|
||||
#include "Tree.h"
|
||||
#include <queue>
|
||||
#include <unordered_set>
|
||||
|
||||
// Usage:
|
||||
// This tree can only be built correctly if it is based on some material tree that has materials throughout
|
||||
// (Such as HierarchicalRoot<T> or MaterialRoot<T>). To build this tree, create this root object and then call
|
||||
// the "BaseOn(tree)" method with the material tree.
|
||||
template<typename T, typename Comparer = std::less<T>, unsigned8 channelsPerPixel = 3>
|
||||
class MaterialLibraryMultiRoot : public HierarchicalMaterialMultiRoot<BitsMaterial<8>>, public IMaterialTexture
|
||||
{
|
||||
private:
|
||||
// After the tree is finalized, the material library will be used to contain the actual materials
|
||||
MaterialLibrary<T, Comparer, channelsPerPixel>* mMaterialLibrary;
|
||||
std::vector<unsigned8> mBitMap;
|
||||
|
||||
std::vector<unsigned char> mMaterialTexture;
|
||||
unsigned short mMaterialTextureSize;
|
||||
MaterialLibraryPointer mMaxTextureIndex;
|
||||
|
||||
inline void WriteMaterialTexture(std::ostream& file)
|
||||
{
|
||||
if (mMaterialTexture.empty())
|
||||
GetMaterialTexture();
|
||||
// Pack the texture size and the biggest texture index in one 32 bit unsigned int (for historic reasons...)
|
||||
unsigned materialTextureSizeSummary = (mMaxTextureIndex.x << 20) | (mMaxTextureIndex.y << 10) | (mMaterialTextureSize - 1);
|
||||
Serializer<unsigned>::Serialize(materialTextureSizeSummary, file);
|
||||
Serializer<unsigned8*>::Serialize(&mMaterialTexture[0], (size_t)mMaterialTextureSize * (size_t)mMaterialTextureSize * (size_t)channelsPerPixel, file);
|
||||
}
|
||||
|
||||
inline void ReadMaterialTexture(std::istream& file)
|
||||
{
|
||||
unsigned materialTextureSizeSummary;
|
||||
Serializer<unsigned>::Deserialize(materialTextureSizeSummary, file);
|
||||
unsigned mask1 = BitHelper::GetLSMask<unsigned32>(20, 30);
|
||||
unsigned mask2 = BitHelper::GetLSMask<unsigned32>(10, 20);
|
||||
unsigned mask3 = BitHelper::GetLSMask<unsigned32>(0, 10);
|
||||
unsigned short maxTextureIndexX = (mask1 & materialTextureSizeSummary) >> 20;
|
||||
unsigned short maxTextureIndexY = (mask2 & materialTextureSizeSummary) >> 10;
|
||||
unsigned materialTextureSize = mask3 & materialTextureSizeSummary;
|
||||
mMaterialTextureSize = materialTextureSize + 1;
|
||||
mMaxTextureIndex = MaterialLibraryPointer(maxTextureIndexX, maxTextureIndexY);
|
||||
|
||||
size_t textureArraySize = (size_t)mMaterialTextureSize * (size_t)mMaterialTextureSize * (size_t)channelsPerPixel;
|
||||
|
||||
mMaterialTexture.resize(textureArraySize);
|
||||
Serializer<unsigned8*>::Deserialize(&mMaterialTexture[0], textureArraySize, file);
|
||||
}
|
||||
|
||||
void AddMaterial(const T& material)
|
||||
{
|
||||
assert(!mMaterialLibrary->IsFinalized());
|
||||
mMaterialLibrary->AddMaterial(material);
|
||||
}
|
||||
|
||||
void FinalizeMaterials()
|
||||
{
|
||||
assert(!mMaterialLibrary->IsFinalized());
|
||||
mMaterialLibrary->Finalize();
|
||||
unsigned requiredXBits = BitHelper::Log2Ceil(mMaterialLibrary->GetTextureSize());
|
||||
unsigned requiredYBits = BitHelper::Log2Ceil(mMaterialLibrary->GetMaxTextureIndex().y);
|
||||
unsigned requiredBits = requiredXBits + requiredYBits;
|
||||
unsigned32 mask = BitHelper::GetLSMask<unsigned32>(16, 16 + requiredXBits) | BitHelper::GetLSMask<unsigned32>(0, requiredYBits);
|
||||
mBitMap = BitHelper::GetBitMapHS(mask);
|
||||
AddSlaveRoots(requiredBits); // The main root can be used for the first bit, the rest of the bits require slave roots
|
||||
}
|
||||
|
||||
bool CheckNodesToRemove(MultiRootMaterialNode<BitsMaterial<8>>* node, bool curValue, BoolArray& nodesCanBeShaved)
|
||||
{
|
||||
if (!node->HasChildren()) return true;
|
||||
auto mat = node->GetMaterial();
|
||||
bool childrenEqual = true;
|
||||
for (ChildIndex c = 0; c < 8; c++)
|
||||
{
|
||||
if (node->HasChild(c))
|
||||
{
|
||||
bool nodeMat = mat.GetLS(c);
|
||||
MultiRootMaterialNode<BitsMaterial<8>>* child = (MultiRootMaterialNode<BitsMaterial<8>>*)GetNode(node->GetChildIndex(c));
|
||||
bool nodeHasSameMat = CheckNodesToRemove(child, nodeMat, nodesCanBeShaved);
|
||||
if (nodeMat != curValue || !nodeHasSameMat) childrenEqual = false;
|
||||
}
|
||||
}
|
||||
if (!childrenEqual) nodesCanBeShaved.Set(node->GetIndex(), false);
|
||||
return childrenEqual;
|
||||
}
|
||||
|
||||
static std::vector<unsigned32> HashChildren(const MultiRootMaterialNode<BitsMaterial<8>>* node)
|
||||
{
|
||||
assert(!node->GetIsGeometry());
|
||||
std::vector<unsigned32> hash(8, 0);
|
||||
auto mat = node->GetMaterial();
|
||||
for (ChildIndex c = 0; c < 8; c++)
|
||||
{
|
||||
if (node->HasChild(c))
|
||||
hash[c] = (node->GetChildIndex(c) << 1) | (mat.GetLS(c) ? 1 : 0);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
public:
|
||||
MaterialLibraryMultiRoot(unsigned8 maxLevel) : HierarchicalMaterialMultiRoot<BitsMaterial<8>>(maxLevel, 0)
|
||||
{
|
||||
mMaterialLibrary = new BlockBasedMaterialLibrary<T, Comparer, channelsPerPixel>();
|
||||
}
|
||||
|
||||
~MaterialLibraryMultiRoot() override {
|
||||
if (mMaterialLibrary != NULL)
|
||||
delete mMaterialLibrary;
|
||||
}
|
||||
|
||||
void CreateMaterialLibrary(const std::vector<T>& materials)
|
||||
{
|
||||
for (const T& material : materials) AddMaterial(material);
|
||||
FinalizeMaterials();
|
||||
}
|
||||
|
||||
// Copies the geometry and material information of the given tree to this tree
|
||||
template <typename MaterialTree>
|
||||
void BaseOn(MaterialTree* tree, bool autoDAG)
|
||||
{
|
||||
// Make sure the leaf nodes are the last nodes in the tree, so that we can skip them and only construct one.
|
||||
std::vector<unsigned32> levelIndices = tree->SortOnLevel();
|
||||
|
||||
// Clear the existing tree
|
||||
Clear();
|
||||
auto root = Create(0);
|
||||
root->SetIsGeometry(true);
|
||||
|
||||
// Create the single leaf node that can exist for the geometry tree (for memory efficiency)
|
||||
auto geometryLeaf = Create(GetMaxLevel());
|
||||
geometryLeaf->SetIsGeometry(true);
|
||||
|
||||
// Copy the geometry information to a geometry tree
|
||||
unsigned32 destChildrenCache[8];
|
||||
unsigned32 offset = GetNodeCount();
|
||||
for (unsigned32 i = 0; i < levelIndices[GetMaxLevel()]; i++)
|
||||
{
|
||||
Node* source = tree->GetNode(i);
|
||||
unsigned32* sourceChildren = source->GetChildren();
|
||||
if (source->GetLevel() == GetMaxLevel() - 1)
|
||||
{
|
||||
for (ChildIndex child = 0; child < source->GetChildCount(); child++)
|
||||
destChildrenCache[child] = geometryLeaf->GetIndex();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (ChildIndex child = 0; child < source->GetChildCount(); child++)
|
||||
destChildrenCache[child] = offset + sourceChildren[child] - 1; // The root is reused, so that index shouldn't be counted towards the offset
|
||||
}
|
||||
auto dest = source->GetLevel() == 0 ? root : Create(source->GetLevel());
|
||||
dest->SetIsGeometry(true);
|
||||
dest->SetChildren(source->GetChildmask(), destChildrenCache);
|
||||
}
|
||||
|
||||
// Create and fill the material library (if this hasn't been done yet)
|
||||
if (!mMaterialLibrary->IsFinalized())
|
||||
{
|
||||
auto materials = tree->GetUniqueMaterials();
|
||||
CreateMaterialLibrary(materials);
|
||||
}
|
||||
else
|
||||
{ // Re-add the slaveroots
|
||||
AddSlaveRoots(mBitMap.size());
|
||||
}
|
||||
|
||||
unsigned32 bitCount = (unsigned32)mBitMap.size();
|
||||
|
||||
// Go through all nodes that aren't leaf nodes
|
||||
// And construct their (bit-based) material tree (e.g. the not-geometry part of the tree)
|
||||
auto leaf = Create(GetMaxLevel());
|
||||
leaf->SetIsGeometry(false);
|
||||
for (unsigned32 bit = 0; bit < bitCount; bit++)
|
||||
{
|
||||
offset = GetNodeCount();
|
||||
for (unsigned32 i = 0; i < levelIndices[GetMaxLevel()]; i++)
|
||||
{
|
||||
auto source = tree->GetTypedNode(i);
|
||||
|
||||
// Create the material the new node has to have
|
||||
BitsMaterial<8> destMaterial;
|
||||
for (ChildIndex childIdx = 0; childIdx < 8; childIdx++)
|
||||
{
|
||||
if (source->HasChild(childIdx))
|
||||
{
|
||||
auto child = tree->GetTypedNode(source->GetChildIndex(childIdx));
|
||||
T sourceMaterial = tree->GetMaterial(child);
|
||||
unsigned32 sourceMaterialPointer = (unsigned32)mMaterialLibrary->GetTextureIndex(sourceMaterial);
|
||||
destMaterial.SetLS(childIdx, BitHelper::GetHS(sourceMaterialPointer, mBitMap[bit]));
|
||||
}
|
||||
}
|
||||
|
||||
// Create the children pointers the new node has to have
|
||||
unsigned32* sourceChildren = source->GetChildren();
|
||||
if (source->GetLevel() == GetMaxLevel() - 1)
|
||||
{
|
||||
for (ChildIndex child = 0; child < source->GetChildCount(); child++)
|
||||
destChildrenCache[child] = leaf->GetIndex();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (ChildIndex child = 0; child < source->GetChildCount(); child++)
|
||||
destChildrenCache[child] = offset + sourceChildren[child] - 1; // The root gets reused so it shouldn't be counted towards the offset
|
||||
}
|
||||
|
||||
// Create the new node
|
||||
auto dest = source->GetLevel() == 0 ? GetSlaveRoot(bit) : Create(source->GetLevel());
|
||||
dest->SetMaterial(destMaterial);
|
||||
dest->SetIsGeometry(false);
|
||||
dest->SetChildren(source->GetChildmask(), destChildrenCache);
|
||||
}
|
||||
|
||||
if (autoDAG)
|
||||
{
|
||||
ToDAG(1, false);
|
||||
printf(".");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Bottom-up remove node that only have the same bit value (e.g. all 1 or all 0 for the non-geometry nodes).
|
||||
void ShaveEquals()
|
||||
{
|
||||
// For all the nodes on level 1, RemoveChildrenWithSameBit
|
||||
std::vector<unsigned32> levelIndices = SortOnLevel();
|
||||
BoolArray nodesToShave(GetNodeCount());
|
||||
// Assume that all nodes that arent geometry (or roots) can be shaved until the opposite has been proven
|
||||
for (unsigned32 i = 0; i < GetNodeCount(); i++)
|
||||
{
|
||||
Node* node = GetNode((unsigned32)i);
|
||||
if (node->GetLevel() > 0)
|
||||
{
|
||||
MultiRootMaterialNode<BitsMaterial<8>>* matNode = (MultiRootMaterialNode<BitsMaterial<8>>*)node;
|
||||
nodesToShave.Set(matNode->GetIndex(), !matNode->GetIsGeometry());
|
||||
}
|
||||
}
|
||||
|
||||
// Now try to find proof not to shave certain nodes
|
||||
for (unsigned32 i = levelIndices[1]; i < levelIndices[2]; i++)
|
||||
{
|
||||
MultiRootMaterialNode<BitsMaterial<8>>* node =GetTypedNode(i);
|
||||
if (!node->GetIsGeometry()) CheckNodesToRemove(node, false, nodesToShave); // Since nodes on the second level don't have material properties, assume the bit value is false
|
||||
}
|
||||
|
||||
// Shave all nodes of which no evidence is found not to shave them
|
||||
size_t nodesRemoved = 0;
|
||||
for (unsigned32 i = 0; i < GetNodeCount(); i++)
|
||||
{
|
||||
if (nodesToShave.Get(i))
|
||||
{
|
||||
nodesRemoved++;
|
||||
MultiRootMaterialNode<BitsMaterial<8>>* node = (MultiRootMaterialNode<BitsMaterial<8>>*)GetNode((unsigned32)i);
|
||||
node->SetMaterial(BitsMaterial<8>());
|
||||
node->SetChildren(0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
printf("Connections removed from %llu nodes.\n", (unsigned64)nodesRemoved);
|
||||
ClearOrphans();
|
||||
}
|
||||
|
||||
// Since the bit values only need to be correct for parts of the scene that are defined, we can randomly fill in the rest to be correct
|
||||
void FillEmptySpace(bool full = true)
|
||||
{
|
||||
// TODO: For the layer above the leaf node, just add all 8 children for each node (all pointing to the leaf node), and make
|
||||
// the only difference the bit node. Then connect all nodes in the layer above it correctly.
|
||||
std::vector<unsigned32> levelOffsets = SortOnLevel();
|
||||
|
||||
// Find the leaf node that is not geometry
|
||||
MultiRootMaterialNode<BitsMaterial<8>>* leaf = NULL;
|
||||
for (unsigned32 i = levelOffsets[GetMaxLevel()]; i < levelOffsets[GetMaxLevel() + 1]; i++)
|
||||
{
|
||||
auto node = GetTypedNode(i);
|
||||
if (!node->GetIsGeometry()) { leaf = node; break; }
|
||||
}
|
||||
assert(leaf != NULL);
|
||||
|
||||
// Create properties for pointers to this leaf node
|
||||
unsigned32 leafPointer = leaf->GetIndex();
|
||||
unsigned32 leafChildren[8];
|
||||
for (size_t i = 0; i < 8; i++) leafChildren[i] = leafPointer;
|
||||
ChildMask leafMask(255);
|
||||
|
||||
// Make sure all nodes above the leaf level have full geometry, allowing more merging
|
||||
for (unsigned32 i = levelOffsets[GetMaxLevel() - 1]; i < levelOffsets[GetMaxLevel()]; i++)
|
||||
{
|
||||
auto node = GetTypedNode(i);
|
||||
if (!node->GetIsGeometry() && node->HasChildren())
|
||||
node->SetChildren(leafMask, leafChildren);
|
||||
}
|
||||
|
||||
if (!full) return;
|
||||
|
||||
// TODO: After every layer is made smaller, call "ToDAG" up to the next level to process :P
|
||||
// Now use a lookup table to quickly find all feasible nodes for a merge.
|
||||
std::function<std::vector<unsigned32>(const MultiRootMaterialNode<BitsMaterial<8>>*)> childrenHasher = &MaterialLibraryMultiRoot::HashChildren;
|
||||
|
||||
for (auto level = GetMaxLevel() - 1; level-- > 1;)
|
||||
{
|
||||
ToDAG(level - 1);
|
||||
levelOffsets = SortOnLevel();
|
||||
std::vector<size_t> parentsPerNode = GetParentCounts();
|
||||
NodeReplacementFinder<unsigned32, MultiRootMaterialNode<BitsMaterial<8>>*> finder(childrenHasher);
|
||||
|
||||
auto levelStart = levelOffsets[level];
|
||||
auto levelEnd = levelOffsets[level + 1];
|
||||
std::vector<MultiRootMaterialNode<BitsMaterial<8>>*> nodesToTryVec;
|
||||
// Add all nodes to the finder:
|
||||
for (size_t i = levelStart; i < levelEnd; i++)
|
||||
{
|
||||
MultiRootMaterialNode<BitsMaterial<8>>* node = (MultiRootMaterialNode<BitsMaterial<8>>*)GetNode((unsigned32)i);
|
||||
if (!node->GetIsGeometry() && node->HasChildren())
|
||||
{
|
||||
finder.Add(node);
|
||||
nodesToTryVec.push_back(node);
|
||||
}
|
||||
}
|
||||
// Sort nodes on number of parents. Merging nodes with many parents is preferred as it has a high probability of leading to more merges higher up in the tree
|
||||
std::sort(nodesToTryVec.begin(), nodesToTryVec.end(), [&](MultiRootMaterialNode<BitsMaterial<8>>* a, MultiRootMaterialNode<BitsMaterial<8>>* b)
|
||||
{
|
||||
return parentsPerNode[a->GetIndex()] > parentsPerNode[b->GetIndex()];
|
||||
});
|
||||
|
||||
std::queue<MultiRootMaterialNode<BitsMaterial<8>>*> nodesToTry;
|
||||
for (auto nodeToTry : nodesToTryVec) nodesToTry.push(nodeToTry);
|
||||
|
||||
std::unordered_set<MultiRootMaterialNode<BitsMaterial<8>>*> allMergedNodes;
|
||||
// Now replace as much as possible
|
||||
while (!nodesToTry.empty())
|
||||
{
|
||||
MultiRootMaterialNode<BitsMaterial<8>>* node = nodesToTry.front();
|
||||
if (allMergedNodes.find(node) == allMergedNodes.end())
|
||||
{
|
||||
std::vector<MultiRootMaterialNode<BitsMaterial<8>>*> mergeOptions = finder.Find(node);
|
||||
// Prefer the merge options with most children :)
|
||||
std::sort(mergeOptions.begin(), mergeOptions.end(), [&](Node* a, Node* b)
|
||||
{
|
||||
if (parentsPerNode[a->GetIndex()] != parentsPerNode[b->GetIndex()]) return parentsPerNode[a->GetIndex()] > parentsPerNode[b->GetIndex()];
|
||||
return a->GetChildCount() > b->GetChildCount();
|
||||
});
|
||||
// All merge options for this node will be explored, so remove it
|
||||
if (mergeOptions.size() > 1)
|
||||
{
|
||||
// Keep track of which nodes have been merged, so that we can set them to be equal to this node in the end
|
||||
ChildMask combinedMask = node->GetChildmask();
|
||||
unsigned32* combinedChildIndices = new unsigned32[8];
|
||||
for (ChildIndex c = 0; c < 8; c++) if (node->HasChild(c)) combinedChildIndices[c] = node->GetChildIndex(c);
|
||||
unsigned8 combinedMaterial = (unsigned8)node->GetMaterial().GetValue();
|
||||
|
||||
std::vector<MultiRootMaterialNode<BitsMaterial<8>>*> mergedNodes(1, node);
|
||||
for (auto option : mergeOptions)
|
||||
{
|
||||
if (option != node)
|
||||
{
|
||||
// Check if the merge is still valid
|
||||
unsigned8 optionMaterial = (unsigned8)option->GetMaterial().GetValue();
|
||||
unsigned8 optionMask = (unsigned8)option->GetChildmask().mask;
|
||||
|
||||
// The material mask should be the same for children that both nodes have:
|
||||
bool valid = (optionMaterial & (optionMask & combinedMask.mask)) == (combinedMaterial & (optionMask & combinedMask.mask));
|
||||
if (valid)
|
||||
for (ChildIndex c = 0; c < 8; c++)
|
||||
if (combinedMask.Get(c) && option->HasChild(c) && combinedChildIndices[c] != node->GetChildIndex(c))
|
||||
{
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// If the merge is still valid, updated the combined Mask, combined material and combinedChildIndices
|
||||
if (valid)
|
||||
{
|
||||
for (ChildIndex c = 0; c < 8; c++)
|
||||
{
|
||||
if (!combinedMask.Get(c) && option->HasChild(c))
|
||||
{
|
||||
combinedChildIndices[c] = option->GetChildIndex(c);
|
||||
combinedMask.Set(c, true);
|
||||
}
|
||||
}
|
||||
combinedMaterial |= optionMaterial;
|
||||
mergedNodes.push_back(option);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If more nodes then the current node are merged,
|
||||
// Update all merged nodes to be equal. Also remove the (old) merged nodes from the finder and add the merged version
|
||||
if (mergedNodes.size() > 1)
|
||||
{
|
||||
unsigned8 i = 0;
|
||||
unsigned32* combinedChildren = new unsigned32[combinedMask.GetSet()];
|
||||
for (ChildIndex c = 0; c < 8; c++) if (combinedMask.Get(c)) combinedChildren[i++] = combinedChildIndices[c];
|
||||
BitsMaterial<8> combinedMaterialProp((size_t)combinedMaterial);
|
||||
|
||||
for (auto mergedNode : mergedNodes)
|
||||
{
|
||||
finder.Remove(mergedNode);
|
||||
mergedNode->SetChildren(combinedMask, combinedChildren);
|
||||
mergedNode->SetMaterial(combinedMaterialProp);
|
||||
allMergedNodes.insert(mergedNode);
|
||||
}
|
||||
delete combinedChildren;
|
||||
finder.Add(node);
|
||||
}
|
||||
else
|
||||
{
|
||||
finder.Remove(node);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
finder.Remove(node);
|
||||
}
|
||||
}
|
||||
nodesToTry.pop();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::vector<unsigned8> GetMaterialTexture() override
|
||||
{
|
||||
if (!mMaterialTexture.empty())
|
||||
return mMaterialTexture;
|
||||
assert(mMaterialLibrary->IsFinalized());
|
||||
mMaterialTextureSize = mMaterialLibrary->GetTextureSize();
|
||||
mMaterialTexture = mMaterialLibrary->GetTexture();
|
||||
mMaxTextureIndex = mMaterialLibrary->GetMaxTextureIndex();
|
||||
return mMaterialTexture;
|
||||
}
|
||||
|
||||
unsigned GetMaterialTextureSize() override
|
||||
{
|
||||
GetMaterialTexture();
|
||||
return mMaterialTextureSize;
|
||||
}
|
||||
|
||||
unsigned8 GetMaterialTextureChannelsPerPixel() override { return channelsPerPixel; }
|
||||
|
||||
bool HasAdditionalPool() const override { return true; }
|
||||
protected:
|
||||
void AppendPostProcess(glm::uvec3 coordinates, unsigned8 level, Tree<MultiRootMaterialNode<BitsMaterial<8>>>* tree) override
|
||||
{
|
||||
MaterialLibraryMultiRoot<T, Comparer, channelsPerPixel>* other = (MaterialLibraryMultiRoot<T, Comparer, channelsPerPixel>*)tree;
|
||||
if (other->mMaterialLibrary != NULL)
|
||||
{
|
||||
// Copy the material library of the appended tree
|
||||
if ((!this->mMaterialLibrary->IsFinalized()) && (*(other->mMaterialLibrary)) != (*(this->mMaterialLibrary)))
|
||||
{
|
||||
// Use copy constructor to copy the library of the other tree
|
||||
delete mMaterialLibrary;
|
||||
this->mMaterialLibrary = new MaterialLibrary<T, Comparer, channelsPerPixel>(*(other->mMaterialLibrary));
|
||||
}
|
||||
|
||||
assert((*(this->mMaterialLibrary)) == (*(other->mMaterialLibrary)));
|
||||
}
|
||||
}
|
||||
|
||||
void WriteProperties(std::ostream& file) override {
|
||||
WriteMaterialTexture(file);
|
||||
HierarchicalMaterialMultiRoot<BitsMaterial<8>>::WriteProperties(file);
|
||||
}
|
||||
void ReadProperties(std::istream& file) override {
|
||||
// Reat the material texture
|
||||
ReadMaterialTexture(file);
|
||||
// Restore the material library from the texture
|
||||
if (mMaterialLibrary != NULL)
|
||||
delete mMaterialLibrary;
|
||||
mMaterialLibrary = new MaterialLibrary<T, Comparer, channelsPerPixel>(mMaterialTexture, mMaterialTextureSize, mMaxTextureIndex);
|
||||
mMaterialLibrary->Finalize();
|
||||
|
||||
HierarchicalMaterialMultiRoot<BitsMaterial<8>>::ReadProperties(file);
|
||||
}
|
||||
void WriteAdditionalPoolProperties(std::ostream& file) override { WriteMaterialTexture(file); HierarchicalMaterialMultiRoot<BitsMaterial<8>>::WriteAdditionalPoolProperties(file); }
|
||||
void ReadAdditionalPoolProperties(std::istream& file) override { ReadMaterialTexture(file); HierarchicalMaterialMultiRoot<BitsMaterial<8>>::ReadAdditionalPoolProperties(file); }
|
||||
};
|
||||
385
Research/scene/Octree/MaterialLibraryTree.h
Normal file
385
Research/scene/Octree/MaterialLibraryTree.h
Normal file
@@ -0,0 +1,385 @@
|
||||
#pragma once
|
||||
#include "Tree.h"
|
||||
#include "MaterialNode.h"
|
||||
#include "IMaterialTexture.h"
|
||||
#include "../../inc/tbb/parallel_for_each.h"
|
||||
#include "../../inc/tbb/concurrent_queue.h"
|
||||
#include "../Material/MaterialLibrary.h"
|
||||
#include "../../core/BitHelper.h"
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
typedef MaterialNode<MaterialLibraryPointer> MaterialLibraryNode;
|
||||
|
||||
template<typename T, typename Comparer = std::less<T>, unsigned8 channelsPerPixel = 3>
|
||||
class MaterialLibraryTree : public Tree<MaterialLibraryNode>, public IMaterialTexture
|
||||
{
|
||||
protected:
|
||||
MaterialLibrary<T, Comparer, channelsPerPixel>* mMaterialLibrary;
|
||||
bool mOwnsLibrary;
|
||||
std::vector<unsigned char> mMaterialTexture;
|
||||
MaterialLibraryPointer mMaxTextureIndex;
|
||||
unsigned short mMaterialTextureSize;
|
||||
|
||||
std::unordered_map<T, MaterialLibraryNode*> leafMap;
|
||||
|
||||
inline void WriteMaterialTexture(std::ostream& file)
|
||||
{
|
||||
assert(mMaterialLibrary != NULL);
|
||||
mMaterialLibrary->Serialize(file);
|
||||
}
|
||||
|
||||
inline void ReadMaterialTexture(std::istream& file)
|
||||
{
|
||||
if (mMaterialLibrary == NULL)
|
||||
mMaterialLibrary = new MaterialLibrary<T, Comparer, channelsPerPixel>();
|
||||
mMaterialLibrary->Deserialize(file);
|
||||
GetMaterialTexture();
|
||||
}
|
||||
|
||||
inline void CreateAllMaterialLeafs()
|
||||
{
|
||||
std::vector<std::pair<T, MaterialLibraryPointer>> materialsAndIndices = mMaterialLibrary->GetMaterialTextureIndices();
|
||||
for (auto materialAndIndex : materialsAndIndices)
|
||||
{
|
||||
T material = materialAndIndex.first;
|
||||
MaterialLibraryPointer textureIndex = materialAndIndex.second;
|
||||
MaterialLibraryNode* leaf = (MaterialLibraryNode*)Create(GetMaxLevel());
|
||||
leaf->SetMaterial(textureIndex);
|
||||
leafMap.insert(std::pair<T, MaterialLibraryNode*>(material, leaf));
|
||||
}
|
||||
}
|
||||
public:
|
||||
// Creates a material library tree.
|
||||
MaterialLibraryTree(unsigned8 maxLevel) :
|
||||
Tree<MaterialLibraryNode>(maxLevel),
|
||||
mOwnsLibrary(true),
|
||||
mMaterialLibrary(new MaterialLibrary<T, Comparer, channelsPerPixel>())
|
||||
{
|
||||
mLeafsAreEqual = false;
|
||||
}
|
||||
|
||||
// Creates a material library tree with the given (finalized!) material library. The library is not owned by this tree and therefore will not
|
||||
// be deleted when this tree is deleted.
|
||||
MaterialLibraryTree(unsigned8 maxLevel, MaterialLibrary<T, Comparer, channelsPerPixel>* materialLibrary) :
|
||||
Tree<MaterialLibraryNode>(maxLevel),
|
||||
mOwnsLibrary(false),
|
||||
mMaterialLibrary(materialLibrary)
|
||||
{
|
||||
mLeafsAreEqual = false;
|
||||
CreateAllMaterialLeafs();
|
||||
}
|
||||
|
||||
~MaterialLibraryTree() override {
|
||||
if(mOwnsLibrary)
|
||||
delete mMaterialLibrary;
|
||||
}
|
||||
|
||||
MaterialLibrary<T, Comparer, channelsPerPixel>* GetMaterialLibrary()
|
||||
{
|
||||
return mMaterialLibrary;
|
||||
}
|
||||
|
||||
static void PassLibraryOwnership(MaterialLibraryTree* tree1, MaterialLibraryTree* tree2)
|
||||
{
|
||||
tree1->mOwnsLibrary = false;
|
||||
tree2->mOwnsLibrary = true;
|
||||
}
|
||||
|
||||
void AddMaterial(T material)
|
||||
{
|
||||
if (mMaterialLibrary->IsFinalized())
|
||||
return;
|
||||
mMaterialLibrary->AddMaterial(material);
|
||||
}
|
||||
|
||||
void FinalizeMaterials()
|
||||
{
|
||||
mMaterialLibrary->Finalize();
|
||||
CreateAllMaterialLeafs();
|
||||
}
|
||||
|
||||
// Assuming all leaf nodes contain pointers to materials, propagates those materials up in the tree, leaving the average material everywhere.
|
||||
// It is advised to call this method after DAG conversion, as it is still valid at that point, and it will be cheaper.
|
||||
void PropagateMaterials()
|
||||
{
|
||||
// Bottom up go through the nodes to propagate the materials
|
||||
auto levelIndices = SortOnLevel();
|
||||
|
||||
// Set node weights for weighted average calculation
|
||||
std::vector<float> lastLevelNodeWeights;
|
||||
std::vector<T> perfectMaterialPerNode(GetNodeCount());
|
||||
std::vector<float> levelNodeWeights;
|
||||
|
||||
auto leafStart = levelIndices[GetMaxLevel()];
|
||||
auto leafEnd = levelIndices[GetMaxLevel() + 1];
|
||||
lastLevelNodeWeights.resize(leafEnd - leafStart, 1.f);
|
||||
// Initialize the vectors for leaf nodes, assuming leaf nodes have weight 1 and perfect materials
|
||||
for (auto i = leafStart; i != leafEnd; i++)
|
||||
perfectMaterialPerNode[i] = mMaterialLibrary->GetMaterial(GetTypedNode(i)->GetMaterial());
|
||||
|
||||
// Bottom-up calculate the weighted average material for each node in the tree, and store them in perfectMaterialPerNode
|
||||
for (unsigned8 level = GetMaxLevel(); level-- > 0;)
|
||||
{
|
||||
unsigned32 levelStart = levelIndices[level];
|
||||
unsigned32 levelEnd = levelIndices[level + 1];
|
||||
levelNodeWeights = std::vector<float>(levelEnd - levelStart);
|
||||
tbb::parallel_for(levelStart, levelEnd, [&](unsigned32 i)
|
||||
{
|
||||
// Get the current node
|
||||
MaterialLibraryNode* node = GetTypedNode(i);
|
||||
std::vector<T> childMaterials;
|
||||
std::vector<float> childWeights;
|
||||
float nodeWeight = 0;
|
||||
// Find all materials the children use
|
||||
unsigned32* children = node->GetChildren();
|
||||
unsigned8 childCount = node->GetChildCount();
|
||||
for (ChildIndex c = 0; c < childCount; c++)
|
||||
{
|
||||
unsigned32 i = children[c];
|
||||
MaterialLibraryNode* child = GetTypedNode(i);
|
||||
MaterialLibraryPointer matPtr = child->GetMaterial();
|
||||
float childWeight = lastLevelNodeWeights[i - levelEnd];
|
||||
childMaterials.push_back(perfectMaterialPerNode[i]);
|
||||
childWeights.push_back(childWeight);
|
||||
nodeWeight += childWeight;
|
||||
}
|
||||
// Calculate the average material and retrieve the closest material to that from the library
|
||||
T nodeMaterial = T::WeightedAverage(childMaterials, childWeights);
|
||||
// Store the weighted average in perfectMaterialPerNode
|
||||
perfectMaterialPerNode[i] = nodeMaterial;
|
||||
levelNodeWeights[i - levelStart] = nodeWeight;
|
||||
});
|
||||
// Update the last level node weights to the node weights of the current level
|
||||
lastLevelNodeWeights = std::move(levelNodeWeights);
|
||||
}
|
||||
|
||||
// Find all materials required for this scene
|
||||
std::vector<T> perfectMaterials(GetNodeCount());
|
||||
tbb::parallel_for((size_t)0, perfectMaterialPerNode.size(), [&](size_t i)
|
||||
{
|
||||
perfectMaterials[i] = perfectMaterialPerNode[i];
|
||||
});
|
||||
|
||||
// Reduce the materials by only using unique ones
|
||||
tbb::parallel_sort(perfectMaterials, Comparer());
|
||||
perfectMaterials.erase(std::unique(perfectMaterials.begin(), perfectMaterials.end()), perfectMaterials.end());
|
||||
perfectMaterials.shrink_to_fit();
|
||||
|
||||
// Create a new material library based on these materials
|
||||
if (mOwnsLibrary) delete mMaterialLibrary;
|
||||
mMaterialTexture.clear();
|
||||
mMaterialLibrary = new MaterialLibrary<T, Comparer, channelsPerPixel>();
|
||||
for (auto material : perfectMaterials)
|
||||
mMaterialLibrary->AddMaterial(material);
|
||||
mMaterialLibrary->Finalize();
|
||||
|
||||
// Update all nodes in the tree to point to the new material library
|
||||
tbb::parallel_for((unsigned32)0, GetNodeCount(), [&](unsigned32 i)
|
||||
{
|
||||
MaterialLibraryPointer materialPointer = mMaterialLibrary->GetTextureIndex(perfectMaterialPerNode[i]);
|
||||
auto node = GetTypedNode(i);
|
||||
node->SetMaterial(materialPointer);
|
||||
});
|
||||
|
||||
// Update the material texture
|
||||
GetMaterialTexture();
|
||||
}
|
||||
|
||||
std::vector<unsigned8> GetMaterialTexture() override
|
||||
{
|
||||
if (!mMaterialTexture.empty())
|
||||
return mMaterialTexture;
|
||||
assert(mMaterialLibrary->IsFinalized());
|
||||
mMaterialTextureSize = mMaterialLibrary->GetTextureSize();
|
||||
mMaterialTexture = mMaterialLibrary->GetTexture();
|
||||
mMaxTextureIndex = mMaterialLibrary->GetMaxTextureIndex();
|
||||
return mMaterialTexture;
|
||||
}
|
||||
|
||||
unsigned GetMaterialTextureSize() override
|
||||
{
|
||||
GetMaterialTexture();
|
||||
return mMaterialTextureSize;
|
||||
}
|
||||
|
||||
unsigned8 GetMaterialTextureChannelsPerPixel() override { return channelsPerPixel; }
|
||||
|
||||
std::vector<T> GetLeafMaterials()
|
||||
{
|
||||
auto levelIndices = SortOnLevel();
|
||||
unsigned32 leafsStart = levelIndices[GetMaxLevel()];
|
||||
unsigned32 leafsEnd = levelIndices[GetMaxLevel() + 1];
|
||||
std::vector<T> leafMaterials(levelIndices[GetMaxLevel() + 1] - levelIndices[GetMaxLevel()]);
|
||||
tbb::parallel_for(leafsStart, leafsEnd, [&](const unsigned32 i)
|
||||
{
|
||||
leafMaterials[i - leafsStart] = GetMaterial(GetTypedNode(i));
|
||||
});
|
||||
return leafMaterials;
|
||||
}
|
||||
|
||||
// Replaces the current leaf materials (and thereby also the material library) by the given material.
|
||||
// In this, it assumes the leafMaterials list is ordered in the same way the leafs are ordered in the current tree.
|
||||
// It is advised to follow this command by "ToDAG()" and "PropagateMaterials()"
|
||||
void SetLeafMaterials(const std::vector<T> leafMaterials)
|
||||
{
|
||||
// Create the new material library
|
||||
delete mMaterialLibrary;
|
||||
mMaterialLibrary = new MaterialLibrary<T, Comparer, channelsPerPixel>();
|
||||
for (auto material : leafMaterials)
|
||||
mMaterialLibrary->AddMaterial(material);
|
||||
mMaterialLibrary->Finalize();
|
||||
|
||||
// Set the leaf node materials
|
||||
size_t i = 0;
|
||||
for (unsigned32 i = 0; i < GetNodeCount(); i++)
|
||||
MaterialLibraryNode* node = GetTypedNode(i);
|
||||
if (node->GetLevel() == GetMaxLevel())
|
||||
{
|
||||
node->SetMaterial(mMaterialLibrary->GetTextureIndex(leafMaterials[i]));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
T GetMaterial(const MaterialLibraryNode* node) const
|
||||
{
|
||||
assert(mMaterialLibrary->IsFinalized());
|
||||
return mMaterialLibrary->GetMaterial(node->GetMaterial());
|
||||
}
|
||||
|
||||
std::vector<T> GetMaterials() const
|
||||
{
|
||||
// Read all materials from all nodes
|
||||
std::vector<T> materials(GetNodeCount());
|
||||
//for (unsigned32 i = 0; i < GetNodeCount(); i++)
|
||||
tbb::parallel_for((unsigned32)0, GetNodeCount(), [&](const unsigned32 i)
|
||||
{
|
||||
materials[i] = GetMaterial(GetTypedNode(i));
|
||||
});
|
||||
return materials;
|
||||
}
|
||||
|
||||
void SetMaterials(const std::vector<T>& materials)
|
||||
{
|
||||
// Not a valid material vector
|
||||
if (materials.size() != GetNodeCount())
|
||||
return;
|
||||
|
||||
// Create a new material library containing the new materials
|
||||
if (mOwnsLibrary) delete mMaterialLibrary;
|
||||
mMaterialLibrary = new MaterialLibrary<T, Comparer, channelsPerPixel>();
|
||||
for (auto material : materials)
|
||||
{
|
||||
mMaterialLibrary->AddMaterial(material);
|
||||
}
|
||||
mMaterialLibrary->Finalize();
|
||||
mMaterialTexture.clear();
|
||||
|
||||
// Update all nodes to point to this new library
|
||||
tbb::parallel_for((unsigned32)0, GetNodeCount(), [&](unsigned32 i)
|
||||
{
|
||||
MaterialLibraryNode* node = GetTypedNode(i);
|
||||
node->SetMaterial(mMaterialLibrary->GetTextureIndex(materials[i]));
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<T> GetUniqueMaterials() const
|
||||
{
|
||||
return mMaterialLibrary->GetMaterials();
|
||||
}
|
||||
|
||||
// Clears all material pointers from non-leaf nodes
|
||||
void ClearPropagation()
|
||||
{
|
||||
auto levelIndices = SortOnLevel();
|
||||
tbb::parallel_for((unsigned32)0, levelIndices[GetMaxLevel()], [&](const unsigned32 i)
|
||||
{
|
||||
MaterialLibraryNode* node = GetTypedNode(i);
|
||||
node->SetMaterial(MaterialLibraryPointer(0));
|
||||
});
|
||||
}
|
||||
|
||||
// Replaces the leaf materials by the given leaf materials. It is advised to follow this command by "ToDAG()" and "PropagateMaterials()"
|
||||
void ReplaceLeafMaterials(const std::map<T, T, Comparer>& leafMaterialReplacers)
|
||||
{
|
||||
auto newMaterialLibrary = new MaterialLibrary<T, Comparer, channelsPerPixel>();
|
||||
for (auto material : leafMaterialReplacers)
|
||||
newMaterialLibrary->AddMaterial(material.second);
|
||||
newMaterialLibrary->Finalize();
|
||||
std::unordered_map<MaterialLibraryPointer, MaterialLibraryPointer> materialLibraryPointerReplacers;
|
||||
for (auto material : leafMaterialReplacers)
|
||||
materialLibraryPointerReplacers.insert(std::make_pair(mMaterialLibrary->GetTextureIndex(material.first), newMaterialLibrary->GetTextureIndex(material.second)));
|
||||
|
||||
auto levelIndices = SortOnLevel();
|
||||
tbb::parallel_for(levelIndices[GetMaxLevel()], levelIndices[GetMaxLevel() + 1], [&](const unsigned32 i)
|
||||
{
|
||||
MaterialLibraryNode* node = GetTypedNode(i);
|
||||
node->SetMaterial(materialLibraryPointerReplacers[node->GetMaterial()]);
|
||||
});
|
||||
delete mMaterialLibrary;
|
||||
mMaterialLibrary = newMaterialLibrary;
|
||||
}
|
||||
|
||||
unsigned8 GetAdditionalBytesPerNode(unsigned8 level) const override
|
||||
{
|
||||
return 3; // For now, assume that material pointers are always 12+12 bits
|
||||
}
|
||||
std::vector<unsigned8> GetAdditionalNodeBytes(const Node* node) const override
|
||||
{
|
||||
MaterialLibraryPointer materialPointer = ((MaterialLibraryNode*)node)->GetMaterial();
|
||||
std::vector<unsigned8> res(3);
|
||||
res[0] = (unsigned8)(materialPointer.x >> 4);
|
||||
res[1] = (unsigned8)((materialPointer.x << 4) | ((materialPointer.y & (0x0FFF)) >> 8));
|
||||
res[2] = (unsigned8)materialPointer.y;
|
||||
return res;
|
||||
}
|
||||
|
||||
void AddLeafNode(glm::uvec3 coordinate, T material)
|
||||
{
|
||||
// Get the material pointer
|
||||
assert(mMaterialLibrary->IsFinalized());
|
||||
// Check if there is already a leaf for this material (for auto reuse)
|
||||
auto existingLeaf = leafMap.find(material);
|
||||
if (existingLeaf != leafMap.end())
|
||||
{
|
||||
// If the leaf node already exists, reuse it:
|
||||
// Create the parent node of the leaf
|
||||
MaterialLibraryNode* parentOfLeaf = Tree::AddNode(glm::uvec3(coordinate.x >> 1, coordinate.y >> 1, coordinate.z >> 1), GetMaxLevel() - 1);
|
||||
// The last bit of the coordinate can be used to find the childindex:
|
||||
ChildIndex index = (((coordinate.x & 1) == 1) ? 1 : 0)
|
||||
+ (((coordinate.y & 1) == 1) ? 2 : 0)
|
||||
+ (((coordinate.z & 1) == 1) ? 4 : 0);
|
||||
|
||||
parentOfLeaf->SetChild(index, existingLeaf->second);
|
||||
}
|
||||
else
|
||||
{
|
||||
MaterialLibraryPointer textureIndex = mMaterialLibrary->GetTextureIndex(material);
|
||||
MaterialLibraryNode* leaf = Tree::AddLeafNode(coordinate);
|
||||
leaf->SetMaterial(textureIndex);
|
||||
leafMap.insert(std::pair<T, MaterialLibraryNode*>(material, leaf));
|
||||
}
|
||||
}
|
||||
|
||||
bool HasAdditionalPool() const override { return true; }
|
||||
protected:
|
||||
void WriteProperties(std::ostream& file) override {
|
||||
// Write the material texture
|
||||
WriteMaterialTexture(file);
|
||||
}
|
||||
void ReadProperties(std::istream& file) override {
|
||||
// Reat the material texture
|
||||
ReadMaterialTexture(file);
|
||||
// Restore the material library from the texture
|
||||
delete mMaterialLibrary;
|
||||
mMaterialLibrary = new MaterialLibrary<T, Comparer, channelsPerPixel>(mMaterialTexture, mMaterialTextureSize, mMaxTextureIndex);
|
||||
mMaterialLibrary->Finalize();
|
||||
|
||||
//// Refresh the material texture for debug purposes
|
||||
//mMaterialTexture.clear();
|
||||
//GetMaterialTexture();
|
||||
}
|
||||
void WriteAdditionalPoolProperties(std::ostream& file) override { WriteMaterialTexture(file); }
|
||||
void ReadAdditionalPoolProperties(std::istream& file) override { ReadMaterialTexture(file); }
|
||||
};
|
||||
337
Research/scene/Octree/MaterialLibraryUniqueIndexTree.h
Normal file
337
Research/scene/Octree/MaterialLibraryUniqueIndexTree.h
Normal file
@@ -0,0 +1,337 @@
|
||||
#pragma once
|
||||
#include "UniqueIndexTree.h"
|
||||
#include "MaterialTree.h"
|
||||
#include "IMaterialTexture.h"
|
||||
#include "../../inc/tbb/parallel_for_each.h"
|
||||
#include "../Material/MaterialLibrary.h"
|
||||
#include "../../core/BitHelper.h"
|
||||
#include "../../core/Serializer.h"
|
||||
#include "../../core/Util/BoolArray.h"
|
||||
#include "../../inc/lodepng/lodepng.h"
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
#include <stack>
|
||||
|
||||
//#define PRINT_ERROR_DATA
|
||||
|
||||
template<typename T, typename Comparer = std::less<T>, unsigned8 channelsPerPixel = 3>
|
||||
class MaterialLibraryUniqueIndexTree : public UniqueIndexTree<MaterialLibraryPointer>, public IMaterialTexture
|
||||
{
|
||||
private:
|
||||
// After the tree is finalized, the material library will be used to contain the actual materials
|
||||
MaterialLibrary<T, Comparer, channelsPerPixel>* mMaterialLibrary;
|
||||
|
||||
std::vector<unsigned8> mMaterialTexture;
|
||||
unsigned16 mMaterialTextureSize;
|
||||
MaterialLibraryPointer mMaxTextureIndex;
|
||||
|
||||
inline void WriteMaterialTexture(std::ostream& file)
|
||||
{
|
||||
assert(mMaterialLibrary != NULL);
|
||||
mMaterialLibrary->Serialize(file);
|
||||
}
|
||||
|
||||
inline void ReadMaterialTexture(std::istream& file)
|
||||
{
|
||||
if (mMaterialLibrary == NULL)
|
||||
mMaterialLibrary = new MaterialLibrary<T, Comparer, channelsPerPixel>();
|
||||
mMaterialLibrary->Deserialize(file);
|
||||
GetMaterialTexture();
|
||||
}
|
||||
|
||||
public:
|
||||
MaterialLibraryUniqueIndexTree(unsigned8 maxLevel, CompressedTexture<MaterialLibraryPointer>* nodeMaterialsTexture, unsigned32 collapsedMaterialLevels) :
|
||||
UniqueIndexTree(maxLevel, nodeMaterialsTexture, collapsedMaterialLevels),
|
||||
mMaterialLibrary(NULL),
|
||||
mMaterialTexture(std::vector<unsigned8>()),
|
||||
mMaterialTextureSize(0),
|
||||
mMaxTextureIndex(MaterialLibraryPointer(0))
|
||||
{}
|
||||
|
||||
// Creates a UniqueIndexRoot with "maxLevel" levels.
|
||||
// Note that the nodeMaterialsTexture will be deleted if the tree is deleted.
|
||||
MaterialLibraryUniqueIndexTree(unsigned8 maxLevel, CompressedTexture<MaterialLibraryPointer>* nodeMaterialsTexture)
|
||||
: MaterialLibraryUniqueIndexTree(maxLevel, nodeMaterialsTexture, 0)
|
||||
{}
|
||||
|
||||
~MaterialLibraryUniqueIndexTree() override {
|
||||
if(mMaterialLibrary != NULL)
|
||||
delete mMaterialLibrary;
|
||||
}
|
||||
|
||||
void AppendPostProcess(glm::uvec3 coordinates, unsigned8 level, Tree* tree) override
|
||||
{
|
||||
MaterialLibraryUniqueIndexTree<T, Comparer, channelsPerPixel>* uniqueIndexTree = (MaterialLibraryUniqueIndexTree<T, Comparer, channelsPerPixel>*)tree;
|
||||
|
||||
// Copy the color information from the blocks of the other tree. Make sure that the blocks are inserted in the correct position.
|
||||
auto oldLibrary = mMaterialLibrary;
|
||||
auto existingMaterials = GetUniqueMaterials();
|
||||
mMaterialLibrary = new MaterialLibrary<T, Comparer, channelsPerPixel>();
|
||||
for (auto material : existingMaterials)
|
||||
mMaterialLibrary->AddMaterial(material);
|
||||
auto appendedLibrary = uniqueIndexTree->GetMaterialLibrary();
|
||||
auto appendedMaterials = uniqueIndexTree->GetUniqueMaterials();
|
||||
for (auto material : appendedMaterials)
|
||||
mMaterialLibrary->AddMaterial(material);
|
||||
mMaterialLibrary->Finalize();
|
||||
mMaterialTexture = std::vector<unsigned char>();
|
||||
mMaterialTextureSize = 0;
|
||||
mMaxTextureIndex = mMaterialLibrary->GetMaxTextureIndex();
|
||||
|
||||
// Replace the existing materials
|
||||
std::unordered_map<MaterialLibraryPointer, MaterialLibraryPointer> ownPointerReplacers;
|
||||
for (auto material : existingMaterials)
|
||||
ownPointerReplacers[oldLibrary->GetTextureIndex(material)] = mMaterialLibrary->GetTextureIndex(material);
|
||||
UniqueIndexTree::ReplaceMaterials(ownPointerReplacers);
|
||||
delete oldLibrary;
|
||||
|
||||
// Create a map from the old tree to the new tree replacers:
|
||||
std::unordered_map<MaterialLibraryPointer, MaterialLibraryPointer> appendedPointerReplacers;
|
||||
for (auto material : appendedMaterials)
|
||||
appendedPointerReplacers[appendedLibrary->GetTextureIndex(material)] = mMaterialLibrary->GetTextureIndex(material);
|
||||
|
||||
UniqueIndexTree::AppendPostProcess(coordinates, level, uniqueIndexTree, appendedPointerReplacers);
|
||||
}
|
||||
|
||||
void ReplaceMaterials(const std::unordered_map<T, T>& replacementMap)
|
||||
{
|
||||
assert(mMaterialLibrary != NULL);
|
||||
auto oldLibrary = mMaterialLibrary;
|
||||
std::vector<T> oldMaterials = oldLibrary->GetMaterials();
|
||||
mMaterialLibrary = new MaterialLibrary<T, Comparer, channelsPerPixel>();
|
||||
std::unordered_map<T, T> actualReplacers;
|
||||
for (auto material : oldMaterials)
|
||||
{
|
||||
auto replacerIt = replacementMap.find(material);
|
||||
T replacer = material;
|
||||
if (replacerIt == replacementMap.end())
|
||||
printf("Material not found in replacementMap");
|
||||
else
|
||||
replacer = replacerIt->second;
|
||||
mMaterialLibrary->AddMaterial(replacer);
|
||||
actualReplacers.insert(std::pair<T, T>(material, replacer));
|
||||
}
|
||||
mMaterialLibrary->Finalize();
|
||||
|
||||
std::unordered_map<MaterialLibraryPointer, MaterialLibraryPointer> pointerReplacers;
|
||||
for (auto material : oldMaterials)
|
||||
{
|
||||
auto oldPointer = oldLibrary->GetTextureIndex(material);
|
||||
auto newPointer = mMaterialLibrary->GetTextureIndex(actualReplacers[material]);
|
||||
pointerReplacers.insert(std::pair<MaterialLibraryPointer, MaterialLibraryPointer>(oldPointer, newPointer));
|
||||
}
|
||||
|
||||
UniqueIndexTree::ReplaceMaterials(pointerReplacers);
|
||||
|
||||
delete oldLibrary;
|
||||
}
|
||||
|
||||
// Will build a unique index tree with the same material information as the given material tree.
|
||||
// This will consume (and delete) the given tree!
|
||||
template<typename MaterialTree>
|
||||
void BaseOn(MaterialTree* tree)
|
||||
{
|
||||
// Create a material library for all original materials
|
||||
assert(mMaterialLibrary == NULL);
|
||||
mMaterialLibrary = new MaterialLibrary<T, Comparer, channelsPerPixel>();
|
||||
std::vector<T> uniqueMaterials = tree->GetUniqueMaterials();
|
||||
for (auto material : uniqueMaterials)
|
||||
mMaterialLibrary->AddMaterial(material);
|
||||
mMaterialLibrary->Finalize();
|
||||
|
||||
// Create a new material tree containing the material pointers:
|
||||
size_t nodeCount = tree->GetNodeCount();
|
||||
std::vector<MaterialLibraryPointer> pointers(nodeCount);
|
||||
tbb::parallel_for(size_t(0), nodeCount, [&](size_t i)
|
||||
{
|
||||
pointers[i] = mMaterialLibrary->GetTextureIndex(tree->GetMaterial(tree->GetTypedNode((unsigned32)i)));
|
||||
});
|
||||
|
||||
UniqueIndexTree::BaseOn(tree, pointers);
|
||||
}
|
||||
|
||||
// Returns a list with all unique materials in the tree
|
||||
std::vector<T> GetUniqueMaterials() const
|
||||
{
|
||||
return mMaterialLibrary->GetMaterials();
|
||||
}
|
||||
|
||||
MaterialLibrary<T, Comparer, channelsPerPixel>* GetMaterialLibrary() const
|
||||
{
|
||||
return mMaterialLibrary;
|
||||
}
|
||||
|
||||
// Returns the material of the node at index i
|
||||
T GetMaterial(const size_t& i) const
|
||||
{
|
||||
return mMaterialLibrary->GetMaterial(GetNodeValue(i));
|
||||
}
|
||||
|
||||
std::vector<T> GetMaterials(size_t fromIndex = 0) const
|
||||
{
|
||||
std::vector<MaterialLibraryPointer> pointersTexture = GetNodeValues(fromIndex);
|
||||
std::vector<T> res(pointersTexture.size());
|
||||
tbb::parallel_for(size_t(0), res.size(), [&](size_t i)
|
||||
{
|
||||
res[i] = mMaterialLibrary->GetMaterial(pointersTexture[i]);
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
// Returns the texture containing all materials once
|
||||
std::vector<unsigned8> GetMaterialTexture() override
|
||||
{
|
||||
if (!mMaterialTexture.empty())
|
||||
return mMaterialTexture;
|
||||
assert(mMaterialLibrary->IsFinalized());
|
||||
mMaterialTextureSize = mMaterialLibrary->GetTextureSize();
|
||||
mMaterialTexture = mMaterialLibrary->GetTexture();
|
||||
mMaxTextureIndex = mMaterialLibrary->GetMaxTextureIndex();
|
||||
return mMaterialTexture;
|
||||
}
|
||||
|
||||
unsigned GetMaterialTextureSize() override
|
||||
{
|
||||
GetMaterialTexture();
|
||||
return mMaterialTextureSize;
|
||||
}
|
||||
|
||||
void PrintDebugInfo() const override
|
||||
{
|
||||
UniqueIndexTree::PrintDebugInfo();
|
||||
}
|
||||
|
||||
unsigned8 GetMaterialTextureChannelsPerPixel() override { return channelsPerPixel; }
|
||||
protected:
|
||||
void WriteAdditionalUniqueIndexTreeProperties(std::ostream& file) override {
|
||||
WriteMaterialTexture(file);
|
||||
}
|
||||
|
||||
void ReadAdditionalUniqueIndexTreeProperties(std::istream& file) override {
|
||||
ReadMaterialTexture(file);
|
||||
}
|
||||
|
||||
void WriteAdditionalPoolProperties(std::ostream& file) override { WriteMaterialTexture(file); UniqueIndexTree::WriteAdditionalPoolProperties(file); }
|
||||
void ReadAdditionalPoolProperties(std::istream& file) override { ReadMaterialTexture(file); UniqueIndexTree::ReadAdditionalPoolProperties(file); }
|
||||
};
|
||||
|
||||
#ifdef PRINT_ERROR_DATA
|
||||
#include "../../core/OctreeBuilder/ColorQuantizerFactory.h"
|
||||
#include "../Material/MaterialQuantizer/ColorQuantizer/BaseColorQuantizer.h"
|
||||
|
||||
struct ColorErrorInfo
|
||||
{
|
||||
glm::u64vec3 sumError = glm::u64vec3(0);
|
||||
unsigned maxError = 0;
|
||||
glm::uvec3 maxErrorValue = glm::uvec3(0);
|
||||
long double sumDeltaE = 0;
|
||||
float maxDeltaE = 0;
|
||||
};
|
||||
|
||||
template<>
|
||||
void MaterialLibraryUniqueIndexTree<Color, ColorCompare>::PrintDebugInfo() const
|
||||
{
|
||||
// If the colors are not quantized, print some information
|
||||
std::vector<Color> uniqueMaterials = mMaterialLibrary->GetMaterials();
|
||||
printf("Calculating quantization errors over the full scene (%u unique colors, %llu values)...\n", (unsigned32)uniqueMaterials.size(), (unsigned64)this->GetMaterialCount());
|
||||
std::string quantizationTypes[4] = { "lab256", "lab1024", "lab4096", "lab16384" };
|
||||
for (std::string type : quantizationTypes)
|
||||
{
|
||||
printf("%s: ", type.c_str());
|
||||
BaseColorQuantizer* quantizer = ColorQuantizerFactory::Create(type);
|
||||
auto quantizationMap = quantizer->QuantizeMaterials(uniqueMaterials);
|
||||
|
||||
//glm::dvec3 sumError(0);
|
||||
//unsigned maxError = 0;
|
||||
//glm::uvec3 maxErrorValue(0);
|
||||
//long double sumDeltaE = 0;
|
||||
//float maxDeltaE = 0;
|
||||
ColorErrorInfo initial;
|
||||
//tbb::parallel_for((size_t)0, this->GetMaterialCount(), [&](const size_t& i)
|
||||
//for (size_t i = 0; i < this->GetMaterialCount(); i++)
|
||||
ColorErrorInfo error = tbb::parallel_reduce(tbb::blocked_range<size_t>(0, this->GetMaterialCount()), initial, [&](const tbb::blocked_range<size_t>& r, ColorErrorInfo errorInfo)
|
||||
{
|
||||
for (size_t i = r.begin(); i != r.end(); i++)
|
||||
{
|
||||
const Color& actual = GetMaterial(i);
|
||||
const Color& quantized = quantizationMap->at(actual);
|
||||
glm::ivec3 error = glm::abs(glm::ivec3(actual.GetColor()) - glm::ivec3(quantized.GetColor()));
|
||||
errorInfo.sumError += error;
|
||||
unsigned errorU = error.r + error.g + error.b;
|
||||
if (errorU > errorInfo.maxError)
|
||||
{
|
||||
errorInfo.maxError = errorU;
|
||||
errorInfo.maxErrorValue = error;
|
||||
}
|
||||
float deltaE = ColorHelper::GetDeltaEFromRGB(actual.GetColor(), quantized.GetColor());
|
||||
if (deltaE == deltaE) // Only sum if it is not NaN...
|
||||
errorInfo.sumDeltaE += deltaE;
|
||||
if (deltaE > errorInfo.maxDeltaE) errorInfo.maxDeltaE = deltaE;
|
||||
}
|
||||
return errorInfo;
|
||||
}, [&](ColorErrorInfo a, ColorErrorInfo b)
|
||||
{
|
||||
ColorErrorInfo res;
|
||||
res.sumError = a.sumError + b.sumError;
|
||||
res.maxError = a.maxError > b.maxError ? a.maxError : b.maxError;
|
||||
res.maxErrorValue = a.maxError > b.maxError ? a.maxErrorValue : b.maxErrorValue;
|
||||
res.sumDeltaE = a.sumDeltaE + b.sumDeltaE;
|
||||
res.maxDeltaE = a.maxDeltaE > b.maxDeltaE ? a.maxDeltaE : b.maxDeltaE;
|
||||
return res;
|
||||
});
|
||||
//);
|
||||
glm::dvec3 sumError = glm::dvec3(error.sumError);
|
||||
glm::dvec3 meanError = sumError / double(GetMaterialCount());
|
||||
double meanDeltaE = error.sumDeltaE / double(GetMaterialCount());
|
||||
printf("Mean errors: (%f, %f, %f), Max errors: (%u, %u, %u), Mean delta-E: %f, Max delta-E: %f\n", meanError.x, meanError.y, meanError.z, error.maxErrorValue.x, error.maxErrorValue.y, error.maxErrorValue.z, meanDeltaE, error.maxDeltaE);
|
||||
|
||||
delete quantizationMap;
|
||||
}
|
||||
}
|
||||
|
||||
#include "../Material/MaterialQuantizer/NormalQuantizer.h"
|
||||
#include "../../inc/glm/gtx/vector_angle.hpp"
|
||||
|
||||
struct NormalErrorInfo
|
||||
{
|
||||
float maxAngle = 0;
|
||||
long double sumAngle = 0;
|
||||
};
|
||||
|
||||
template<>
|
||||
void MaterialLibraryUniqueIndexTree<SmallNormal, NormalCompare, 4>::PrintDebugInfo() const
|
||||
{
|
||||
// If the colors are not quantized, print some information
|
||||
std::vector<SmallNormal> uniqueMaterials = mMaterialLibrary->GetMaterials();
|
||||
printf("Calculating normal quantization errors over the full scene (%u unique normals, %llu values)...\n", (unsigned32)uniqueMaterials.size(), (unsigned64)this->GetMaterialCount());
|
||||
unsigned8 bits[] = { 12 };
|
||||
for (unsigned8 quantizeToBits : bits)
|
||||
{
|
||||
printf("%u bits: ", quantizeToBits);
|
||||
NormalQuantizer quantizer(quantizeToBits);
|
||||
|
||||
NormalErrorInfo initial;
|
||||
NormalErrorInfo error = tbb::parallel_reduce(tbb::blocked_range<size_t>(0, this->GetMaterialCount()), initial, [&](const tbb::blocked_range<size_t>& r, NormalErrorInfo errorInfo)
|
||||
{
|
||||
for (size_t i = r.begin(); i != r.end(); i++)
|
||||
{
|
||||
const SmallNormal& actual = GetMaterial(i);
|
||||
SmallNormal quantized = quantizer.Quantize(actual);
|
||||
float angle = glm::angle(actual.Get(), quantized.Get());
|
||||
if (angle > errorInfo.maxAngle) errorInfo.maxAngle = angle;
|
||||
errorInfo.sumAngle += angle;
|
||||
}
|
||||
return errorInfo;
|
||||
}, [&](NormalErrorInfo a, NormalErrorInfo b)
|
||||
{
|
||||
NormalErrorInfo res;
|
||||
res.maxAngle = a.maxAngle > b.maxAngle ? a.maxAngle : b.maxAngle;
|
||||
res.sumAngle = a.sumAngle + b.sumAngle;
|
||||
return res;
|
||||
});
|
||||
//);
|
||||
long double meanError = error.sumAngle / long double(GetMaterialCount());
|
||||
printf("Mean angle error: %f, Max angle error: %f\n", meanError * (360.0 / (2 * mPi)), error.maxAngle * (360.0 / (2 * mPi)));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
67
Research/scene/Octree/MaterialNode.h
Normal file
67
Research/scene/Octree/MaterialNode.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
#include <fstream>
|
||||
#include "Node.h"
|
||||
#include "BaseTree.h"
|
||||
#include "../../inc/glm/glm.hpp"
|
||||
#include "../../core/Defines.h"
|
||||
#include "../../core/Serializer.h"
|
||||
|
||||
template <typename T, typename Comparer = std::less<T>>
|
||||
class MaterialNode : public Node
|
||||
{
|
||||
private:
|
||||
T mMaterial;
|
||||
public:
|
||||
MaterialNode(BaseTree* root, unsigned8 level = 0) : Node(root, level) {}
|
||||
MaterialNode(BaseTree* root, T material, unsigned8 level = 0) : MaterialNode(root, level) { mMaterial = material; }
|
||||
//MaterialNode(const MaterialNode& node) : mMaterial(node.mMaterial), Node(node) {} // Copy ctor
|
||||
MaterialNode(MaterialNode&& node) : Node(std::move(node))
|
||||
{
|
||||
mMaterial = std::move(node.mMaterial);
|
||||
}
|
||||
~MaterialNode() {}
|
||||
|
||||
//MaterialNode& operator=(const MaterialNode& node)
|
||||
//{
|
||||
// mMaterial = node.mMaterial;
|
||||
// Node::operator=(node);
|
||||
// return *this;
|
||||
//}
|
||||
MaterialNode& operator=(MaterialNode&& node)
|
||||
{
|
||||
mMaterial = std::move(node.mMaterial);
|
||||
Node::operator=(std::move(node));
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
T GetMaterial() const { return mMaterial; }
|
||||
void SetMaterial(T material) { mMaterial = material; }
|
||||
|
||||
bool Compare(const MaterialNode& node) const
|
||||
{
|
||||
if (this->mMaterial != node.mMaterial) return Comparer()(this->mMaterial, node.mMaterial);
|
||||
return Node::Compare(node);
|
||||
}
|
||||
|
||||
bool Equals(const MaterialNode& node) const
|
||||
{
|
||||
if (this == &node) return true;
|
||||
return node.mMaterial == this->mMaterial && Node::Equals(node);
|
||||
}
|
||||
|
||||
void WriteProperties(std::ostream& file)
|
||||
{
|
||||
Serializer<T>::Serialize(mMaterial, file);
|
||||
}
|
||||
void ReadProperties(std::istream& file)
|
||||
{
|
||||
Serializer<T>::Deserialize(mMaterial, file);
|
||||
}
|
||||
|
||||
void CopyProperties(MaterialNode* source)
|
||||
{
|
||||
auto node = (MaterialNode<T, Comparer>*)source;
|
||||
this->SetMaterial(node->GetMaterial());
|
||||
}
|
||||
};
|
||||
199
Research/scene/Octree/MaterialTree.h
Normal file
199
Research/scene/Octree/MaterialTree.h
Normal file
@@ -0,0 +1,199 @@
|
||||
#pragma once
|
||||
#include "Tree.h"
|
||||
#include "MaterialNode.h"
|
||||
#include "../../inc/tbb/parallel_for_each.h"
|
||||
#include "../../core/BitHelper.h"
|
||||
#include "../../core/CollectionHelper.h"
|
||||
#include "../../core/Serializer.h"
|
||||
#include <unordered_map>
|
||||
//#include <map>
|
||||
#include <stack>
|
||||
|
||||
template<typename T, typename Comparer = std::less<T>>
|
||||
class MaterialTree : public Tree<MaterialNode<T, Comparer>>
|
||||
{
|
||||
private:
|
||||
std::unordered_map<T, Node*> mLeafMap;
|
||||
bool mUseLeafMap;
|
||||
public:
|
||||
// Creates a Material Tree with "maxLevel" levels.
|
||||
MaterialTree(unsigned8 maxLevel) : Tree<MaterialNode<T, Comparer>>(maxLevel), mUseLeafMap(false)
|
||||
{
|
||||
mLeafsAreEqual = false;
|
||||
}
|
||||
|
||||
~MaterialTree() override { }
|
||||
|
||||
// When using the leaf map, leaf nodes with the same material will only be created once (e.g. the leaf level is already a DAG)
|
||||
void UseLeafMap(bool value)
|
||||
{
|
||||
mUseLeafMap = value;
|
||||
|
||||
// Build the leaf map for the nodes that already exist
|
||||
if (mUseLeafMap)
|
||||
{
|
||||
for (unsigned32 i = 0; i < GetNodeCount(); i++)
|
||||
{
|
||||
MaterialNode<T, Comparer>* node = GetTypedNode(i);
|
||||
if (node->GetLevel() == GetMaxLevel())
|
||||
mLeafMap.insert(std::pair<T, Node*>(node->GetMaterial(), node));
|
||||
}
|
||||
// Convert the leaf nodes to a DAG
|
||||
ToDAG(GetMaxLevel() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Assuming all leaf nodes contain pointers to materials, propagates those materials up in the tree, leaving the average material everywhere.
|
||||
// It is advised to call this method after DAG conversion, as it is still valid at that point, and it will be cheaper.
|
||||
void PropagateMaterials(T(*Average)(const std::vector<T>& materials, const std::vector<float>& weights))
|
||||
{
|
||||
// Bottom up go through the nodes to propagate the materials
|
||||
auto levelIndices = SortOnLevel();
|
||||
// Calculate how many levels actually contain data
|
||||
unsigned8 filledLevels = 0;
|
||||
for (unsigned8 level = 0; level <= GetMaxLevel(); level++)
|
||||
{
|
||||
if (levelIndices[level] != levelIndices[level + 1]) filledLevels++;
|
||||
else break;
|
||||
}
|
||||
|
||||
// If the levelIndices.size() <= 2, that means there's only the root level, thus no propagation is needed
|
||||
if (filledLevels < 2) return;
|
||||
|
||||
// Set node weights for weighted average calculation
|
||||
std::vector<float> lastLevelNodeWeights;
|
||||
std::vector<float> levelNodeWeights;
|
||||
|
||||
// Bottom-up calculate the weighted average material for each node in the tree, and store them in perfectMaterialPerNode
|
||||
for (unsigned8 level = filledLevels - 1; level-- > 0;)
|
||||
{
|
||||
auto levelStart = levelIndices[level];
|
||||
auto levelEnd = levelIndices[level + 1];
|
||||
levelNodeWeights = std::vector<float>(levelEnd - levelStart);
|
||||
tbb::parallel_for(levelStart, levelEnd, [&](const unsigned32 i)
|
||||
{
|
||||
// Get the current node
|
||||
MaterialNode<T, Comparer>* node = GetTypedNode(i);
|
||||
std::vector<T> childMaterials;
|
||||
std::vector<float> childWeights;
|
||||
float nodeWeight = 0;
|
||||
// Find all materials the children use
|
||||
unsigned32* children = node->GetChildren();
|
||||
unsigned8 childCount = node->GetChildCount();
|
||||
for (ChildIndex c = 0; c < childCount; c++)
|
||||
{
|
||||
const unsigned32& i = children[c];
|
||||
MaterialNode<T, Comparer>* child = GetTypedNode(i);
|
||||
float childWeight = (i - levelEnd < lastLevelNodeWeights.size()) ? lastLevelNodeWeights[i - levelEnd] : 1.f;
|
||||
childMaterials.push_back(child->GetMaterial());
|
||||
childWeights.push_back(childWeight);
|
||||
nodeWeight += childWeight;
|
||||
}
|
||||
// Calculate the average material and retrieve the closest material to that from the library
|
||||
node->SetMaterial(Average(childMaterials, childWeights));
|
||||
// Store the weighted average in perfectMaterialPerNode
|
||||
levelNodeWeights[i - levelStart] = nodeWeight;
|
||||
});
|
||||
// Update the last level node weights to the node weights of the current level
|
||||
lastLevelNodeWeights = std::move(levelNodeWeights);
|
||||
}
|
||||
}
|
||||
|
||||
void ReplaceMaterials(const std::unordered_map<T, T>& replacementMap)
|
||||
{
|
||||
tbb::parallel_for((unsigned32)0, GetNodeCount(), [&](unsigned32 i)
|
||||
{
|
||||
auto node = GetTypedNode(i);
|
||||
node->SetMaterial(replacementMap.at(node->GetMaterial()));
|
||||
});
|
||||
}
|
||||
|
||||
void SetMaterials(std::vector<T> materials)
|
||||
{
|
||||
assert(materials.size() == GetNodeCount());
|
||||
tbb::parallel_for((unsigned32)0, GetNodeCount(), [&](const unsigned32 i)
|
||||
{
|
||||
MaterialNode<T, Comparer>* node = GetTypedNode(i);
|
||||
node->SetMaterial(materials[i]);
|
||||
});
|
||||
}
|
||||
|
||||
T GetMaterial(MaterialNode<T, Comparer>* node)
|
||||
{
|
||||
return node->GetMaterial();
|
||||
}
|
||||
|
||||
std::vector<T> GetMaterials() const
|
||||
{
|
||||
std::vector<T> nodeMaterials(GetNodeCount());
|
||||
tbb::parallel_for((unsigned32)0, GetNodeCount(), [&](const unsigned32 i)
|
||||
{
|
||||
nodeMaterials[i] = GetTypedNode(i)->GetMaterial();
|
||||
});
|
||||
return nodeMaterials;
|
||||
}
|
||||
|
||||
// Returns a list with all unique materials in the tree
|
||||
std::vector<T> GetUniqueMaterials() const
|
||||
{
|
||||
std::vector<T> nodeMaterials = GetMaterials();
|
||||
CollectionHelper::Unique(nodeMaterials, Comparer());
|
||||
return nodeMaterials;
|
||||
}
|
||||
|
||||
// Sets the material of a node.
|
||||
void SetMaterial(glm::uvec3 coordinate, unsigned level, T material)
|
||||
{
|
||||
MaterialNode<T, Comparer>* node = AddNode(coordinate, level);
|
||||
node->SetMaterial(material);
|
||||
}
|
||||
|
||||
// Creates a leaf node and sets its material
|
||||
void AddLeafNode(glm::uvec3 coordinate, T material)
|
||||
{
|
||||
if (mUseLeafMap)
|
||||
{
|
||||
// Check if there is already a leaf for this material (for auto reuse)
|
||||
auto existingLeaf = mLeafMap.find(material);
|
||||
Node* leaf = NULL;
|
||||
if (existingLeaf != mLeafMap.end())
|
||||
{
|
||||
// If there is a leaf, use it
|
||||
assert(material == ((MaterialNode<T, Comparer>*)(existingLeaf->second))->GetMaterial());
|
||||
leaf = existingLeaf->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If no leaf with this material exists yet, create it
|
||||
leaf = Create(GetMaxLevel());
|
||||
((MaterialNode<T, Comparer>*)leaf)->SetMaterial(material);
|
||||
mLeafMap.insert(std::pair<T, Node*>(material, leaf));
|
||||
}
|
||||
// Create the parent node of the leaf
|
||||
MaterialNode<T, Comparer>* parentOfLeaf = Tree<MaterialNode<T, Comparer>>::AddNode(glm::uvec3(coordinate.x >> 1, coordinate.y >> 1, coordinate.z >> 1), GetMaxLevel() - 1);
|
||||
// The last bit of the coordinate can be used to find the childindex in this parent:
|
||||
ChildIndex index = (((coordinate.x & 1) == 1) ? 1 : 0)
|
||||
+ (((coordinate.y & 1) == 1) ? 2 : 0)
|
||||
+ (((coordinate.z & 1) == 1) ? 4 : 0);
|
||||
|
||||
// Make sure the leaf points to this child
|
||||
parentOfLeaf->SetChild(index, leaf);
|
||||
}
|
||||
else
|
||||
{
|
||||
MaterialNode<T, Comparer>* leaf = Tree<MaterialNode<T, Comparer>>::AddLeafNode(coordinate);
|
||||
leaf->SetMaterial(material);
|
||||
}
|
||||
}
|
||||
|
||||
bool HasAdditionalPool() const override { return false; }
|
||||
protected:
|
||||
unsigned8 GetAdditionalBytesPerNode(unsigned8 level) const override
|
||||
{
|
||||
return sizeof(T);
|
||||
}
|
||||
std::vector<unsigned8> GetAdditionalNodeBytes(const Node* node) const override
|
||||
{
|
||||
return ((MaterialNode<T, Comparer>*)node)->GetMaterial().Serialize();
|
||||
}
|
||||
};
|
||||
11
Research/scene/Octree/MultiRootBitsTree.h
Normal file
11
Research/scene/Octree/MultiRootBitsTree.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "../Material/BitsMaterial.h"
|
||||
#include "LeafMaterialMultiRootTree.h"
|
||||
|
||||
template class LeafMaterialMultiRootTree < BitsMaterial<2> >;
|
||||
template class LeafMaterialMultiRootTree < BitsMaterial<3> >;
|
||||
template class LeafMaterialMultiRootTree < BitsMaterial<4> >;
|
||||
template class LeafMaterialMultiRootTree < BitsMaterial<5> >;
|
||||
template class LeafMaterialMultiRootTree < BitsMaterial<6> >;
|
||||
template class LeafMaterialMultiRootTree < BitsMaterial<7> >;
|
||||
template class LeafMaterialMultiRootTree < BitsMaterial<8> >;
|
||||
187
Research/scene/Octree/MultiRootTree.h
Normal file
187
Research/scene/Octree/MultiRootTree.h
Normal file
@@ -0,0 +1,187 @@
|
||||
#pragma once
|
||||
#include "Tree.h"
|
||||
#include "IAdditionalProperties.h"
|
||||
|
||||
template<typename NodeType = Node>
|
||||
class MultiRootTree : public Tree<NodeType>, public IAdditionalProperties
|
||||
{
|
||||
public:
|
||||
MultiRootTree(unsigned8 maxLevel, unsigned32 slaveRootCount) : Tree(maxLevel)
|
||||
{
|
||||
// Initialize the slaves
|
||||
AddSlaveRoots(slaveRootCount);
|
||||
}
|
||||
~MultiRootTree() override {}
|
||||
|
||||
void Clear() override
|
||||
{
|
||||
mSlaveRoots.clear();
|
||||
Tree<NodeType>::Clear();
|
||||
}
|
||||
|
||||
unsigned32 GetSlaveRootCount() const { return (unsigned32)mSlaveRoots.size(); }
|
||||
const NodeType* GetSlaveRoot(unsigned32 i) const { return GetTypedNode(mSlaveRoots[i]); }
|
||||
NodeType* GetSlaveRoot(unsigned32 i) { return GetTypedNode(mSlaveRoots[i]); }
|
||||
void AddSlaveRoots(size_t count)
|
||||
{
|
||||
size_t originalSlaveRootCount = GetSlaveRootCount();
|
||||
mSlaveRoots.resize(originalSlaveRootCount + count);
|
||||
for (size_t i = 0; i < count; i++)
|
||||
mSlaveRoots[originalSlaveRootCount + i] = Create(0)->GetIndex();
|
||||
}
|
||||
|
||||
void Append(glm::uvec3 coordinates, unsigned8 level, MultiRootTree* tree)
|
||||
{
|
||||
// Let some possible inhereting class do preprocessing on the current tree before append
|
||||
AppendPreProcess(coordinates, level, tree);
|
||||
|
||||
// If the tree that is to be appended has more roots, add roots to this tree first:
|
||||
if (tree->GetSlaveRootCount() > this->GetSlaveRootCount())
|
||||
AddSlaveRoots(tree->GetSlaveRootCount() - this->GetSlaveRootCount());
|
||||
|
||||
std::vector<unsigned32> equivalents(tree->GetNodeCount());
|
||||
|
||||
// First create/get the node that acts as the root of the tree to append
|
||||
NodeType* treeRoot = this->AddNode(coordinates, level);
|
||||
treeRoot->CopyProperties(tree->GetRoot());
|
||||
equivalents[0] = treeRoot->GetIndex();
|
||||
|
||||
// Make copies of all nodes in tree, and add them to our own nodepool (with the correct level and root)
|
||||
NodeType* copy = NULL;
|
||||
for (unsigned32 nodeId = 1; nodeId < tree->GetNodeCount(); nodeId++)
|
||||
{
|
||||
NodeType* node = tree->GetTypedNode(nodeId);
|
||||
if (node->GetLevel() == 0)
|
||||
{
|
||||
// Find which slave this root belongs to
|
||||
for (unsigned i = 0; i < tree->GetSlaveRootCount(); i++)
|
||||
if (tree->GetSlaveRoot(i) == node)
|
||||
// And add it
|
||||
copy = (NodeType*)this->GetSlaveRoot(i)->AddNode(coordinates, level);
|
||||
}
|
||||
else
|
||||
copy = this->Create(node->GetLevel() + level);
|
||||
assert(copy != NULL);
|
||||
copy->CopyProperties(node);
|
||||
equivalents[nodeId] = copy->GetIndex();
|
||||
}
|
||||
// Restore all child pointers
|
||||
unsigned32* newChildren = new unsigned32[8];
|
||||
for (unsigned32 i = 0; i < tree->GetNodeCount(); i++)
|
||||
{
|
||||
NodeType* copy = GetTypedNode(equivalents[i]);
|
||||
NodeType* node = tree->GetTypedNode(i);
|
||||
|
||||
unsigned32* children = node->GetChildren();
|
||||
unsigned8 childCount = node->GetChildCount();
|
||||
|
||||
for (ChildIndex c = 0; c < childCount; c++) newChildren[c] = equivalents[children[c]];
|
||||
|
||||
copy->SetChildren(node->GetChildmask(), newChildren);
|
||||
}
|
||||
delete[] newChildren;
|
||||
|
||||
// Let some possible inhereting class do postprocessing on the added nodes
|
||||
AppendPostProcess(coordinates, level, tree);
|
||||
}
|
||||
|
||||
// Adds a leaf node to the given slave root ID. Use AddLeafNode without additional arguments to add leafs to the main root.
|
||||
NodeType* AddLeafNode(glm::uvec3 coordinates)
|
||||
{
|
||||
return Tree<NodeType>::AddLeafNode(coordinates);
|
||||
}
|
||||
NodeType* AddLeafNode(glm::uvec3 coordinates, unsigned32 slaveRootID)
|
||||
{
|
||||
return (NodeType*)GetSlaveRoot(slaveRootID)->AddNode(coordinates, GetMaxLevel());
|
||||
}
|
||||
bool SlaveHasLeaf(glm::uvec3 coordinates, unsigned32 slaveRootID) const
|
||||
{
|
||||
return GetSlaveRoot(slaveRootID)->HasNode(coordinates, GetMaxLevel());
|
||||
}
|
||||
|
||||
std::vector<size_t> GetOctreeNodesPerLevel() const override
|
||||
{
|
||||
std::vector<size_t> octreeNodesPerLevel(GetMaxLevel() + 1);
|
||||
std::function<void(const Node*)> nodeCounter = [&octreeNodesPerLevel](const Node* node) -> void
|
||||
{
|
||||
octreeNodesPerLevel[node->GetLevel()]++;
|
||||
};
|
||||
this->Traverse(nodeCounter);
|
||||
for (unsigned32 i = 0; i < GetSlaveRootCount(); i++) GetSlaveRoot(i)->Traverse(nodeCounter);
|
||||
return octreeNodesPerLevel;
|
||||
}
|
||||
|
||||
//size_t GetMinimumNodePoolSize() const override;
|
||||
//std::vector<unsigned8>& GetNodePool() override;
|
||||
|
||||
void WriteProperties(std::ostream& file) override
|
||||
{
|
||||
// Write the number of slave roots, and their indexes
|
||||
Serializer<std::vector<unsigned32>, unsigned32>::Serialize(mSlaveRoots, file);
|
||||
}
|
||||
void ReadProperties(std::istream& file) override
|
||||
{
|
||||
// By this time all original slaves have been deleted, so we need to rebuild them
|
||||
Serializer<std::vector<unsigned32>, unsigned32>::Deserialize(mSlaveRoots, file);
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> GetAdditionalProperties() override
|
||||
{
|
||||
if (!mAdditionalProperties.empty()) return mAdditionalProperties;
|
||||
else
|
||||
{
|
||||
mAdditionalProperties.insert(std::pair<std::string, std::string>("RootCount", std::to_string(1 + mSlaveRoots.size())));
|
||||
return mAdditionalProperties;
|
||||
}
|
||||
}
|
||||
|
||||
bool HasAdditionalPool() const override { return true; }
|
||||
protected:
|
||||
unsigned8 GetAdditionalBytesPerNode(unsigned8 level) const override { return level == GetMaxLevel() ? 1 : 0; }
|
||||
virtual std::vector<unsigned8> GetAdditionalNodeBytes(const Node* node) const override
|
||||
{
|
||||
if (node->GetLevel() != GetMaxLevel()) return std::vector<unsigned8>();
|
||||
return std::vector<unsigned8>(1, 1);
|
||||
}
|
||||
|
||||
unsigned8 GetAdditionalTreeInfoSize() const override { return (unsigned8)(mSlaveRoots.size() + 1) * 2; }
|
||||
std::vector<unsigned8> GetAdditionalTreeInfo(const std::vector<size_t>& nodePointers) const override
|
||||
{
|
||||
std::vector<unsigned8> res(GetAdditionalTreeInfoSize());
|
||||
// Start with the main root:
|
||||
std::vector<unsigned8> rootPointer = BitHelper::SplitInBytes(nodePointers[0], 2);
|
||||
std::move(rootPointer.begin(), rootPointer.end(), res.begin());
|
||||
for (unsigned32 i = 0; i < GetSlaveRootCount(); i++)
|
||||
{
|
||||
size_t slavePointer = nodePointers[GetSlaveRoot(i)->GetIndex()];
|
||||
std::vector<unsigned8> slavePointerBytes = BitHelper::SplitInBytes(slavePointer, 2);
|
||||
std::move(slavePointerBytes.begin(), slavePointerBytes.end(), res.begin() + (1 + i) * 2);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void UpdateLocalReferences(const std::vector<unsigned32>& indexMap)
|
||||
{
|
||||
for (size_t i = 0; i < mSlaveRoots.size(); i++)
|
||||
mSlaveRoots[i] = indexMap[mSlaveRoots[i]];
|
||||
Tree<NodeType>::UpdateLocalReferences(indexMap);
|
||||
}
|
||||
|
||||
void WriteAdditionalPoolProperties(std::ostream& file) override
|
||||
{
|
||||
GetAdditionalProperties();
|
||||
Serializer<std::map<std::string, std::string>>::Serialize(mAdditionalProperties, file);
|
||||
}
|
||||
void ReadAdditionalPoolProperties(std::istream& file) override
|
||||
{
|
||||
Serializer<std::map<std::string, std::string>>::Deserialize(mAdditionalProperties, file);
|
||||
}
|
||||
|
||||
std::vector<unsigned32> mSlaveRoots;
|
||||
std::map<std::string, std::string> mAdditionalProperties;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
234
Research/scene/Octree/Node.cpp
Normal file
234
Research/scene/Octree/Node.cpp
Normal file
@@ -0,0 +1,234 @@
|
||||
#pragma warning(disable:4996)
|
||||
#include "Node.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
//#include "../PropertyLoader.h"
|
||||
//#include "../Renderer.h"
|
||||
#include "../../core/IntersectTests.h"
|
||||
#include "BaseTree.h"
|
||||
|
||||
//************************************
|
||||
// Should only be called on a root node from outside this class
|
||||
//************************************
|
||||
Node::Node(const BaseTree* tree, unsigned8 level) :
|
||||
mLevel(level),
|
||||
mChildMask(ChildMask(0)),
|
||||
#ifdef USE_DYNAMIC_ARRAY
|
||||
mChildren(SmallDynamicArray<unsigned32>()),
|
||||
#else
|
||||
mChildren(std::vector<unsigned32>()),
|
||||
#endif
|
||||
mTree(tree != NULL ? tree->GetTreeIndex() : 0)
|
||||
{}
|
||||
|
||||
//Node::Node(const Node& node) :
|
||||
// mIndex(node.mIndex),
|
||||
// mLevel(node.mLevel),
|
||||
// mTree(node.mTree)
|
||||
//{
|
||||
// SetChildren(node.mChildMask, node.GetChildren());
|
||||
//}
|
||||
|
||||
//Node::Node(Node&& node) :
|
||||
// mChildren(std::move(node.mChildren)),
|
||||
// mIndex(std::move(node.mIndex)),
|
||||
// mChildMask(std::move(node.mChildMask)),
|
||||
// mLevel(std::move(node.mLevel)),
|
||||
// mTree(std::move(node.mTree))
|
||||
//{
|
||||
//}
|
||||
//
|
||||
//Node& Node::operator=(Node&& node)
|
||||
//{
|
||||
// mChildren = std::move(node.mChildren);
|
||||
// mIndex = std::move(node.mIndex);
|
||||
// mChildMask = std::move(node.mChildMask);
|
||||
// mLevel = std::move(node.mLevel);
|
||||
// mTree = std::move(node.mTree);
|
||||
//}
|
||||
|
||||
//************************************
|
||||
// Destroys the current node
|
||||
//************************************
|
||||
Node::~Node() {
|
||||
//printf("Help I'm being killed!\n");
|
||||
}
|
||||
|
||||
void Node::Traverse(const std::function<void(const Node*)>& f) const
|
||||
{
|
||||
f.operator()(this);
|
||||
|
||||
unsigned8 childCount = GetChildCount();
|
||||
for (unsigned8 i = 0; i < childCount; i++)
|
||||
BaseTree::Get(mTree)->GetNode(mChildren[i])->Traverse(f);
|
||||
}
|
||||
|
||||
unsigned64 Node::GetOctreeNodeCount(bool (*f)(const Node*)) const
|
||||
{
|
||||
unsigned8 childCount = GetChildCount();
|
||||
unsigned64 nodeCount = f(this) ? 1 : 0;
|
||||
for (unsigned i = 0; i < childCount; i++)
|
||||
nodeCount += BaseTree::Get(mTree)->GetNode(mChildren[i])->GetOctreeNodeCount(f);
|
||||
return nodeCount;
|
||||
}
|
||||
unsigned64 Node::GetLeafVoxelCount() const
|
||||
{
|
||||
if (mChildMask.mask == 0 || mLevel == BaseTree::Get(mTree)->GetMaxLevel()) // This is e a leaf node!
|
||||
return 1;
|
||||
|
||||
unsigned long long res = 0;
|
||||
unsigned8 childCount = mChildMask.GetSetBefore(8);
|
||||
for (unsigned i = 0; i < childCount; i++)
|
||||
res += BaseTree::Get(mTree)->GetNode(mChildren[i])->GetLeafVoxelCount();
|
||||
return res;
|
||||
}
|
||||
|
||||
Node* Node::AddChild(const ChildIndex index) {
|
||||
Node* curChild = GetChild(index);
|
||||
if (curChild == NULL) {
|
||||
curChild = BaseTree::Get(mTree)->Create(mLevel + 1);
|
||||
SetChild(index, curChild);
|
||||
}
|
||||
return curChild;
|
||||
}
|
||||
|
||||
Node* Node::GetChild(const ChildIndex index) const
|
||||
{
|
||||
if (!HasChild(index))
|
||||
return NULL;
|
||||
ChildIndex vIndex = mChildMask.GetSetBefore(index);
|
||||
return BaseTree::Get(mTree)->GetNode(mChildren[vIndex]);
|
||||
}
|
||||
|
||||
void Node::SetChild(const ChildIndex index, Node* child)
|
||||
{
|
||||
SetChildIndex(index, child->GetIndex());
|
||||
}
|
||||
|
||||
void Node::SetChildIndex(const ChildIndex c, const unsigned32 index)
|
||||
{
|
||||
bool isNew = !HasChild(c);
|
||||
unsigned8 oldChildCount = mChildMask.GetSet();
|
||||
mChildMask.Set(c, true);
|
||||
ChildIndex vIndex = mChildMask.GetSetBefore(c);
|
||||
if (isNew)
|
||||
{
|
||||
#ifdef USE_DYNAMIC_ARRAY
|
||||
mChildren.Insert(vIndex, index, oldChildCount);
|
||||
#else
|
||||
mChildren.insert(mChildren.begin() + vIndex, index);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
mChildren[vIndex] = index;
|
||||
}
|
||||
|
||||
void Node::SetChildren(ChildMask mask, const unsigned* children)
|
||||
{
|
||||
unsigned8 oldChildCount = mChildMask.GetSet();
|
||||
unsigned8 newChildCount = mask.GetSet();
|
||||
mChildMask = mask;
|
||||
#ifdef USE_DYNAMIC_ARRAY
|
||||
if (oldChildCount != newChildCount)
|
||||
mChildren.Resize(oldChildCount, newChildCount);
|
||||
mChildren.SetRange(children, 0, newChildCount);
|
||||
#else
|
||||
mChildren.resize(newChildCount);
|
||||
for (ChildIndex c = 0; c < newChildCount; c++)
|
||||
mChildren[c] = children[c];
|
||||
#endif
|
||||
}
|
||||
|
||||
void Node::MoveToTree(BaseTree* tree)
|
||||
{
|
||||
assert(typeid(*(BaseTree::Get(mTree))) == typeid(*tree));
|
||||
mTree = tree->GetTreeIndex();
|
||||
}
|
||||
|
||||
|
||||
//************************************
|
||||
// Adds the given node at the given coordinates: recursively adds parents in top-down fashion
|
||||
//************************************
|
||||
Node* Node::AddNode(glm::uvec3 coordinates, const unsigned8 level) {
|
||||
if (GetLevel() >= level)
|
||||
{
|
||||
if (coordinates.x != 0 || coordinates.y != 0 || coordinates.z != 0)
|
||||
printf("Unexpected root coordinate (%d, %d, %d)\n", coordinates.x, coordinates.y, coordinates.z);
|
||||
return this;
|
||||
}
|
||||
|
||||
ChildIndex child = GetChildIndex(coordinates, level);
|
||||
unsigned32 mask = BitHelper::GetLSMask<unsigned32>(0, level - GetLevel() - 1);
|
||||
coordinates.x &= mask;
|
||||
coordinates.y &= mask;
|
||||
coordinates.z &= mask;
|
||||
return AddChild(child)->AddNode(coordinates, level);
|
||||
}
|
||||
|
||||
bool Node::HasNode(glm::uvec3 coord, const unsigned8 level) const
|
||||
{
|
||||
// If we reached a node at the wanted level, return true
|
||||
if (mLevel >= level) return true;
|
||||
|
||||
// Get the wanted child
|
||||
ChildIndex child = GetChildIndex(coord, level);
|
||||
if (HasChild(child))
|
||||
{
|
||||
unsigned32 mask = BitHelper::GetLSMask<unsigned32>(0, level - this->GetLevel() - 1);
|
||||
coord.x &= mask;
|
||||
coord.y &= mask;
|
||||
coord.z &= mask;
|
||||
return GetChild(child)->HasNode(coord, level);
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
ChildIndex Node::GetChildIndex(const glm::uvec3 coord, const unsigned8 level) const
|
||||
{
|
||||
unsigned range = 1 << (level - this->GetLevel() - 1);
|
||||
return (coord.x < range ? 0 : 1)
|
||||
+ (coord.y < range ? 0 : 2)
|
||||
+ (coord.z < range ? 0 : 4);
|
||||
}
|
||||
|
||||
void Node::WriteProperties(std::ostream& file) {}
|
||||
void Node::ReadProperties(std::istream& file) {}
|
||||
void Node::CopyProperties(Node* source) {}
|
||||
|
||||
bool Node::Compare(const Node& node) const
|
||||
{
|
||||
// Then on childmask
|
||||
unsigned8 nodeMask = node.GetChildmask().mask;
|
||||
if (mChildMask.mask != nodeMask)
|
||||
// This is equal to returning false if the highest significant bit in the other childmask is more significant than the highest significant bit in this childmask
|
||||
return mChildMask.mask < nodeMask;
|
||||
|
||||
// Then on child pointer values
|
||||
unsigned8 childCount = mChildMask.GetSet();
|
||||
for (unsigned8 i = 0; i < childCount; i++)
|
||||
{
|
||||
// Cast pointer to unsigned number
|
||||
auto aPtr = this->mChildren[i];
|
||||
auto bPtr = node.mChildren[i];
|
||||
if (aPtr != bPtr) return aPtr < bPtr;
|
||||
}
|
||||
|
||||
// Apparently the nodes are equal
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Node::Equals(const Node& node) const
|
||||
{
|
||||
if (this == &node) // Same address == same node
|
||||
return true;
|
||||
if (this->GetLevel() != node.GetLevel() || this->GetChildmask().mask != node.GetChildmask().mask)
|
||||
return false;
|
||||
unsigned8 childCount = mChildMask.GetSet();
|
||||
for (unsigned8 i = 0; i < childCount; i++)
|
||||
{
|
||||
if (this->mChildren[i] != node.mChildren[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
114
Research/scene/Octree/Node.h
Normal file
114
Research/scene/Octree/Node.h
Normal file
@@ -0,0 +1,114 @@
|
||||
#pragma once
|
||||
|
||||
// Dynamic arrays are more memory efficient (as they don't require explicitly storing the capacity, size, etc)
|
||||
// But using them is less save. In Windows it seems to work fine, but segmentation errors occur on linux using dynamic arrays.
|
||||
#define USE_DYNAMIC_ARRAY
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <typeinfo>
|
||||
#include "../../inc/glm/glm.hpp"
|
||||
#include "../../core/Defines.h"
|
||||
#include "../../core/Util/SmallDynamicArray.h"
|
||||
#include "ChildMask.h"
|
||||
|
||||
class BaseTree;
|
||||
|
||||
class Node {
|
||||
public:
|
||||
Node(const BaseTree* rootIndex, unsigned8 level = 0);
|
||||
//Node(const Node& node); // Copy ctor
|
||||
Node(Node&& node)
|
||||
{
|
||||
mChildren = std::move(node.mChildren);
|
||||
mIndex = std::move(node.mIndex);
|
||||
mChildMask = std::move(node.mChildMask);
|
||||
mLevel = std::move(node.mLevel);
|
||||
mTree = std::move(node.mTree);
|
||||
}// Move ctor
|
||||
|
||||
//Node(const Node& node) {
|
||||
// mChildren = node.mChildren);
|
||||
//}
|
||||
//Node& operator=(const Node&) = default;
|
||||
|
||||
~Node();
|
||||
|
||||
//Node& operator=(const Node& other); // Copy assignment
|
||||
Node& operator=(Node&& other) // Move assignment
|
||||
{
|
||||
mChildren = std::move(other.mChildren);
|
||||
mIndex = std::move(other.mIndex);
|
||||
mChildMask = std::move(other.mChildMask);
|
||||
mLevel = std::move(other.mLevel);
|
||||
mTree = std::move(other.mTree);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void SetChild(const ChildIndex index, Node* child);
|
||||
Node* GetChild(const ChildIndex index) const;
|
||||
inline bool HasChild(const ChildIndex index) const { return mChildMask.Get(index); }
|
||||
inline unsigned GetChildIndex(const ChildIndex index) const
|
||||
{
|
||||
if (!HasChild(index))
|
||||
return 0;
|
||||
ChildIndex vIndex = mChildMask.GetSetBefore(index);
|
||||
return mChildren[vIndex];
|
||||
}
|
||||
|
||||
inline bool HasChildren() const { return mChildMask.mask != 0; }
|
||||
inline unsigned* GetChildren() const { return (unsigned*)&mChildren[0]; }
|
||||
// Replaces the current children of this node by the children in the given array of children.
|
||||
// The given array should have at least as many members as the number of set bits in the new child mask.
|
||||
void SetChildren(ChildMask mask, const unsigned* children);
|
||||
|
||||
void MoveToTree(BaseTree* tree);
|
||||
|
||||
// Returns the number of direct children this node has (e.g. the number of "1" in the childmask)
|
||||
inline unsigned8 GetChildCount() const { return mChildMask.GetSet(); }
|
||||
|
||||
inline unsigned8 GetLevel() const { return mLevel; }
|
||||
inline void SetLevel(unsigned8 level) { mLevel = level; }
|
||||
inline unsigned GetIndex() const { return mIndex; }
|
||||
inline void SetIndex(unsigned32 index) { mIndex = index; }
|
||||
// If the tree type is equal to the current tree type, moved the node to the new tree.
|
||||
inline ChildMask GetChildmask() const { return mChildMask; }
|
||||
|
||||
// Returns the total number of octree nodes (including the current node) that can be reached from this nodes
|
||||
unsigned64 GetOctreeNodeCount(bool (*f)(const Node*)) const;
|
||||
unsigned64 GetOctreeNodeCount() const { return GetOctreeNodeCount([](const Node* node) { return true; }); }
|
||||
|
||||
// Returns the number of leaf voxels in this octree (if it wasn't compressed)
|
||||
unsigned64 GetLeafVoxelCount() const;
|
||||
|
||||
// Returns true if this node is smaller than the other node. Used for sorting
|
||||
bool Compare(const Node& node) const;
|
||||
// Returns true if this node is equal to the other node
|
||||
bool Equals(const Node& node) const;
|
||||
|
||||
// Adds the the given to the tree at the given coordinate. The node should specify at which level it needs to be added. If a node already exists at the given coordinate,
|
||||
// false is returned
|
||||
Node* AddNode(glm::uvec3 coordinates, const unsigned8 level);
|
||||
bool HasNode(glm::uvec3 coord, const unsigned8 level) const;
|
||||
ChildIndex GetChildIndex(const glm::uvec3 coord, const unsigned8 level) const;
|
||||
void SetChildIndex(const ChildIndex index, const unsigned32 childIndex);
|
||||
|
||||
void Traverse(const std::function<void(const Node*)>& f) const;
|
||||
|
||||
void WriteProperties(std::ostream& file);
|
||||
void ReadProperties(std::istream& file);
|
||||
void CopyProperties(Node* source);
|
||||
protected:
|
||||
Node* AddChild(const ChildIndex index);
|
||||
|
||||
#ifdef USE_DYNAMIC_ARRAY
|
||||
SmallDynamicArray<unsigned32> mChildren; // Array (with length of the number of children), containing the indices of child nodes
|
||||
#else
|
||||
std::vector<unsigned32> mChildren;
|
||||
#endif
|
||||
|
||||
unsigned32 mIndex;
|
||||
ChildMask mChildMask;
|
||||
unsigned8 mLevel; // the level of the current node, where the root has level 0
|
||||
unsigned16 mTree; // the tree to which this node
|
||||
};
|
||||
205
Research/scene/Octree/NodeReplacementFinder.h
Normal file
205
Research/scene/Octree/NodeReplacementFinder.h
Normal file
@@ -0,0 +1,205 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <assert.h>
|
||||
#include <functional>
|
||||
|
||||
#include "../../core/Defines.h"
|
||||
|
||||
// Searchtree based class that finds all fitting nodes for a certain value.
|
||||
// A function needs to be given that hashes a value into a set of hashes (all values should give a set with the same length!)
|
||||
// For each hash in this set of hashes, a value of 0 means that anything can be put there. The other hashes have to match.
|
||||
template<typename K, typename V>
|
||||
class NodeReplacementFinder
|
||||
{
|
||||
private:
|
||||
struct SearchTreeNode
|
||||
{
|
||||
private:
|
||||
std::unordered_map<K, SearchTreeNode*> children;
|
||||
union WildCardOrValue
|
||||
{
|
||||
SearchTreeNode* wildcard;
|
||||
V value;
|
||||
};
|
||||
WildCardOrValue wildCardOrValue;
|
||||
bool isLeaf;
|
||||
|
||||
V GetValue() const
|
||||
{
|
||||
assert(isLeaf);
|
||||
return wildCardOrValue.value;
|
||||
}
|
||||
|
||||
void SetValue(V value)
|
||||
{
|
||||
assert(isLeaf);
|
||||
wildCardOrValue.value = value;
|
||||
}
|
||||
|
||||
SearchTreeNode* GetWildCard() const
|
||||
{
|
||||
assert(!isLeaf);
|
||||
return wildCardOrValue.wildcard;
|
||||
}
|
||||
|
||||
void SetWildCard(SearchTreeNode* wildcard)
|
||||
{
|
||||
assert(!isLeaf);
|
||||
wildCardOrValue.wildcard = wildcard;
|
||||
}
|
||||
|
||||
|
||||
SearchTreeNode* GetChild(unsigned32 key) const
|
||||
{
|
||||
// If the key is a wildcard, return the wildcardnode
|
||||
if (key == 0) return GetWildCard();
|
||||
|
||||
// Otherwise, find the correct child
|
||||
auto it = children.find(key);
|
||||
if (it == children.end()) return NULL;
|
||||
else return (*it).second;
|
||||
}
|
||||
|
||||
SearchTreeNode* AddChild(unsigned32 key, bool isLeaf)
|
||||
{
|
||||
SearchTreeNode* child = GetChild(key);
|
||||
if (child != NULL) return child;
|
||||
|
||||
SearchTreeNode* newChild = new SearchTreeNode(isLeaf);
|
||||
if (key == 0) SetWildCard(newChild);
|
||||
else children.insert(std::make_pair(key, newChild));
|
||||
return newChild;
|
||||
}
|
||||
|
||||
// Recursively call add, updating the index to make sure the correct key is used
|
||||
void Add(const std::vector<unsigned32>& keys, const V& value, size_t index)
|
||||
{
|
||||
if (isLeaf) SetValue(value);
|
||||
else
|
||||
{
|
||||
unsigned32 curKey = keys[index];
|
||||
SearchTreeNode* child = AddChild(curKey, index == keys.size() - 1);
|
||||
child->Add(keys, value, index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void Find(const std::vector<unsigned32>& keys, size_t index, std::vector<V>& out) const
|
||||
{
|
||||
if (isLeaf) out.push_back(GetValue());
|
||||
else
|
||||
{
|
||||
unsigned32 curKey = keys[index];
|
||||
if (curKey == 0)
|
||||
{
|
||||
// if the current key is a wildcard, explore all possible paths:
|
||||
for (auto child : children)
|
||||
child.second->Find(keys, index + 1, out);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the current key isn't a wildcard, only explore the current key and the wildcard key
|
||||
auto curKeyChild = GetChild(curKey);
|
||||
if (curKeyChild != NULL) curKeyChild->Find(keys, index + 1, out);
|
||||
}
|
||||
SearchTreeNode* wildcard = GetWildCard();
|
||||
if (wildcard != NULL) wildcard->Find(keys, index + 1, out);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if the node can be removed safely
|
||||
bool Remove(const std::vector<unsigned32>& keys, size_t index)
|
||||
{
|
||||
if (isLeaf) return true;
|
||||
else
|
||||
{
|
||||
unsigned32 curKey = keys[index];
|
||||
auto child = GetChild(curKey);
|
||||
if (child != NULL)
|
||||
{
|
||||
bool canDelete = child->Remove(keys, index + 1);
|
||||
if (canDelete)
|
||||
{
|
||||
if (curKey == 0) SetWildCard(NULL);
|
||||
else children.erase(children.find(curKey));
|
||||
|
||||
delete child;
|
||||
}
|
||||
}
|
||||
}
|
||||
return children.empty();
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
SearchTreeNode(bool isLeaf) : isLeaf(isLeaf)
|
||||
{
|
||||
if (!isLeaf)
|
||||
{
|
||||
children = std::unordered_map<unsigned32, SearchTreeNode*>();
|
||||
SetWildCard(NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetValue(V());
|
||||
}
|
||||
}
|
||||
~SearchTreeNode()
|
||||
{
|
||||
if (!isLeaf)
|
||||
{
|
||||
for (auto child : children)
|
||||
delete child.second;
|
||||
}
|
||||
}
|
||||
|
||||
void Add(const std::vector<unsigned32>& keys, V value)
|
||||
{
|
||||
Add(keys, value, 0);
|
||||
}
|
||||
|
||||
void Remove(const std::vector<unsigned32>& keys)
|
||||
{
|
||||
Remove(keys, 0);
|
||||
}
|
||||
|
||||
std::vector<V> Find(std::vector<unsigned32> keys) const
|
||||
{
|
||||
std::vector<V> res;
|
||||
Find(keys, 0, res);
|
||||
return res;
|
||||
}
|
||||
};
|
||||
std::function<std::vector<K>(const V)> GetKeys;
|
||||
SearchTreeNode* root;
|
||||
public:
|
||||
NodeReplacementFinder(std::function<std::vector<K>(const V)> keyGetter)
|
||||
{
|
||||
GetKeys = keyGetter;
|
||||
root = new SearchTreeNode(false);
|
||||
}
|
||||
~NodeReplacementFinder()
|
||||
{
|
||||
delete root;
|
||||
}
|
||||
|
||||
void Add(const V value)
|
||||
{
|
||||
std::vector<K> keys = GetKeys(value);
|
||||
root->Add(keys, value);
|
||||
}
|
||||
|
||||
void Remove(const V value)
|
||||
{
|
||||
std::vector<K> keys = GetKeys(value);
|
||||
root->Remove(keys);
|
||||
}
|
||||
|
||||
std::vector<V> Find(const V value) const
|
||||
{
|
||||
std::vector<K> keys = GetKeys(value);
|
||||
return root->Find(keys);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
47
Research/scene/Octree/NodeSmall.h
Normal file
47
Research/scene/Octree/NodeSmall.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include "../../inc/glm/glm.hpp"
|
||||
#include "../../core/Defines.h"
|
||||
#include "Root.h"
|
||||
|
||||
class NodeSmall {
|
||||
public:
|
||||
NodeSmall(Root* root, unsigned8 level = 0) {}
|
||||
~NodeSmall() {}
|
||||
|
||||
//Node* GetChild(ChildIndex index);
|
||||
//bool HasChild(ChildIndex index);
|
||||
//void SetChild(ChildIndex index, Node* child);
|
||||
//// Returns the number of direct children this node has (e.g. the number of "1" in the childmask)
|
||||
//unsigned8 GetChildCount();
|
||||
//unsigned8 GetLevel();
|
||||
//// Returns the number of leaf voxels in this octree (if it wasn't compressed)
|
||||
//virtual unsigned long long GetLeafVoxelCount();
|
||||
|
||||
//// Returns true if this node is smaller than the other node. Used for sorting
|
||||
//virtual bool Compare(Node* node);
|
||||
//// Returns true if this node is equal to the other node
|
||||
//virtual bool Equals(Node* node);
|
||||
|
||||
//// Recursively set the level of the current node
|
||||
//void SetLevel(unsigned8 level);
|
||||
|
||||
//ChildMask GetChildmask();
|
||||
|
||||
//virtual void WriteProperties(std::ostream& file);
|
||||
//virtual void ReadProperties(std::istream& file);
|
||||
//virtual void CopyProperties(Node* source);
|
||||
protected:
|
||||
|
||||
//Node* AddChild(ChildIndex index);
|
||||
//// Adds the the given to the tree at the given coordinate. The node should specify at which level it needs to be added. If a node already exists at the given coordinate,
|
||||
//// false is returned
|
||||
//Node* AddNode(glm::uvec3 coordinates, unsigned8 level);
|
||||
|
||||
Root* mRoot; // the tree to which this node belongs
|
||||
|
||||
ChildMask mChildMask;
|
||||
std::vector<Node*> mChildren; // index of children, NULL if leaf node
|
||||
unsigned8 mLevel; // the level of the current node, where the root has level 0
|
||||
private:
|
||||
};
|
||||
965
Research/scene/Octree/Tree.h
Normal file
965
Research/scene/Octree/Tree.h
Normal file
@@ -0,0 +1,965 @@
|
||||
#pragma once
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <unordered_set>
|
||||
#include <numeric>
|
||||
#include <functional>
|
||||
#include "BaseTree.h"
|
||||
#include "../../inc/tbb/parallel_sort.h"
|
||||
#include "../../inc/tbb/parallel_for_each.h"
|
||||
#include "../../inc/tbb/concurrent_queue.h"
|
||||
#include "../../core/Defines.h"
|
||||
#include "../../core/CollectionHelper.h"
|
||||
#include "../../core/Serializer.h"
|
||||
#include "../../core/Util/BoolArray.h"
|
||||
#include "../../core/Util/ObjectPool.h"
|
||||
|
||||
template<typename NodeType = Node>
|
||||
class Tree : public BaseTree {
|
||||
public:
|
||||
Tree(unsigned8 maxLevel) :
|
||||
BaseTree(),
|
||||
mLeafNode(0),
|
||||
mLeafsAreEqual(true), // Default tree nodes are equal. Override the root for trees that don't have this property
|
||||
mMaxLevel(maxLevel),
|
||||
mNodePool(ObjectPool<NodeType>(/*1000000*/)),
|
||||
mSortedOnLevel(true)
|
||||
{
|
||||
// Create the root
|
||||
Create(0);
|
||||
}
|
||||
|
||||
~Tree() override
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
const Node* GetNode(const unsigned32 index) const override { return mNodePool[index]; }
|
||||
Node* GetNode(const unsigned32 index) override { return mNodePool[index]; }
|
||||
inline const NodeType* GetTypedNode(const unsigned32 index) const { return mNodePool[index]; }
|
||||
inline NodeType* GetTypedNode(const unsigned32 index) { return mNodePool[index]; }
|
||||
inline const NodeType* GetRoot() const { return mNodePool[0]; /*The first node in the nodepool is ALWAYS the root.*/ }
|
||||
inline NodeType* GetRoot() { return mNodePool[0]; }
|
||||
|
||||
inline unsigned8 GetMaxLevel() const override { return mMaxLevel; }
|
||||
unsigned32 GetNodeCount() const override { return (unsigned32)mNodePool.Size(); }
|
||||
inline bool IsEmpty() const override { return !GetRoot()->HasChildren(); }
|
||||
|
||||
bool HasLeaf(const glm::uvec3& coord) const { return HasNode(coord, GetMaxLevel()); }
|
||||
NodeType* AddLeafNode(const glm::uvec3& coord) { return AddNode(coord, GetMaxLevel()); }
|
||||
bool HasNode(const glm::uvec3& coord, const unsigned8 level) const { return GetRoot()->HasNode(coord, level); }
|
||||
NodeType* AddNode(const glm::uvec3& coord, const unsigned8 level) {
|
||||
NodeType* root = GetRoot();
|
||||
return (NodeType*)(root->AddNode(coord, level));
|
||||
}
|
||||
|
||||
void Traverse(const std::function<void(const Node*)>& f) const { GetRoot()->Traverse(f); }
|
||||
|
||||
std::vector<size_t> GetNodesPerLevel() const override
|
||||
{
|
||||
std::vector<size_t> nodesPerLevel;
|
||||
nodesPerLevel.resize(GetMaxLevel() + 1, 0);
|
||||
for (unsigned32 i = 0; i < GetNodeCount(); i++)
|
||||
nodesPerLevel[GetTypedNode(i)->GetLevel()]++;
|
||||
return nodesPerLevel;
|
||||
}
|
||||
size_t GetNodesInLevel(const unsigned8 level) const
|
||||
{
|
||||
size_t res = 0;
|
||||
for (unsigned32 i = 0; i < GetNodeCount(); i++)
|
||||
if (GetTypedNode(i)->GetLevel() == level) res++;
|
||||
return res;
|
||||
}
|
||||
virtual std::vector<size_t> GetOctreeNodesPerLevel() const override
|
||||
{
|
||||
std::vector<size_t> octreeNodesPerLevel(GetMaxLevel() + 1);
|
||||
std::function<void(const Node*)> nodeCounter = [&octreeNodesPerLevel](const Node* node) -> void
|
||||
{
|
||||
octreeNodesPerLevel[node->GetLevel()]++;
|
||||
};
|
||||
this->Traverse(nodeCounter);
|
||||
return octreeNodesPerLevel;
|
||||
}
|
||||
|
||||
virtual unsigned64 GetPointerCount() const override
|
||||
{
|
||||
unsigned64 res = 0;
|
||||
for (unsigned32 i = 0; i < GetNodeCount(); i++)
|
||||
res += GetTypedNode(i)->GetChildCount();
|
||||
return res;
|
||||
}
|
||||
|
||||
// Returns a map with for each node who it's parents are (O(N)). To find the parents of a node, use it's index (node->GetIndex())
|
||||
std::vector<std::vector<std::pair<unsigned, ChildIndex>>> GetParentMap() const
|
||||
{
|
||||
std::vector<std::vector<std::pair<unsigned, ChildIndex>>> parentMap(GetNodeCount(), std::vector<std::pair<unsigned, ChildIndex>>());
|
||||
for (unsigned32 i = 0; i < GetNodeCount(); i++)
|
||||
{
|
||||
NodeType* node = GetTypedNode(i);
|
||||
for (ChildIndex c = 0; c < 8; c++)
|
||||
if (node->HasChild(c))
|
||||
parentMap[node->GetChildIndex(c)].push_back(std::make_pair(i, c));
|
||||
}
|
||||
return parentMap;
|
||||
}
|
||||
|
||||
// Counts the number of parents each node has (e.g. how many pointers there are to each node). For a regular octree, this will be 1, but for a DAG it isn't necessarily.
|
||||
std::vector<size_t> GetParentCounts() const override
|
||||
{
|
||||
std::vector<size_t> parentCounts(GetNodeCount(), 0);
|
||||
for (unsigned32 i = 0; i < GetNodeCount(); i++)
|
||||
{
|
||||
const NodeType* node = GetTypedNode(i);
|
||||
unsigned32* children = node->GetChildren();
|
||||
for (ChildIndex c = 0; c < node->GetChildCount(); c++)
|
||||
parentCounts[children[c]]++;
|
||||
}
|
||||
return parentCounts;
|
||||
}
|
||||
|
||||
unsigned64 GetLeafVoxelCount() const
|
||||
{
|
||||
std::vector<unsigned64> leafCountTable(GetNodeCount(), 1);
|
||||
for (unsigned8 level = GetMaxLevel(); level-- > 0;)
|
||||
{
|
||||
for (unsigned32 i = 0; i < GetNodeCount(); i++)
|
||||
{
|
||||
const NodeType* node = GetTypedNode(i);
|
||||
if (node->GetLevel() == level)
|
||||
{
|
||||
unsigned64 sum = 0;
|
||||
unsigned32* children = node->GetChildren();
|
||||
for (ChildIndex c = 0; c < node->GetChildCount(); c++)
|
||||
sum += leafCountTable[children[c]];
|
||||
leafCountTable[i] = sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
return leafCountTable[0];
|
||||
}
|
||||
|
||||
bool ReadTree(const char* fileName) override
|
||||
{
|
||||
std::string binFileName = GetOctreeFileName(fileName);
|
||||
std::ifstream treeInFile(binFileName, std::ios::binary);
|
||||
|
||||
if (treeInFile.good()) {
|
||||
bool succes = Deserialize(treeInFile);
|
||||
|
||||
// Finished reading
|
||||
treeInFile.close();
|
||||
return succes;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
bool WriteTree(const char* fileName) override
|
||||
{
|
||||
std::string binFileName = GetOctreeFileName(fileName);
|
||||
std::ofstream treeOutFile(binFileName, std::ios::binary);
|
||||
|
||||
bool succes = Serialize(treeOutFile);
|
||||
|
||||
treeOutFile.close();
|
||||
return succes;
|
||||
}
|
||||
|
||||
bool VerifyTree(const char* fileName) override
|
||||
{
|
||||
std::string binFileName = GetOctreeFileName(fileName);
|
||||
std::ifstream treeInFile(binFileName, std::ios::binary);
|
||||
|
||||
if (treeInFile.good()) {
|
||||
// Read the maximum level in the tree
|
||||
unsigned8 maxLevel;
|
||||
Serializer<unsigned8>::Deserialize(maxLevel, treeInFile);
|
||||
this->mMaxLevel = maxLevel;
|
||||
|
||||
// Read the nodes per level
|
||||
std::vector<unsigned> nodesPerLevel(maxLevel + 1);
|
||||
Serializer<unsigned*>::Deserialize(&nodesPerLevel[0], mMaxLevel + 1, treeInFile);
|
||||
|
||||
// Sum the nodes per level to get the total number of nodes
|
||||
size_t nodeCount = std::accumulate(nodesPerLevel.begin(), nodesPerLevel.end(), 0);
|
||||
|
||||
printf(".");
|
||||
|
||||
// Read the childmasks for all nodes (needed to know which pointers exist)
|
||||
std::vector<ChildMask> childmasks(nodeCount);
|
||||
Serializer<ChildMask*>::Deserialize(&childmasks[0], nodeCount, treeInFile);
|
||||
printf(".");
|
||||
|
||||
// Read all pointers
|
||||
unsigned* newChildren = new unsigned[8];
|
||||
for (ChildMask mask : childmasks)
|
||||
{
|
||||
if (treeInFile.eof()) return false;
|
||||
ChildIndex childCount = mask.GetSet();
|
||||
Serializer<unsigned*>::Deserialize(newChildren, childCount, treeInFile);
|
||||
|
||||
for (ChildIndex i = 0; i < childCount; i++)
|
||||
{
|
||||
if (newChildren[i] >= nodeCount)
|
||||
{
|
||||
printf("Node index out of bounds: %u", newChildren[i]);
|
||||
delete[] newChildren;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
delete[] newChildren;
|
||||
printf("..");
|
||||
|
||||
// Finished reading
|
||||
treeInFile.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Serialize(std::ostream& treeOutFile)
|
||||
{
|
||||
auto levelIndices = SortOnLevel();
|
||||
|
||||
// Write the total number of levels in the tree
|
||||
unsigned maxLevel = GetMaxLevel();
|
||||
Serializer<unsigned8>::Serialize(maxLevel, treeOutFile);
|
||||
|
||||
// Write the nodes per level
|
||||
unsigned nodesThisLevel;
|
||||
for (unsigned8 level = 0; level < maxLevel + 1; level++)
|
||||
{
|
||||
nodesThisLevel = (unsigned)(levelIndices[level + 1] - levelIndices[level]);
|
||||
Serializer<unsigned32>::Serialize(nodesThisLevel, treeOutFile);
|
||||
}
|
||||
|
||||
// Gather the child masks for all nodes:
|
||||
std::vector<ChildMask> childMasks(GetNodeCount());
|
||||
tbb::parallel_for((unsigned32)0, GetNodeCount(), [&](unsigned32 i)
|
||||
{
|
||||
childMasks[i] = GetTypedNode(i)->GetChildmask();
|
||||
});
|
||||
|
||||
Serializer<ChildMask*>::Serialize(&childMasks[0], childMasks.size(), treeOutFile);
|
||||
|
||||
// Write child pointers of all nodes
|
||||
for (unsigned32 i = 0; i < GetNodeCount(); i++)
|
||||
{
|
||||
NodeType* node = GetTypedNode(i);
|
||||
ChildMask mask = node->GetChildmask();
|
||||
unsigned32* children = node->GetChildren();
|
||||
Serializer<unsigned32*>::Serialize(children, mask.GetSet(), treeOutFile);
|
||||
}
|
||||
|
||||
// Hack: first write the root properties, then the tree properties, then the rest of the nodes
|
||||
// This is done to be compliant with old files of previous versions
|
||||
|
||||
GetRoot()->WriteProperties(treeOutFile);
|
||||
WriteProperties(treeOutFile);
|
||||
// Write extra properties per node
|
||||
for (unsigned32 i = 1; i < GetNodeCount(); i++)
|
||||
GetTypedNode(i)->WriteProperties(treeOutFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Deserialize(std::istream& treeInFile)
|
||||
{
|
||||
this->Clear();
|
||||
this->InitializeReadTree();
|
||||
|
||||
// Read the maximum level in the tree
|
||||
Serializer<unsigned8>::Deserialize(mMaxLevel, treeInFile);
|
||||
|
||||
// Read the nodes per level
|
||||
std::vector<unsigned> nodesPerLevel(mMaxLevel + 1);
|
||||
Serializer<unsigned*>::Deserialize(&nodesPerLevel[0], mMaxLevel + 1, treeInFile);
|
||||
|
||||
// Sum the nodes per level to get the total number of nodes
|
||||
unsigned32 nodeCount = std::accumulate(nodesPerLevel.begin(), nodesPerLevel.end(), 0);
|
||||
|
||||
printf(".");
|
||||
|
||||
// Read the childmasks for all nodes (needed to know which pointers exist)
|
||||
std::vector<ChildMask> childmasks(nodeCount);
|
||||
Serializer<ChildMask*>::Deserialize(&childmasks[0], nodeCount, treeInFile);
|
||||
|
||||
for (unsigned8 level = 0; level <= mMaxLevel; level++)
|
||||
for (unsigned i = 0; i < nodesPerLevel[level]; i++)
|
||||
// Create the node. Creating them in order also adds them to the node pool in order.
|
||||
Create(level);
|
||||
printf(".");
|
||||
|
||||
// Read all pointers
|
||||
unsigned* newChildren = new unsigned[8];
|
||||
for (unsigned32 i = 0; i < nodeCount; i++)
|
||||
{
|
||||
ChildMask mask = childmasks[i];
|
||||
Serializer<unsigned*>::Deserialize(newChildren, mask.GetSet(), treeInFile);
|
||||
GetTypedNode(i)->SetChildren(mask, newChildren);
|
||||
}
|
||||
delete[] newChildren;
|
||||
printf(".");
|
||||
|
||||
GetRoot()->ReadProperties(treeInFile);
|
||||
ReadProperties(treeInFile);
|
||||
// Read extra properties per node
|
||||
for (unsigned32 i = 1; i < GetNodeCount(); i++)
|
||||
{
|
||||
if (treeInFile.eof())
|
||||
{
|
||||
printf("Something is wrong...");
|
||||
return false;
|
||||
}
|
||||
GetTypedNode(i)->ReadProperties(treeInFile);
|
||||
}
|
||||
printf(".");
|
||||
|
||||
this->FinalizeReadTree();
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string GetOctreeFileName(const char* fileName) { return std::string(fileName) + ".oct"; }
|
||||
static std::string GetAdditionalPoolFileName(const char* fileName) { return std::string(fileName) + ".additional.pool"; }
|
||||
|
||||
virtual void ReadProperties(std::istream& file) {}
|
||||
virtual void WriteProperties(std::ostream& file) {}
|
||||
|
||||
unsigned8 GetAdditionalTreeInfoSize() const override { return 0; }
|
||||
std::vector<unsigned8> GetAdditionalTreeInfo(const std::vector<size_t>& nodePointers) const override { return std::vector<unsigned8>(); }
|
||||
unsigned8 GetAdditionalBytesPerNode(unsigned8 level) const override { return 0; }
|
||||
std::vector<unsigned8> GetAdditionalNodeBytes(const Node* node) const override { return std::vector<unsigned8>(); }
|
||||
bool LastChildHasAdditionalBytes() const override { return true; }
|
||||
unsigned8 GetAdditionalBytesPerPointer(unsigned8 level) const override { return 0; }
|
||||
std::vector<unsigned8> GetAdditionalPointerBytes(const Node* node, ChildIndex child) const override { return std::vector<unsigned8>(); }
|
||||
|
||||
// Reads the node pool and stores it in this tree
|
||||
bool ReadAdditionalPool(const char* fileName) override
|
||||
{
|
||||
if (!HasAdditionalPool()) return true;
|
||||
std::string binFileName = GetAdditionalPoolFileName(fileName);
|
||||
std::ifstream additionalPoolInFile(binFileName, std::ios::binary);
|
||||
|
||||
if (additionalPoolInFile.good()) {
|
||||
// Destroy whole current node pool
|
||||
ReadAdditionalPoolProperties(additionalPoolInFile);
|
||||
additionalPoolInFile.close();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// Writes the node pool
|
||||
bool WriteAdditionalPool(const char* fileName) override
|
||||
{
|
||||
if (!HasAdditionalPool()) return true;
|
||||
std::string binFileName = GetAdditionalPoolFileName(fileName);
|
||||
std::ofstream additionalPoolOutFile(binFileName, std::ios::binary);
|
||||
WriteAdditionalPoolProperties(additionalPoolOutFile);
|
||||
additionalPoolOutFile.close();
|
||||
return true;
|
||||
}
|
||||
virtual bool HasAdditionalPool() const { return false; }
|
||||
|
||||
// Clears the whole tree, including the root!
|
||||
virtual void Clear() override
|
||||
{
|
||||
mNodePool.Clear();
|
||||
}
|
||||
|
||||
// Removed all nodes marked for deletion and updates the indices.
|
||||
void Clean()
|
||||
{
|
||||
unsigned32 oldNodeCount = GetNodeCount();
|
||||
mNodePool.Clean();
|
||||
UpdateNodeIndices(oldNodeCount);
|
||||
}
|
||||
|
||||
NodeType* Create(unsigned8 level) override
|
||||
{
|
||||
// Make sure only one leaf node is created (memory, performance)
|
||||
if (mLeafsAreEqual && level == GetMaxLevel() && mLeafNode != 0)
|
||||
return GetTypedNode(mLeafNode);
|
||||
// Otherwise, create a new node
|
||||
NodeType* res = mNodePool.Create(this, level);
|
||||
mSortedOnLevel = false;
|
||||
|
||||
res->SetIndex(GetNodeCount() - 1);
|
||||
if (mLeafsAreEqual && level == GetMaxLevel() && mLeafNode == 0) mLeafNode = res->GetIndex();
|
||||
return res;
|
||||
}
|
||||
|
||||
void Append(glm::uvec3 coordinates, unsigned8 level, Tree* tree)
|
||||
{
|
||||
// Let some possible inhereting class do preprocessing on the current tree before append
|
||||
AppendPreProcess(coordinates, level, tree);
|
||||
|
||||
// First create/get the node that acts as the root of the tree to append
|
||||
std::vector<unsigned32> equivalents(tree->GetNodeCount());
|
||||
NodeType* treeRoot = AddNode(coordinates, level);
|
||||
equivalents[0] = treeRoot->GetIndex();
|
||||
|
||||
// Make copies of all nodes in tree, and add them to our own nodepool (with the correct level and root)
|
||||
for (unsigned32 i = 1; i < tree->GetNodeCount(); i++)
|
||||
{
|
||||
NodeType* node = tree->GetTypedNode(i);
|
||||
NodeType* copy = this->Create(node->GetLevel() + level);
|
||||
equivalents[i] = copy->GetIndex();
|
||||
}
|
||||
|
||||
unsigned32* newChildren = new unsigned32[8];
|
||||
// Restore all child pointers
|
||||
for (unsigned32 i = 0; i < tree->GetNodeCount(); i++)
|
||||
{
|
||||
NodeType* node = tree->GetTypedNode(i);
|
||||
unsigned32* children = node->GetChildren();
|
||||
unsigned8 childCount = node->GetChildCount();
|
||||
for (ChildIndex c = 0; c < childCount; c++) newChildren[c] = equivalents[children[c]];
|
||||
NodeType* copy = this->GetTypedNode(equivalents[i]);
|
||||
copy->SetChildren(node->GetChildmask(), newChildren);
|
||||
}
|
||||
delete[] newChildren;
|
||||
|
||||
// Copy properties
|
||||
tbb::parallel_for((unsigned32)0, tree->GetNodeCount(), [&](unsigned i)
|
||||
{
|
||||
NodeType* source = tree->GetTypedNode(i);
|
||||
GetTypedNode(equivalents[i])->CopyProperties(source);
|
||||
});
|
||||
|
||||
// Let some possible inhereting class do postprocessing on the added nodes
|
||||
AppendPostProcess(coordinates, level, tree);
|
||||
}
|
||||
|
||||
// Appends the given tree to this tree. The nodes in the original tree will be moved to this tree.
|
||||
// During the appending, nodes are automatically merged when possible to keep memory usage low.
|
||||
// Note that this DOES NOT call AppendPostProcess, so trees that depend on this cannot be build using this method
|
||||
void AppendAndMerge(glm::uvec3 coordinates, unsigned8 appendLevel, Tree* tree)
|
||||
{
|
||||
// Let some possible inhereting class do preprocessing on the current tree before append
|
||||
AppendPreProcess(coordinates, appendLevel, tree);
|
||||
|
||||
this->SortNodes();
|
||||
std::vector<unsigned32> levelIndices = this->SortOnLevel();
|
||||
std::vector<unsigned32> appendedTreeLevelIndices = tree->SortOnLevel();
|
||||
|
||||
// First create/get the node that acts as the root of the tree to append
|
||||
NodeType* treeRoot = this->AddNode(coordinates, appendLevel);
|
||||
|
||||
// Contains at the index of the node in the tree that is to be appended, the index of the replacement node in the pool
|
||||
std::vector<unsigned32> replacementMap(tree->GetNodeCount(), 0);
|
||||
replacementMap[0] = treeRoot->GetIndex();
|
||||
for (unsigned8 level = GetMaxLevel(); level > appendLevel; level--)
|
||||
{
|
||||
// Sort all the nodes in the current level of the other tree
|
||||
unsigned32 levelStart = levelIndices[level];
|
||||
unsigned32 levelEnd = levelIndices[level + 1];
|
||||
unsigned32 appendedLevelStart = appendedTreeLevelIndices[level - appendLevel];
|
||||
unsigned32 appendedLevelEnd = appendedTreeLevelIndices[level - appendLevel + 1];
|
||||
|
||||
//std::vector<NodeType&> existingNodes(levelEnd - levelStart);
|
||||
//for (unsigned32 i = levelStart; i < levelEnd; i++) existingNodes[i - levelStart] = GetTypedNode(i);
|
||||
|
||||
//tbb::parallel_sort(existingNodes.begin(), existingNodes.end(), NodeComparer());
|
||||
|
||||
// Vector of all nodes that need to be appended
|
||||
std::vector<unsigned32> toAppend(appendedLevelEnd - appendedLevelStart);
|
||||
for (unsigned32 i = appendedLevelStart; i < appendedLevelEnd; i++) toAppend[i - appendedLevelStart] = i;
|
||||
|
||||
tbb::parallel_for_each(toAppend.begin(), toAppend.end(), [&](const unsigned32 i)
|
||||
{
|
||||
NodeType* node = tree->GetTypedNode(i);
|
||||
unsigned32* children = node->GetChildren();
|
||||
ChildIndex childCount = node->GetChildCount();
|
||||
// Make sure the node looks exactly like how it would look if it was in the current tree:
|
||||
for (ChildIndex c = 0; c < childCount; c++)
|
||||
children[c] = replacementMap[children[c]];
|
||||
node->SetLevel(level);
|
||||
node->MoveToTree(this);
|
||||
});
|
||||
|
||||
// Sort the nodes in the same way the existing nodes are sorted
|
||||
//NodeComparer comparer();
|
||||
tbb::parallel_sort(toAppend.begin(), toAppend.end(), [&](const unsigned32 i1, const unsigned32 i2)
|
||||
{
|
||||
NodeType* node1 = tree->GetTypedNode(i1);
|
||||
NodeType* node2 = tree->GetTypedNode(i2);
|
||||
return node1->Compare(*node2);
|
||||
});
|
||||
|
||||
// Go through the nodes in this order.
|
||||
unsigned32 existingIndex = 0;
|
||||
for (const unsigned32 i : toAppend)
|
||||
{
|
||||
NodeType* node = tree->GetTypedNode(i);
|
||||
// Scan the existing nodes until they are no longer smaller than the current node
|
||||
while (existingIndex < (levelEnd - levelStart) && GetTypedNode(levelStart + existingIndex)->Compare(*node))
|
||||
existingIndex++;
|
||||
// If the node at that position is equal to the current node, then delete the current node
|
||||
if (existingIndex < (levelEnd - levelStart) && GetTypedNode(levelStart + existingIndex)->Equals(*node))
|
||||
{
|
||||
replacementMap[node->GetIndex()] = levelStart + existingIndex;
|
||||
tree->Destroy(node);
|
||||
}
|
||||
else // Otherwise, add this node to the pool
|
||||
{
|
||||
// Create a copy of the node
|
||||
node = mNodePool.Add(node);
|
||||
tree->Destroy(node->GetIndex());
|
||||
unsigned32 newNodeIndex = GetNodeCount() - 1;
|
||||
replacementMap[node->GetIndex()] = newNodeIndex;
|
||||
node->SetIndex(newNodeIndex);
|
||||
mSortedOnLevel = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reconnect the children of the replacement root
|
||||
unsigned8 childCount = tree->GetRoot()->GetChildCount();
|
||||
unsigned32* children = tree->GetRoot()->GetChildren();
|
||||
for (ChildIndex c = 0; c < childCount; c++)
|
||||
children[c] = replacementMap[children[c]];
|
||||
treeRoot->SetChildren(tree->GetRoot()->GetChildmask(), children);
|
||||
|
||||
tree->Clear();
|
||||
}
|
||||
|
||||
// Moves the given tree to the this tree, replacing the contents of the this try by that of the given tree.
|
||||
// Note that all nodes are recreated, because a tree with a different subclass of node may be moved to this one.
|
||||
// This means that all properties of the original tree will be lost!
|
||||
void MoveShallow(BaseTree* tree)
|
||||
{
|
||||
// Update the current tree to be similar to the source tree
|
||||
Clear();
|
||||
this->mMaxLevel = tree->GetMaxLevel();
|
||||
|
||||
// Create copies of the old tree and put them in the new tree, with correct childpointers
|
||||
for (unsigned32 i = 0; i < tree->GetNodeCount(); i++)
|
||||
{
|
||||
Node* original = tree->GetNode(i);
|
||||
NodeType* replacer = mNodePool.Create(this, original->GetLevel());
|
||||
replacer->SetChildren(original->GetChildmask(), original->GetChildren());
|
||||
//tree->Destroy(i);
|
||||
}
|
||||
|
||||
// Clear the original tree
|
||||
tree->Clear();
|
||||
}
|
||||
|
||||
// Sorts the nodes on their level, and returns a list of start indices per level
|
||||
std::vector<unsigned32> SortOnLevel() override
|
||||
{
|
||||
// Sort nodes on level (ignore the root = first node in pool)
|
||||
if (!mSortedOnLevel)
|
||||
{
|
||||
//mNodePool.Sort(NodeComparer());
|
||||
mNodePool.Sort(1, GetNodeCount(), [](const NodeType& a, const NodeType& b) { return a.GetLevel() < b.GetLevel(); });
|
||||
//tbb::parallel_sort(mNodePool.begin() + 1, mNodePool.end(), [](NodeType* a, NodeType* b) { return a->GetLevel() < b->GetLevel(); });
|
||||
|
||||
UpdateNodeIndices(GetNodeCount());
|
||||
|
||||
mSortedOnLevel = true;
|
||||
}
|
||||
|
||||
// Find the start indices of all levels in the node pool
|
||||
std::vector<unsigned32> levelIndices(GetMaxLevel() + 1);
|
||||
unsigned8 curLevel = 255;
|
||||
for (unsigned32 i = 0; i < GetNodeCount(); i++)
|
||||
{
|
||||
NodeType* node = GetTypedNode(i);
|
||||
if (node->GetLevel() != curLevel) {
|
||||
curLevel = node->GetLevel();
|
||||
levelIndices[curLevel] = node->GetIndex();
|
||||
}
|
||||
}
|
||||
// If for some reason, there are no nodes on every level, fill the rest of the level offsets as if there were these nodes...
|
||||
for (unsigned8 level = curLevel + 1; level <= GetMaxLevel(); level++) levelIndices[level] = GetNodeCount();
|
||||
|
||||
// Add a dummy value at the end
|
||||
levelIndices.push_back(GetNodeCount());
|
||||
|
||||
return levelIndices;
|
||||
}
|
||||
|
||||
void SortNodes()
|
||||
{
|
||||
std::vector<unsigned32> levelIndices = SortOnLevel();
|
||||
for (unsigned8 level = 1; level <= GetMaxLevel(); level++) this->SortBetween(levelIndices[level], levelIndices[level + 1], NodeComparer());
|
||||
UpdateNodeIndices(GetNodeCount());
|
||||
}
|
||||
|
||||
void ToDAG(unsigned8 fromLevel = 0, bool verbose = true)
|
||||
{
|
||||
if (this->GetNodeCount() == 1)
|
||||
return; // Empty tree = DAG
|
||||
|
||||
// Sort the nodes on level (for quick access of nodes within a level).
|
||||
// Note that we can't sort on the node comparer just yet, as the nodes will change during the ToDAG process.
|
||||
auto levelIndices = SortOnLevel();
|
||||
|
||||
mMaxLevel = (unsigned8)(levelIndices.size() - 2);
|
||||
if (fromLevel > GetMaxLevel()) return;
|
||||
|
||||
unsigned32 maxOldIndex = GetNodeCount() - 1;
|
||||
|
||||
// Run through the layers in reverse order and compress them bottom up
|
||||
for (unsigned8 level = GetMaxLevel(); level > fromLevel; --level)
|
||||
{
|
||||
if (verbose) printf(".");
|
||||
// Initialize some variables needed
|
||||
unsigned32 levelStartIndex = levelIndices[level];
|
||||
unsigned32 levelEndIndex = levelIndices[level + 1];
|
||||
unsigned32 parentLevelStartIndex = levelIndices[level - 1];
|
||||
unsigned32 levelNodeCount = levelEndIndex - levelStartIndex;
|
||||
unsigned32 deletedCount = 0;
|
||||
|
||||
bool fullRead = !mLeafsAreEqual || level != GetMaxLevel();
|
||||
|
||||
if (fullRead)
|
||||
{
|
||||
// Sort nodes using the node comparer
|
||||
mNodePool.Sort(levelStartIndex, levelEndIndex, NodeComparer());
|
||||
if (verbose) printf(".");
|
||||
|
||||
// Find unique nodes and store which node duplicates which
|
||||
NodeType* cur = NULL;
|
||||
std::vector<unsigned32> replacements(levelNodeCount);
|
||||
size_t uniqueNodes = 0;
|
||||
for (unsigned32 i = levelStartIndex; i < levelEndIndex; i++)
|
||||
{
|
||||
NodeType* node = GetTypedNode(i);
|
||||
if (cur == NULL || !node->Equals(*cur))
|
||||
{
|
||||
uniqueNodes++;
|
||||
cur = node;
|
||||
}
|
||||
replacements[node->GetIndex() - levelStartIndex] = cur->GetIndex();
|
||||
}
|
||||
if (verbose) printf(".");
|
||||
|
||||
// Point all parents of nodes to the new replacement node
|
||||
// Note that this is only necessary if some nodes are replaced by others
|
||||
if (uniqueNodes < levelNodeCount)
|
||||
{
|
||||
tbb::parallel_for(parentLevelStartIndex, levelStartIndex, [&](const unsigned32 i)
|
||||
{
|
||||
Node* parent = GetTypedNode(i);
|
||||
unsigned32* children = parent->GetChildren();
|
||||
unsigned8 childCount = parent->GetChildCount();
|
||||
for (ChildIndex c = 0; c < childCount; c++)
|
||||
children[c] = replacements[children[c] - levelStartIndex];
|
||||
});
|
||||
if (verbose) printf(".");
|
||||
for (unsigned32 i = levelStartIndex; i < levelEndIndex; i++)
|
||||
{
|
||||
unsigned32 nodeIndex = GetTypedNode(i)->GetIndex();
|
||||
// If the node is not replaced by it's own index, then delete it
|
||||
if (replacements[nodeIndex - levelStartIndex] != nodeIndex)
|
||||
{
|
||||
Destroy(i);
|
||||
deletedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (verbose) printf(".");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (verbose) printf(".");
|
||||
// For the leaf nodes (e.g. !fullRead), just add the first leaf node in the DAGNodePool, and replace the rest by it.
|
||||
NodeType* replacer = GetTypedNode(levelStartIndex);
|
||||
if (verbose) printf(".");
|
||||
if (levelNodeCount > 1)
|
||||
{
|
||||
tbb::parallel_for(parentLevelStartIndex, levelStartIndex, [&](const unsigned32 i)
|
||||
{
|
||||
NodeType* parent = GetTypedNode(i);
|
||||
unsigned32* children = parent->GetChildren();
|
||||
unsigned8 childCount = parent->GetChildCount();
|
||||
for (ChildIndex c = 0; c < childCount; c++)
|
||||
children[c] = levelStartIndex;
|
||||
});
|
||||
if (verbose) printf(".");
|
||||
for (unsigned32 i = levelStartIndex + 1; i < levelEndIndex; i++)
|
||||
{
|
||||
Destroy(i);
|
||||
deletedCount++;
|
||||
}
|
||||
}
|
||||
else if (verbose) printf(".");
|
||||
}
|
||||
if (verbose) printf(" Layer %2u compressed, %7u out of %7u nodes left\n", level, levelNodeCount - deletedCount, levelNodeCount);
|
||||
}
|
||||
Clean();
|
||||
}
|
||||
|
||||
void ToOctree(unsigned8 fromLevel = 0)
|
||||
{
|
||||
auto levelIndices = SortOnLevel();
|
||||
BoolArray usedNodes(GetNodeCount(), false);
|
||||
unsigned32 newLevelStart = GetNodeCount();
|
||||
unsigned32 newLevelEnd = GetNodeCount();
|
||||
for (auto level = fromLevel; level < GetMaxLevel(); level++)
|
||||
{
|
||||
// Update start and end index of the nodes that were created during the last iteration
|
||||
newLevelStart = newLevelEnd;
|
||||
newLevelEnd = GetNodeCount();
|
||||
|
||||
// Find indices of existing nodes for each level
|
||||
unsigned32 levelStart = levelIndices[level];
|
||||
unsigned32 levelEnd = levelIndices[level + 1];
|
||||
|
||||
// Process all nodes
|
||||
for (unsigned32 i = levelStart; i < newLevelEnd; i++)
|
||||
{
|
||||
// Skip nodes of other levels
|
||||
if (i == levelEnd) i = newLevelStart;
|
||||
|
||||
NodeType* node = GetTypedNode(i);
|
||||
|
||||
unsigned32* children = node->GetChildren();
|
||||
unsigned8 childCount = node->GetChildCount();
|
||||
|
||||
for (ChildIndex c = 0; c < childCount; c++)
|
||||
{
|
||||
if (!usedNodes[children[c]])
|
||||
{
|
||||
// If this node wasn't used yet, we can keep the same child.
|
||||
usedNodes.Set(children[c], true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If this node was used before, we need to create a new one
|
||||
auto oldNode = GetTypedNode(children[c]);
|
||||
auto newNode = Create(level + 1);
|
||||
newNode->SetChildren(oldNode->GetChildmask(), oldNode->GetChildren());
|
||||
newNode->CopyProperties(oldNode);
|
||||
children[c] = newNode->GetIndex();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClearOrphans()
|
||||
{
|
||||
BoolArray orphans(GetNodeCount(), true);
|
||||
orphans.Set(0, false);
|
||||
size_t orphansCleared = 0;
|
||||
bool firstPass = true;
|
||||
while (orphans.Any())
|
||||
{
|
||||
// Assume all nodes (except the root) are orphans
|
||||
orphans.SetRange(1, GetNodeCount(), true);
|
||||
for (unsigned32 i = 0; i < GetNodeCount(); i++)
|
||||
{
|
||||
NodeType* node = GetTypedNode(i);
|
||||
if (node == NULL) // Empty nodes are not orphans
|
||||
orphans.Set(i, false);
|
||||
else
|
||||
{
|
||||
// Roots are not orphans (by definition, they're just roots)
|
||||
if (node->GetLevel() == 0)
|
||||
orphans.Set(i, false);
|
||||
|
||||
// All the nodes that are children of this node can not be orphans (since this node still lives)
|
||||
for (ChildIndex child = 0; child < node->GetChildCount(); child++)
|
||||
orphans.Set(node->GetChildren()[child], false);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete nodes that are orphans. Note that this might cause other nodes to be orphaned as well
|
||||
// In other words: if you're Batman's child, and Batman gets killed, then you are an orphan as well ;)
|
||||
for (unsigned32 i = 1; i < GetNodeCount(); i++)
|
||||
if (orphans.Get(i))
|
||||
{
|
||||
orphansCleared++;
|
||||
Destroy(i);
|
||||
}
|
||||
}
|
||||
|
||||
printf("%llu orphans have been removed.\n", (unsigned64)orphansCleared);
|
||||
|
||||
// Clean the nodepool to really remove all orphans
|
||||
Clean();
|
||||
}
|
||||
|
||||
// Finds all parent of a node (O(N)).
|
||||
std::vector<std::pair<const NodeType*, ChildIndex>> FindParents(const NodeType* node) const
|
||||
{
|
||||
tbb::concurrent_queue<std::pair<const NodeType*, ChildIndex>> parents;
|
||||
unsigned32 childToFind = node->GetIndex();
|
||||
unsigned8 parentLevel = node->GetLevel() - 1;
|
||||
tbb::parallel_for((unsigned32)0, GetNodeCount(), [&](const unsigned32 i)
|
||||
{
|
||||
NodeType* node = GetTypedNode(i);
|
||||
if (node->GetLevel() == parentLevel)
|
||||
{
|
||||
for (ChildIndex child = 0; child < 8; child++)
|
||||
if (node->HasChild(child) && node->GetChildIndex(child) == childToFind)
|
||||
parents.push(std::pair<const NodeType*, ChildIndex>(node, child));
|
||||
}
|
||||
});
|
||||
std::vector<std::pair<const NodeType*, ChildIndex>> res;
|
||||
for (auto parent = parents.unsafe_begin(); parent != parents.unsafe_end(); parent++)
|
||||
res.push_back(*parent);
|
||||
return res;
|
||||
}
|
||||
|
||||
// Cuts off all nodes that have a level higher than depth.
|
||||
void Shave(unsigned8 depth) override
|
||||
{
|
||||
if (depth >= GetMaxLevel()) return;
|
||||
|
||||
auto levelIndices = SortOnLevel();
|
||||
|
||||
// Make sure all nodes at level "depth" have no children (since we're gonna shave them off)
|
||||
const unsigned* emptyChildren = new unsigned[0];
|
||||
tbb::parallel_for(levelIndices[depth], levelIndices[depth + 1], [&](const unsigned32 i)
|
||||
{
|
||||
GetTypedNode(i)->SetChildren(ChildMask(0), emptyChildren);
|
||||
});
|
||||
delete emptyChildren;
|
||||
|
||||
// Delete all nodes that have a higher level than depth
|
||||
for (unsigned32 i = levelIndices[depth + 1]; i < GetNodeCount(); i++)
|
||||
Destroy(i);
|
||||
|
||||
// Resize the node pool so that no reference to the destroyed nodes exist
|
||||
mNodePool.Clean();
|
||||
mMaxLevel = depth;
|
||||
}
|
||||
|
||||
virtual void PrintDebugInfo() const
|
||||
{
|
||||
printf("NodeCount: %u\n", GetNodeCount());
|
||||
unsigned64 leafVoxelCount = GetLeafVoxelCount();
|
||||
printf("Leaf voxel count: %llu\n", leafVoxelCount);
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
void Destroy(unsigned32 index) override { mNodePool.Delete(index); }
|
||||
virtual void Destroy(NodeType* node)
|
||||
{
|
||||
// Assert that we are actually deleting the correct node
|
||||
if (node->GetIndex() < GetNodeCount() && GetTypedNode(node->GetIndex()) == node)
|
||||
mNodePool.Delete(node->GetIndex());
|
||||
else
|
||||
printf("Error: Can't delete, node with ID %u not located at that position", node->GetIndex());
|
||||
}
|
||||
|
||||
virtual void InitializeReadTree() {}
|
||||
virtual void FinalizeReadTree() {}
|
||||
|
||||
virtual void AppendPreProcess(glm::uvec3 coordinates, unsigned8 level, Tree* tree) {}
|
||||
virtual void AppendPostProcess(glm::uvec3 coordinates, unsigned8 level, Tree* tree) {}
|
||||
|
||||
// Sorts a subset of the nodes, but DOES NOT update the indices! Make sure to call UpdateNodeIndices at some point!
|
||||
template<typename Comparer>
|
||||
void SortBetween(unsigned32 startIndex, unsigned32 endIndex, const Comparer& comparer = NodeComparer())
|
||||
{
|
||||
if (startIndex >= endIndex || startIndex >= GetNodeCount()) return; // Nothing to sort
|
||||
|
||||
mNodePool.Sort(startIndex, endIndex, comparer);
|
||||
|
||||
// Check if the given range is monotonically increasing, to make sure the ndoes are still sorted on level (if they were before)
|
||||
if (mSortedOnLevel)
|
||||
{
|
||||
unsigned8 wantedLevel = GetTypedNode(startIndex)->GetLevel();
|
||||
for (unsigned32 i = startIndex; i < endIndex; i++)
|
||||
{
|
||||
unsigned8 level = GetTypedNode(i)->GetLevel();
|
||||
if (level != wantedLevel)
|
||||
{
|
||||
if (level > wantedLevel) wantedLevel = level;
|
||||
else
|
||||
{
|
||||
mSortedOnLevel = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//std::vector<NodeType*> GetNodePoolCopy() const
|
||||
//{
|
||||
// std::vector<NodeType*> nodePoolCopy(mNodePool.size());
|
||||
// std::copy(mNodePool.begin(), mNodePool.end(), nodePoolCopy.begin());
|
||||
// return nodePoolCopy;
|
||||
//}
|
||||
|
||||
// Updates the current index value of all nodes to the value prescribed by the current nodepool.
|
||||
// oldMaxIndex is needed to know the size of the map that maps old to new indices.
|
||||
// If oldMaxIndex is not provided or 0, it will be calculated, requiring an additional pass
|
||||
// through the nodes
|
||||
void UpdateNodeIndices(unsigned32 oldMaxIndex = 0)
|
||||
{
|
||||
if (oldMaxIndex == 0)
|
||||
{
|
||||
// oldMaxIndex = max(oldIndices)
|
||||
for (unsigned32 i = 0; i < GetNodeCount(); i++)
|
||||
if (GetTypedNode(i)->GetIndex() > oldMaxIndex)
|
||||
oldMaxIndex = GetTypedNode(i)->GetIndex();
|
||||
oldMaxIndex++;
|
||||
}
|
||||
// Update the indices in all nodes, store a map of where nodes have been moved
|
||||
std::vector<unsigned32> indexMap(oldMaxIndex + 1);
|
||||
tbb::parallel_for((unsigned32)0, GetNodeCount(), [&](const unsigned32 i)
|
||||
{
|
||||
NodeType* node = GetTypedNode(i);
|
||||
if (node != NULL)
|
||||
{
|
||||
indexMap[node->GetIndex()] = i;
|
||||
node->SetIndex(i);
|
||||
}
|
||||
});
|
||||
// Update the child indices based on the node movement map
|
||||
//tbb::parallel_for((unsigned32)0, GetNodeCount(), [&](const unsigned32 i)
|
||||
for (unsigned32 i = 0; i < GetNodeCount(); i++)
|
||||
{
|
||||
NodeType* node = GetTypedNode(i);
|
||||
if (node != NULL)
|
||||
{
|
||||
const unsigned8 nodeChildCount = node->GetChildCount();
|
||||
unsigned32* children = node->GetChildren();
|
||||
for (ChildIndex c = 0; c < nodeChildCount; c++)
|
||||
children[c] = indexMap[children[c]];
|
||||
}
|
||||
}//);
|
||||
|
||||
UpdateLocalReferences(indexMap);
|
||||
}
|
||||
|
||||
virtual void UpdateLocalReferences(const std::vector<unsigned32>& indexMap)
|
||||
{
|
||||
if (mLeafsAreEqual)
|
||||
mLeafNode = indexMap[mLeafNode];
|
||||
}
|
||||
|
||||
struct NodeComparer
|
||||
{
|
||||
bool operator()(const NodeType& a, const NodeType& b) const
|
||||
{
|
||||
return a.Compare(b);
|
||||
}
|
||||
};
|
||||
|
||||
virtual void WriteAdditionalPoolProperties(std::ostream& file) {}
|
||||
virtual void ReadAdditionalPoolProperties(std::istream& file) {}
|
||||
|
||||
unsigned32 mLeafNode;
|
||||
bool mLeafsAreEqual;
|
||||
|
||||
unsigned8 mMaxLevel; // size of the texture in which the node pool will be stored
|
||||
private:
|
||||
ObjectPool<NodeType> mNodePool; // the node pool containing all nodes
|
||||
bool mSortedOnLevel;
|
||||
//ObjectPool<NodeType>* mNodeObjectPool; // The Node ObjectPool, used to reuse nodes instead of new and delete all the time
|
||||
};
|
||||
|
||||
185
Research/scene/Octree/UniqueIndexShiftTree.h
Normal file
185
Research/scene/Octree/UniqueIndexShiftTree.h
Normal file
@@ -0,0 +1,185 @@
|
||||
#pragma once
|
||||
#include "UniqueIndexTree.h"
|
||||
#include "../Material/SignedIntMaterial.h"
|
||||
#include "MaterialTree.h"
|
||||
#include "../Material/MaterialLibrary.h"
|
||||
#include "../../inc/tbb/parallel_for.h"
|
||||
#include "IMaterialTexture.h"
|
||||
#include<vector>
|
||||
|
||||
template<typename T, typename Comparer = std::less<T>, unsigned8 channelsPerPixel = 3>
|
||||
class UniqueIndexShiftTree : public UniqueIndexTree<SignedIntMaterial>, public IMaterialTexture
|
||||
{
|
||||
private:
|
||||
MaterialLibrary<T, Comparer, channelsPerPixel>* mMaterialLibrary;
|
||||
std::vector<unsigned8> mMaterialTexture;
|
||||
unsigned16 mMaterialTextureSize;
|
||||
MaterialLibraryPointer mMaxTextureIndex;
|
||||
|
||||
void ClearMaterialLibrary()
|
||||
{
|
||||
if (mMaterialLibrary != NULL)
|
||||
{
|
||||
delete mMaterialLibrary;
|
||||
mMaterialLibrary = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
inline void WriteMaterialTexture(std::ostream& file)
|
||||
{
|
||||
assert(mMaterialLibrary != NULL);
|
||||
mMaterialLibrary->Serialize(file);
|
||||
}
|
||||
|
||||
inline void ReadMaterialTexture(std::istream& file)
|
||||
{
|
||||
if (mMaterialLibrary == NULL)
|
||||
mMaterialLibrary = new MaterialLibrary<T, Comparer, channelsPerPixel>();
|
||||
mMaterialLibrary->Deserialize(file);
|
||||
GetMaterialTexture();
|
||||
}
|
||||
|
||||
void BuildMaterialLibrary(const std::vector<T>& materials)
|
||||
{
|
||||
ClearMaterialLibrary();
|
||||
mMaterialLibrary = new MaterialLibrary<T, Comparer, channelsPerPixel>();
|
||||
for (T m : materials) mMaterialLibrary->AddMaterial(m);
|
||||
mMaterialLibrary->Finalize();
|
||||
}
|
||||
|
||||
bool CheckMaterialLibrary(const std::vector<T>& materials)
|
||||
{
|
||||
for (T m : materials) if (!mMaterialLibrary->Contains(m)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
UniqueIndexShiftTree(unsigned8 maxLevel, CompressedTexture<SignedIntMaterial>* nodeMaterialsTexture, unsigned32 collapsedMaterialLevels) :
|
||||
UniqueIndexTree(maxLevel, nodeMaterialsTexture, collapsedMaterialLevels),
|
||||
mMaterialLibrary(NULL),
|
||||
mMaterialTexture(std::vector<unsigned8>()),
|
||||
mMaterialTextureSize(0),
|
||||
mMaxTextureIndex(MaterialLibraryPointer(0))
|
||||
{}
|
||||
|
||||
UniqueIndexShiftTree(unsigned8 maxLevel, CompressedTexture<SignedIntMaterial>* nodeMaterialsTexture)
|
||||
: UniqueIndexShiftTree(maxLevel, nodeMaterialsTexture, 0)
|
||||
{}
|
||||
|
||||
~UniqueIndexShiftTree() override {
|
||||
ClearMaterialLibrary();
|
||||
}
|
||||
|
||||
void AppendPostProcess(glm::uvec3 coordinates, unsigned8 level, Tree<UniqueIndexNode>* tree) override
|
||||
{
|
||||
UniqueIndexShiftTree<T, Comparer, channelsPerPixel>* typedTree = (UniqueIndexShiftTree<T, Comparer, channelsPerPixel>*)tree;
|
||||
// If the material libraries are equal, appending should work correctly
|
||||
assert(*typedTree->GetMaterialLibrary() == *mMaterialLibrary);
|
||||
UniqueIndexTree::AppendPostProcess(coordinates, level, typedTree, std::unordered_map<SignedIntMaterial, SignedIntMaterial>());
|
||||
}
|
||||
|
||||
// Prepare the tree for a certain set of materials. This should be done before calling "BaseOn" or otherwise adding nodes to the tree.
|
||||
void PrepareForMaterials(const std::vector<T>& materials)
|
||||
{
|
||||
assert(GetNodeCount() == 1);
|
||||
BuildMaterialLibrary(materials);
|
||||
}
|
||||
|
||||
template<typename MaterialTree>
|
||||
void BaseOn(MaterialTree* tree)
|
||||
{
|
||||
// First we build a new material tree that contains the differences in the materials compared to the parents for each node (kind of watch hsc is)
|
||||
// To do this, we need an octree (as the same color doesn't mean it will have the same difference to it's parent)
|
||||
tree->ToOctree();
|
||||
std::vector<T> uniqueMaterials = tree->GetUniqueMaterials();
|
||||
|
||||
// Create the actual material library
|
||||
if (mMaterialLibrary == NULL)
|
||||
BuildMaterialLibrary(uniqueMaterials);
|
||||
else
|
||||
assert(CheckMaterialLibrary(uniqueMaterials));
|
||||
|
||||
// Now we calculate the shift for each node in the tree
|
||||
std::vector<int> intMaterialPointer(tree->GetNodeCount());
|
||||
tbb::parallel_for((unsigned32)0, tree->GetNodeCount(), [&](unsigned32 i)
|
||||
{
|
||||
auto node = tree->GetTypedNode(i);
|
||||
MaterialLibraryPointer matPointer = mMaterialLibrary->GetTextureIndex(tree->GetMaterial(node));
|
||||
intMaterialPointer[i] = (matPointer.y * mMaterialLibrary->GetTextureSize()) + matPointer.x;
|
||||
});
|
||||
std::vector<SignedIntMaterial> shiftMaterials(tree->GetNodeCount());
|
||||
shiftMaterials[0] = intMaterialPointer[0];
|
||||
tbb::parallel_for((unsigned32)0, tree->GetNodeCount(), [&](unsigned32 i)
|
||||
//for (size_t i = 0; i < tree->GetNodeCount(); i++)
|
||||
{
|
||||
Node* node = tree->GetNode(i);
|
||||
int curIntIndex = intMaterialPointer[i];
|
||||
unsigned32* children = node->GetChildren();
|
||||
unsigned8 childCount = node->GetChildCount();
|
||||
for (ChildIndex c = 0; c < childCount; c++)
|
||||
{
|
||||
int childIntIndex = intMaterialPointer[children[c]];
|
||||
shiftMaterials[children[c]] = SignedIntMaterial(childIntIndex - curIntIndex);
|
||||
}
|
||||
});
|
||||
|
||||
std::vector<int> shiftsCopy(shiftMaterials.size());
|
||||
for (size_t i = 0; i < shiftMaterials.size(); i++) shiftsCopy[i] = shiftMaterials[i];
|
||||
|
||||
CollectionHelper::PrintStats(shiftsCopy);
|
||||
for (size_t i = 0; i < shiftsCopy.size(); i++) shiftsCopy[i] = abs(shiftsCopy[i]);
|
||||
CollectionHelper::PrintStats(shiftsCopy);
|
||||
|
||||
UniqueIndexTree::BaseOn(tree, shiftMaterials);
|
||||
}
|
||||
|
||||
std::vector<T> GetUniqueMaterials() const
|
||||
{
|
||||
return mMaterialLibrary->GetMaterials();
|
||||
}
|
||||
|
||||
MaterialLibrary<T, Comparer, channelsPerPixel>* GetMaterialLibrary() const
|
||||
{
|
||||
return mMaterialLibrary;
|
||||
}
|
||||
|
||||
// Returns the texture containing all materials once
|
||||
std::vector<unsigned8> GetMaterialTexture() override
|
||||
{
|
||||
if (!mMaterialTexture.empty())
|
||||
return mMaterialTexture;
|
||||
assert(mMaterialLibrary->IsFinalized());
|
||||
mMaterialTextureSize = mMaterialLibrary->GetTextureSize();
|
||||
mMaterialTexture = mMaterialLibrary->GetTexture();
|
||||
mMaxTextureIndex = mMaterialLibrary->GetMaxTextureIndex();
|
||||
return mMaterialTexture;
|
||||
}
|
||||
|
||||
unsigned GetMaterialTextureSize() override
|
||||
{
|
||||
GetMaterialTexture();
|
||||
return mMaterialTextureSize;
|
||||
}
|
||||
|
||||
unsigned8 GetMaterialTextureChannelsPerPixel() override { return channelsPerPixel; }
|
||||
|
||||
protected:
|
||||
void WriteAdditionalUniqueIndexTreeProperties(std::ostream& file) override {
|
||||
WriteMaterialTexture(file);
|
||||
}
|
||||
|
||||
void ReadAdditionalUniqueIndexTreeProperties(std::istream& file) override {
|
||||
ReadMaterialTexture(file);
|
||||
}
|
||||
|
||||
void WriteAdditionalPoolProperties(std::ostream& file) override { WriteMaterialTexture(file); UniqueIndexTree::WriteAdditionalPoolProperties(file); }
|
||||
void ReadAdditionalPoolProperties(std::istream& file) override { ReadMaterialTexture(file); UniqueIndexTree::ReadAdditionalPoolProperties(file); }
|
||||
|
||||
void PrintDebugInfo() const override
|
||||
{
|
||||
std::vector<SignedIntMaterial> shifts = GetNodeValues();
|
||||
std::vector<int> shiftValues(shifts.size());
|
||||
for (size_t i = 0; i < shifts.size(); i++) shiftValues[i] = (int)shifts[i];
|
||||
CollectionHelper::PrintStats(shiftValues);
|
||||
}
|
||||
};
|
||||
559
Research/scene/Octree/UniqueIndexTree.h
Normal file
559
Research/scene/Octree/UniqueIndexTree.h
Normal file
@@ -0,0 +1,559 @@
|
||||
#pragma once
|
||||
#include "Tree.h"
|
||||
#include "EdgeMaterialNode.h"
|
||||
#include "IBlockTexture.h"
|
||||
#include "IMaterialTexture.h"
|
||||
#include "IAdditionalProperties.h"
|
||||
#include "../../inc/tbb/parallel_for_each.h"
|
||||
#include "../../core/BitHelper.h"
|
||||
#include "../../core/Serializer.h"
|
||||
#include "../../core/Util/BoolArray.h"
|
||||
#include "../TextureCompressor/CompressedTexture.h"
|
||||
#include "../../inc/lodepng/lodepng.h"
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
#include <stack>
|
||||
|
||||
typedef EdgeMaterialNode<unsigned64> UniqueIndexNode;
|
||||
|
||||
// Comment or uncomment these defines to enable or disable offset/shift compression
|
||||
#define offset_sizes_per_level
|
||||
#define implicit_store_first_offset
|
||||
|
||||
template<typename T>
|
||||
class UniqueIndexTree : public Tree<UniqueIndexNode>, public IBlockTexture, public IAdditionalProperties
|
||||
{
|
||||
private:
|
||||
std::vector<unsigned8>* mShiftBytesPerLevel;
|
||||
|
||||
// After the tree is finalized, this vector contains the materialPointers for all nodes in the original octree
|
||||
CompressedTexture<T>* mNodeMaterials;
|
||||
unsigned8 mUniqueShiftLevel;
|
||||
|
||||
// Textures and pools
|
||||
std::vector<unsigned8> mBlockPointerPool;
|
||||
bool mBlockPointerPoolLoaded;
|
||||
std::vector<unsigned8> mBlockPool;
|
||||
bool mBlockPoolLoaded;
|
||||
std::map<std::string, std::string> mAdditionalProperties;
|
||||
bool mAdditionalPropertiesLoaded;
|
||||
|
||||
inline void WriteBlockPointerPool(std::ostream& file)
|
||||
{
|
||||
// Make sure the block pointer pool exists
|
||||
GetBlockPointerPool();
|
||||
|
||||
// Write it to the filestream
|
||||
Serializer<std::vector<unsigned8>, unsigned64>::Serialize(mBlockPointerPool, file);
|
||||
}
|
||||
|
||||
inline void ReadBlockPointerPool(std::istream& file)
|
||||
{
|
||||
// Read from filestream
|
||||
Serializer<std::vector<unsigned8>, unsigned64>::Deserialize(mBlockPointerPool, file);
|
||||
mBlockPointerPoolLoaded = true;
|
||||
}
|
||||
|
||||
inline void WriteBlockPool(std::ostream& file)
|
||||
{
|
||||
// Make sure the block pool exists
|
||||
GetBlockPool();
|
||||
|
||||
Serializer<std::vector<unsigned8>, unsigned64>::Serialize(mBlockPool, file);
|
||||
}
|
||||
|
||||
inline void ReadBlockPool(std::istream& file)
|
||||
{
|
||||
Serializer<std::vector<unsigned8>, unsigned64>::Deserialize(mBlockPool, file);
|
||||
mBlockPoolLoaded = true;
|
||||
}
|
||||
|
||||
inline void WriteAdditionalProperties(std::ostream& file)
|
||||
{
|
||||
// Make sure the additional properties are set.
|
||||
GetAdditionalProperties();
|
||||
|
||||
// Write them to a file
|
||||
Serializer<std::map<std::string, std::string>>::Serialize(mAdditionalProperties, file);
|
||||
}
|
||||
|
||||
inline void ReadAdditionalProperties(std::istream& file)
|
||||
{
|
||||
Serializer<std::map<std::string, std::string>>::Deserialize(mAdditionalProperties, file);
|
||||
mAdditionalPropertiesLoaded = true;
|
||||
}
|
||||
|
||||
void ClearPooledNodeMaterials()
|
||||
{
|
||||
if (mBlockPoolLoaded && mBlockPointerPoolLoaded && mAdditionalPropertiesLoaded) ClearNodeMaterials();
|
||||
}
|
||||
|
||||
void ClearNodeMaterials()
|
||||
{
|
||||
if (mNodeMaterials != NULL)
|
||||
{
|
||||
delete mNodeMaterials;
|
||||
mNodeMaterials = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned64 GetShift(const UniqueIndexNode* node, const ChildIndex& child) const
|
||||
{
|
||||
return node->GetEdgeMaterial(child);
|
||||
}
|
||||
|
||||
// Calculates for each node whether the edge pointing to it has a shift of zero on it
|
||||
// (incorrect if the tree is a DAG, as the results might be ambiguous if nodes have multiple parents).
|
||||
BoolArray GetNodesWithZeroShiftParents()
|
||||
{
|
||||
BoolArray hasZeroShiftParent(GetNodeCount());
|
||||
for (unsigned32 i = 1; i < GetNodeCount(); i++)
|
||||
{
|
||||
UniqueIndexNode* cur = (UniqueIndexNode*)GetNode(i);
|
||||
unsigned64* childShifts = cur->GetEdgeMaterials();
|
||||
unsigned32* children = cur->GetChildren();
|
||||
ChildIndex childCount = cur->GetChildCount();
|
||||
for (ChildIndex c = 0; c < childCount; c++)
|
||||
if (childShifts[c] == 0) hasZeroShiftParent.Set(children[c], true);
|
||||
}
|
||||
for (ChildIndex c = 0; c < GetChildCount(); c++)
|
||||
if (mChildShifts[c] == 0) hasZeroShiftParent.Set(mChildren[c], true);
|
||||
return hasZeroShiftParent;
|
||||
}
|
||||
|
||||
size_t GetHighestShiftSum(const UniqueIndexNode* node) const
|
||||
{
|
||||
// The first child is always the child with the highest shift
|
||||
if (node->HasChildren()) return node->GetEdgeMaterials()[0] + GetHighestShiftSum(GetTypedNode(node->GetChildren()[0]));
|
||||
else return 0;
|
||||
}
|
||||
|
||||
// Create a vector containing all materials from the original tree
|
||||
std::vector<T> GetMappedMaterialTexture(const BaseTree* tree, std::vector<T>& materialPerNode)
|
||||
{
|
||||
std::vector<T> materialTexture;
|
||||
// Go depth-first through all nodes in the tree and copy the materials as they are reached.
|
||||
std::stack<unsigned32> curStack;
|
||||
curStack.push(0); // Push the root index to the stack
|
||||
bool skipNext = false;
|
||||
while (!curStack.empty())
|
||||
{
|
||||
// Go to the next child
|
||||
unsigned node = curStack.top();
|
||||
const Node* cur = tree->GetNode(node);
|
||||
curStack.pop();
|
||||
if (!skipNext)
|
||||
materialTexture.push_back(materialPerNode[node]);
|
||||
else
|
||||
skipNext = false;
|
||||
|
||||
if (cur->GetLevel() < mUniqueShiftLevel)
|
||||
{
|
||||
// if a node has one child, don't update the index of the next node to process (as it will have the same color)
|
||||
//if (cur->GetChildCount() == 1)
|
||||
// skipNext = true;
|
||||
|
||||
// Push the children to the stack
|
||||
unsigned32* children = cur->GetChildren();
|
||||
unsigned8 childCount = cur->GetChildCount();
|
||||
for (ChildIndex c = 0; c < childCount; c++)
|
||||
curStack.push(children[c]);
|
||||
}
|
||||
}
|
||||
return materialTexture;
|
||||
}
|
||||
|
||||
// This method will move the layout of the given (material) tree to this tree.
|
||||
// Since UniqueIndexTrees only need one leaf, all other leafs will be discarded.
|
||||
// The tree will be deleted by this method (as it will be left in an undefined state otherwise)
|
||||
void MoveShallowWithOneLeaf(BaseTree* tree)
|
||||
{
|
||||
Clear();
|
||||
|
||||
std::vector<unsigned32> levelOffsets = tree->SortOnLevel();
|
||||
// Find the first leaf, the one that will replace all other leafs
|
||||
unsigned32 leafsStart = levelOffsets[GetMaxLevel()];
|
||||
if (leafsStart < tree->GetNodeCount())
|
||||
{
|
||||
// Delete all leafs except the first
|
||||
for (unsigned32 i = leafsStart + 1; i < tree->GetNodeCount(); i++) tree->Destroy(i);
|
||||
|
||||
// Now replace all pointers to leaf nodes with pointers to the first leaf.
|
||||
tbb::parallel_for(levelOffsets[GetMaxLevel() - 1], levelOffsets[GetMaxLevel()], [&](unsigned32 i)
|
||||
{
|
||||
Node* node = tree->GetNode(i);
|
||||
unsigned32* children = node->GetChildren();
|
||||
unsigned8 childCount = node->GetChildCount();
|
||||
for (ChildIndex c = 0; c < childCount; c++)
|
||||
children[c] = (unsigned32)leafsStart;
|
||||
});
|
||||
}
|
||||
|
||||
// Create new nodes to replace all nodes in the tree. Since we're doing this in order, the pointers will always be correct
|
||||
for (unsigned32 i = 0; i < std::min(leafsStart + 1, tree->GetNodeCount()); i++)
|
||||
{
|
||||
Node* node = tree->GetNode(i);
|
||||
UniqueIndexNode* replacement = Create(node->GetLevel());
|
||||
replacement->SetChildren(node->GetChildmask(), node->GetChildren());
|
||||
tree->Destroy(node->GetIndex());
|
||||
}
|
||||
|
||||
// Delete the given tree as it is now obsolete
|
||||
delete tree;
|
||||
}
|
||||
|
||||
void UpdateShifts(const unsigned8& untilLevel)
|
||||
{
|
||||
// Go bottom up through the tree, calculate the shifts in each node
|
||||
std::vector<unsigned64> highestShiftSums(GetNodeCount(), 0);
|
||||
if (untilLevel != GetMaxLevel()) // If the until level is not the max level, calculate the highest shift sums in the until level before calculating the shifts
|
||||
for (unsigned32 i = 0; i < GetNodeCount(); i++)
|
||||
{
|
||||
UniqueIndexNode* cur = GetTypedNode(i);
|
||||
if (cur->GetLevel() == untilLevel)
|
||||
highestShiftSums[i] = GetHighestShiftSum(cur);
|
||||
}
|
||||
|
||||
for (unsigned8 level = untilLevel; level-- > 0;)
|
||||
{
|
||||
//tbb::parallel_for((unsigned32)0, GetNodeCount(), [&](const unsigned32 i)
|
||||
for (unsigned32 i = 0; i < GetNodeCount(); i++)
|
||||
{
|
||||
UniqueIndexNode* cur = GetTypedNode(i);
|
||||
if (cur->GetLevel() == level)
|
||||
{
|
||||
unsigned8 childCount = cur->GetChildCount();
|
||||
unsigned64 curShift = 1;
|
||||
unsigned64* childShifts = new unsigned64[childCount];
|
||||
unsigned* curChildren = cur->GetChildren();
|
||||
// Only calculate non-zero shifts for the nodes that need this
|
||||
if (/*childCount == 1 ||*/ level >= mUniqueShiftLevel) {
|
||||
for (ChildIndex c = childCount; c-- > 0;)
|
||||
{
|
||||
childShifts[c] = 0;
|
||||
curShift += highestShiftSums[curChildren[c]];
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (ChildIndex c = childCount; c-- > 0;)
|
||||
{
|
||||
childShifts[c] = curShift;
|
||||
curShift += highestShiftSums[curChildren[c]] + 1;
|
||||
}
|
||||
}
|
||||
cur->SetEdgeMaterials(childShifts);
|
||||
delete childShifts;
|
||||
highestShiftSums[i] = curShift - 1;
|
||||
}
|
||||
}//);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<unsigned64> GetUniqueNodeIndices(const unsigned8& untilLevel) const
|
||||
{
|
||||
// Now go top down through the tree and calculate the indices of each node
|
||||
std::vector<unsigned64> uniqueNodeIndices(GetNodeCount(), 0);
|
||||
for (unsigned8 level = 0; level <= untilLevel; level++)
|
||||
{
|
||||
//tbb::parallel_for(unsigned32(1), GetNodeCount(), [&](const unsigned32& i)
|
||||
for (unsigned32 i = 0; i < GetNodeCount(); i++)
|
||||
{
|
||||
const UniqueIndexNode* cur = GetTypedNode(i);
|
||||
if (cur->GetLevel() == level)
|
||||
{
|
||||
unsigned8 childCount = cur->GetChildCount();
|
||||
unsigned64* childShifts = cur->GetEdgeMaterials();
|
||||
unsigned32* curChildren = cur->GetChildren();
|
||||
for (ChildIndex c = 0; c < childCount; c++)
|
||||
{
|
||||
unsigned64 childIndex = uniqueNodeIndices[i] + childShifts[c];
|
||||
uniqueNodeIndices[curChildren[c]] = childIndex;
|
||||
}
|
||||
}
|
||||
}//);
|
||||
}
|
||||
|
||||
return uniqueNodeIndices;
|
||||
}
|
||||
|
||||
public:
|
||||
// Creates a UniqueIndexRoot with "maxLevel" levels.
|
||||
// collapsedMaterialLevels are used to decide how many levels of the tree will have the same materials as their parents.
|
||||
// Note that the nodeMaterialsTexture will be deleted if the tree is deleted.
|
||||
UniqueIndexTree(unsigned8 maxLevel, CompressedTexture<T>* nodeMaterialsTexture, unsigned32 collapsedMaterialLevels) :
|
||||
Tree<UniqueIndexNode>(maxLevel),
|
||||
mShiftBytesPerLevel(NULL),
|
||||
mNodeMaterials(nodeMaterialsTexture),
|
||||
mUniqueShiftLevel(maxLevel - collapsedMaterialLevels),
|
||||
mBlockPointerPool(std::vector<unsigned8>()),
|
||||
mBlockPointerPoolLoaded(false),
|
||||
mBlockPool(std::vector<unsigned8>()),
|
||||
mBlockPoolLoaded(false),
|
||||
mAdditionalProperties(std::map<std::string, std::string>()),
|
||||
mAdditionalPropertiesLoaded(false)
|
||||
{
|
||||
mLeafsAreEqual = true;
|
||||
}
|
||||
|
||||
// Creates a UniqueIndexRoot with "maxLevel" levels.
|
||||
// Note that the nodeMaterialsTexture will be deleted if the tree is deleted.
|
||||
UniqueIndexTree(unsigned8 maxLevel, CompressedTexture<T>* nodeMaterialsTextureL)
|
||||
: UniqueIndexRoot(maxLevel, nodeMaterialsTexture, 0)
|
||||
{}
|
||||
|
||||
~UniqueIndexTree() override {
|
||||
if (mShiftBytesPerLevel != NULL)
|
||||
delete mShiftBytesPerLevel;
|
||||
ClearNodeMaterials();
|
||||
}
|
||||
|
||||
void SetNodeValues(const std::vector<T>& materialPointers, const size_t& fromIndex = 0)
|
||||
{
|
||||
mNodeMaterials->SetTexture(materialPointers, fromIndex);
|
||||
}
|
||||
|
||||
std::vector<T> GetNodeValues(size_t fromIndex = 0) const
|
||||
{
|
||||
return mNodeMaterials->GetTexture(fromIndex);
|
||||
}
|
||||
|
||||
T GetNodeValue(size_t i) const
|
||||
{
|
||||
return mNodeMaterials->operator[](i);
|
||||
}
|
||||
|
||||
size_t GetMaterialCount() const
|
||||
{
|
||||
return GetHighestShiftSum(GetRoot());
|
||||
}
|
||||
|
||||
// Call this method after appending.
|
||||
// Replacers should be a map from materials in the appended tree to materials in this tree.
|
||||
// If a certain item is not found in the replacer map, it will be ignored
|
||||
void AppendPostProcess(glm::uvec3 coordinates, unsigned8 level, UniqueIndexTree* tree, std::unordered_map<T, T> replacers)
|
||||
{
|
||||
// Unpack all the blocks after where the new blocks should be inserted
|
||||
std::vector<T> newNodeMaterials = tree->GetNodeValues(1);
|
||||
// Replace the material pointers from the other tree to pointers to the same materials in this tree
|
||||
tbb::parallel_for(size_t(0), newNodeMaterials.size(), [&](size_t i)
|
||||
{
|
||||
auto replacer = replacers.find(newNodeMaterials[i]);
|
||||
if (replacer != replacers.end())
|
||||
newNodeMaterials[i] = replacer->second;
|
||||
});
|
||||
|
||||
|
||||
// Copy the shifts from the root of the appended tree to the node in the new tree
|
||||
UniqueIndexNode* appendedRootCopy = (UniqueIndexNode*)this->AddNode(coordinates, level);
|
||||
|
||||
// Update the shifts in the current tree on the levels above the appended tree
|
||||
UpdateShifts(level + 1);
|
||||
auto uniqueIndices = GetUniqueNodeIndices(level + 1);
|
||||
|
||||
unsigned64 pointerIndex = uniqueIndices[appendedRootCopy->GetIndex()] + 1;
|
||||
std::vector<T> existingPointers = this->GetNodeValues(pointerIndex);
|
||||
assert(existingPointers.size() <= (size_t)((1 << level) * (1 << level) * (1 << level)));
|
||||
newNodeMaterials.insert(newNodeMaterials.end(), existingPointers.begin(), existingPointers.end());
|
||||
mNodeMaterials->SetTexture(newNodeMaterials, pointerIndex);
|
||||
|
||||
// Update the shift sizes per level to be the maximum of the current and the new tree
|
||||
for (unsigned8 l = 0; l < tree->GetMaxLevel(); l++)
|
||||
this->mShiftBytesPerLevel->at(l + level) = std::max(tree->mShiftBytesPerLevel->at(l), this->mShiftBytesPerLevel->at(l + level));
|
||||
}
|
||||
|
||||
void ReplaceMaterials(const std::unordered_map<T, T>& replacers)
|
||||
{
|
||||
mNodeMaterials->ReplaceMaterials(replacers);
|
||||
}
|
||||
|
||||
// Takes the current material texture and compresses it again
|
||||
void Recompress()
|
||||
{
|
||||
mNodeMaterials->Recompress();
|
||||
}
|
||||
|
||||
// Will build a unique index tree with the structure taken from root. The materials
|
||||
// should be given as an additional array that contains at the index of the node
|
||||
// it's material.
|
||||
// This will consume (and delete) both the given tree and the given material array!
|
||||
void BaseOn(BaseTree* tree, std::vector<T>& materialPerNode)
|
||||
{
|
||||
std::vector<T> nodeValues = GetMappedMaterialTexture(tree, materialPerNode);
|
||||
// Free some memory
|
||||
materialPerNode.clear();
|
||||
materialPerNode.shrink_to_fit();
|
||||
|
||||
MoveShallowWithOneLeaf(tree);
|
||||
UpdateShifts(GetMaxLevel());
|
||||
|
||||
// Assert that the maximum index that can be reached using the shifts is equal to the maximum index in the material texture
|
||||
assert(GetHighestShiftSum(GetRoot()) == nodeValues.size() - 1);
|
||||
|
||||
// Add all materials to the material table (BIG texture)
|
||||
SetNodeValues(nodeValues);
|
||||
|
||||
|
||||
// Let's check if the compression works correctly
|
||||
// Method 1: Unpack the whole material at once and check that:
|
||||
//auto check = GetNodeValues();
|
||||
//assert(check.size() == nodeMaterialPointers.size());
|
||||
//for (size_t i = 0; i < check.size(); i++)
|
||||
// assert(nodeMaterialPointers[i] == check[i]);
|
||||
|
||||
// Method 2: Unpack per value
|
||||
assert(mNodeMaterials->size() == nodeValues.size());
|
||||
for (size_t i = 0; i < mNodeMaterials->size(); i++)
|
||||
{
|
||||
T value = mNodeMaterials->operator[](i);
|
||||
assert(nodeValues[i] == value);
|
||||
}
|
||||
|
||||
CalculateShiftBytesPerLevel();
|
||||
}
|
||||
|
||||
void ReplaceCompressedTexture(CompressedTexture<T>* replacementCompressor)
|
||||
{
|
||||
std::vector<T> materials = GetNodeValues();
|
||||
delete mNodeMaterials;
|
||||
mNodeMaterials = replacementCompressor;
|
||||
SetNodeValues(materials);
|
||||
}
|
||||
|
||||
std::vector<unsigned8> GetBlockPointerPool() override
|
||||
{
|
||||
if (mBlockPointerPoolLoaded) return mBlockPointerPool;
|
||||
|
||||
GetBlockPointerPoolSize();
|
||||
mBlockPointerPool = mNodeMaterials->GetAdditionalTexturePool();
|
||||
mBlockPointerPoolLoaded = true;
|
||||
ClearPooledNodeMaterials();
|
||||
return mBlockPointerPool;
|
||||
}
|
||||
|
||||
// Assuming 3D texture with one pointer per node (for example GL_R32UI)
|
||||
size_t GetBlockPointerPoolSize() override
|
||||
{
|
||||
if (mBlockPointerPoolLoaded) return mBlockPointerPool.size();
|
||||
return mNodeMaterials->GetAdditionalTexturePoolSize();
|
||||
}
|
||||
|
||||
std::vector<unsigned8> GetBlockPool() override
|
||||
{
|
||||
if (mBlockPoolLoaded) return mBlockPool;
|
||||
|
||||
GetBlockPoolSize();
|
||||
mBlockPool = mNodeMaterials->GetTexturePool();
|
||||
mBlockPoolLoaded = true;
|
||||
ClearPooledNodeMaterials();
|
||||
return mBlockPool;
|
||||
}
|
||||
|
||||
size_t GetBlockPoolSize() override
|
||||
{
|
||||
if (mBlockPoolLoaded) return mBlockPool.size();
|
||||
return mNodeMaterials->GetTexturePoolSize();
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> GetAdditionalProperties() override
|
||||
{
|
||||
if (mAdditionalPropertiesLoaded) return mAdditionalProperties;
|
||||
mAdditionalProperties = mNodeMaterials->GetAdditionalProperties();
|
||||
mAdditionalPropertiesLoaded = true;
|
||||
ClearPooledNodeMaterials();
|
||||
return mAdditionalProperties;
|
||||
}
|
||||
|
||||
bool HasAdditionalPool() const override{ return true; }
|
||||
protected:
|
||||
void CalculateShiftBytesPerLevel()
|
||||
{
|
||||
if (mShiftBytesPerLevel != NULL) delete mShiftBytesPerLevel;
|
||||
#ifdef offset_sizes_per_level
|
||||
mShiftBytesPerLevel = new std::vector<unsigned8>(GetMaxLevel() + 1);
|
||||
std::vector<unsigned64> maxShiftPerLevel(GetMaxLevel() + 1);
|
||||
for (unsigned32 i = 0; i < GetNodeCount(); i++)
|
||||
{
|
||||
UniqueIndexNode* node = GetTypedNode(i);
|
||||
for (ChildIndex child = 0; child < 8; child++)
|
||||
{
|
||||
if (node->HasChild(child))
|
||||
{
|
||||
unsigned64 shift = GetShift(node, child);
|
||||
unsigned8 level = node->GetLevel();
|
||||
if (shift > maxShiftPerLevel[level]) maxShiftPerLevel[level] = shift;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned8 level = 0; level <= GetMaxLevel(); level++)
|
||||
(*mShiftBytesPerLevel)[level] = BitHelper::RoundToBytes(BitHelper::Log2Ceil(maxShiftPerLevel[level] + 1)) / 8;
|
||||
#else
|
||||
mShiftBytesPerLevel = new std::vector<unsigned8>(GetMaxLevel() + 1, 4);
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned8 GetAdditionalBytesPerPointer(unsigned8 level) const override
|
||||
{
|
||||
assert(mShiftBytesPerLevel != NULL);
|
||||
return mShiftBytesPerLevel->operator[](level);
|
||||
}
|
||||
|
||||
bool LastChildHasAdditionalBytes() const override {
|
||||
#ifdef implicit_store_first_offset
|
||||
return false;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<unsigned8> GetAdditionalPointerBytes(const Node* node, ChildIndex child) const override
|
||||
{
|
||||
return BitHelper::SplitInBytes(GetShift((UniqueIndexNode*)node, child), GetAdditionalBytesPerPointer(node->GetLevel()));
|
||||
}
|
||||
|
||||
virtual void WriteAdditionalUniqueIndexTreeProperties(std::ostream& file) {}
|
||||
virtual void ReadAdditionalUniqueIndexTreeProperties(std::istream& file) {}
|
||||
|
||||
void WriteProperties(std::ostream& file) override {
|
||||
WriteAdditionalUniqueIndexTreeProperties(file);
|
||||
mNodeMaterials->WriteToFile(file);
|
||||
|
||||
}
|
||||
void ReadProperties(std::istream& file) override {
|
||||
ReadAdditionalUniqueIndexTreeProperties(file);
|
||||
mNodeMaterials->ReadFromFile(file);
|
||||
}
|
||||
|
||||
void FinalizeReadTree() override
|
||||
{
|
||||
CalculateShiftBytesPerLevel();
|
||||
}
|
||||
|
||||
void WriteAdditionalPoolProperties(std::ostream& file) override { WriteBlockPointerPool(file); WriteBlockPool(file); WriteAdditionalProperties(file); }
|
||||
void ReadAdditionalPoolProperties(std::istream& file) override { ReadBlockPointerPool(file); ReadBlockPool(file); ReadAdditionalProperties(file); }
|
||||
|
||||
void PrintDebugInfo() const override
|
||||
{
|
||||
printf("Leaf voxel count: %llu\n", GetLeafVoxelCount());
|
||||
//printf("Total voxel count: %llu\n", GetOctreeNodeCount());
|
||||
printf("Max node index: %llu\n", GetHighestShiftSum(GetRoot()));
|
||||
auto nodesPerLevel = GetNodesPerLevel();
|
||||
for (unsigned8 level = 0; level <= GetMaxLevel(); level++)
|
||||
printf("Shifts in level %u with %llu nodes: %u bytes\n", level, (unsigned64)nodesPerLevel[level], GetAdditionalBytesPerPointer(level));
|
||||
|
||||
// Print the childshifts of the root for debug purposes
|
||||
unsigned32* rootChildren = GetRoot()->GetChildren();
|
||||
unsigned64* rootEdgeMaterials = GetRoot()->GetEdgeMaterials();
|
||||
for (ChildIndex c = 0; c < GetRoot()->GetChildCount(); c++)
|
||||
{
|
||||
printf("mChildShifts[%u] = %9llu; ->", c, rootEdgeMaterials[c]);
|
||||
const UniqueIndexNode* child = GetTypedNode(rootChildren[c]);
|
||||
for (unsigned j = 0; j < child->GetChildCount(); j++)
|
||||
printf(" %9llu", child->GetEdgeMaterials()[j]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
//printf("Node count = %u\nOctreeNodeCount = %u\n", GetNodeCount(), GetOctreeNodeCount());
|
||||
//printf("Leaf node count = %u\n", GetLeafVoxelCount());
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user