Initial commit: Final state of the master project

This commit is contained in:
2017-09-16 09:41:37 +02:00
commit 696180d43b
832 changed files with 169717 additions and 0 deletions

View 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);
}

View 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;
};

View 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); }
};

View File

@@ -0,0 +1,5 @@
#pragma once
#include "../Material/ColorChannel.h"
#include "LeafMaterialMultiRootTree.h"
typedef LeafMaterialMultiRootTree<ColorChannel> ColorChannelMultiRootTree;

View 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());
}
};

View 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;
}

View 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);
};

View 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
}
};

View 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;
}

View 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();
};

View 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;
};

View 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;
};

View 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;
};

View 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);
}
};

View 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); }
};

View 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); }
};

View 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

View 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());
}
};

View 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();
}
};

View 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> >;

View 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;
};

View 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;
}

View 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
};

View 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);
}
};

View 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:
};

View 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
};

View 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);
}
};

View 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());
}
};