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,87 @@
#pragma once
#include "BaseOctreeBuilder.h"
#include "../../inc/glm/common.hpp"
#include <unordered_set>
#include "../../core/Hashers.h"
#include "../../scene/Material/Color.h"
#include "../../scene/Material/SmallNormal.h"
#include "../../scene/Material/ColorAndNormal.h"
#include "../../scene/Material/ColorAndNormalAndValue.h"
#include "../../scene/Material/ColorAndOpacity.h"
#include "../Voxelizer/VoxelInfo.h"
template<typename T> struct MaterialAbbreviation { std::string operator()() const { return ""; } };
template<> struct MaterialAbbreviation<Color> { std::string operator()() const { return "c"; } };
template<> struct MaterialAbbreviation<SmallNormal> { std::string operator()() const { return "n"; } };
template<> struct MaterialAbbreviation<ColorAndNormal> { std::string operator()() const { return "cn"; } };
template<> struct MaterialAbbreviation<ColorAndOpacity> { std::string operator()() const { return "co"; } };
template<> struct MaterialAbbreviation<ColorAndNormalAndValue> { std::string operator()() const { return "cnr"; } };
template <typename T> struct MaterialName { std::string operator()() const { return "" } };
template<> struct MaterialName<Color> { std::string operator()() const { return "color"; } };
template<> struct MaterialName<SmallNormal> { std::string operator()() const { return "normal"; } };
template<> struct MaterialName<ColorAndNormal> { std::string operator()() const { return "color and normal"; } };
template<> struct MaterialName<ColorAndOpacity> { std::string operator()() const { return "color and opacity"; } };
template<> struct MaterialName<ColorAndNormalAndValue> { std::string operator()() const { return "color, normal and value"; } };
template<typename T> struct DefaultMaterials { std::vector<T> operator()() const { return std::vector<T>(); } };
template<> struct DefaultMaterials<SmallNormal> { std::vector<SmallNormal> operator()() const { return SmallNormal::GetAll(); } };
template<typename T>
class BaseMaterialOctreeBuilder : public BaseOctreeBuilder
{
public:
BaseMaterialOctreeBuilder() : BaseOctreeBuilder() {}
~BaseMaterialOctreeBuilder() override {}
protected:
T GetMaterial(const VoxelInfo& voxel) const { printf("Error: Material not implemented!"); }
bool RequiresColors() const override { return true; }
bool RequiresNormals() const override { return true; }
bool RequiresReflectivity() const override { return true; }
virtual void PreProcessNode(const glm::uvec3& coordinate, const T& material) {}
void PreProcessNode(const VoxelInfo& voxel) override { PreProcessNode(voxel.position, GetMaterial(voxel)); }
virtual void PreProcessMissingNode(const glm::uvec3& coordinate, const T& material) {}
void PreProcessMissingNode(const VoxelInfo& voxel) override { PreProcessMissingNode(voxel.position, GetMaterial(voxel)); }
// Should add a node to the current pass tree at the given coordinate and color
virtual void AddNode(const glm::uvec3& coordinate, const T& color) = 0;
void AddNode(const VoxelInfo& voxel) override { AddNode(voxel.position, GetMaterial(voxel)); }
virtual void AddMissingNode(const glm::uvec3& coordinate, const T& color) { printf("Warning: Missing nodes not caught!"); }
void AddMissingNode(const VoxelInfo& voxel) override { AddMissingNode(voxel.position, GetMaterial(voxel)); }
private:
};
template<> bool BaseMaterialOctreeBuilder<Color>::RequiresNormals() const { return false; }
template<> bool BaseMaterialOctreeBuilder<Color>::RequiresReflectivity() const { return false; }
template<> Color BaseMaterialOctreeBuilder<Color>::GetMaterial(const VoxelInfo& voxel) const { return Color(voxel.color); }
// Hack to render the normals (debugging)
//template<> BaseMaterialOctreeBuilder<Color>::BaseMaterialOctreeBuilder() { readColors = false; readNormals = true; }
//template<> Color BaseMaterialOctreeBuilder<Color>::GetMaterial(const VoxelInfo& voxel)
//{
// glm::vec3 scaledNormal = SmallNormal(voxel.normal).Get();
// scaledNormal = scaledNormal * 128.0f + 128.f;
// return Color(glm::u8vec3(scaledNormal));
//
//}
template<> bool BaseMaterialOctreeBuilder<SmallNormal>::RequiresColors() const { return false; }
template<> bool BaseMaterialOctreeBuilder<SmallNormal>::RequiresReflectivity() const { return false; }
template<> SmallNormal BaseMaterialOctreeBuilder<SmallNormal>::GetMaterial(const VoxelInfo& voxel) const { return SmallNormal(voxel.normal); }
template<> bool BaseMaterialOctreeBuilder<ColorAndNormal>::RequiresReflectivity() const { return false; }
template<> ColorAndNormal BaseMaterialOctreeBuilder<ColorAndNormal>::GetMaterial(const VoxelInfo& voxel) const {
return ColorAndNormal(voxel.color, voxel.normal);
}
template<> ColorAndNormalAndValue BaseMaterialOctreeBuilder<ColorAndNormalAndValue>::GetMaterial(const VoxelInfo& voxel) const
{
return ColorAndNormalAndValue(voxel.color, NormalAndValue(voxel.normal, (unsigned8)(voxel.reflectivity * 255.0f)));
}
template<> bool BaseMaterialOctreeBuilder<ColorAndOpacity>::RequiresNormals() const { return false; }
template<> ColorAndOpacity BaseMaterialOctreeBuilder<ColorAndOpacity>::GetMaterial(const VoxelInfo& voxel) const
{
return ColorAndOpacity(voxel.color, (unsigned32)(glm::clamp(voxel.reflectivity, 0.f, 1.f) * 65535.f));
}

View File

@@ -0,0 +1,105 @@
#include "BaseOctreeBuilder.h"
#include <stdio.h>
#include <algorithm>
#include "../Voxelizer/VoxelInfo.h"
#include "../Util/Stopwatch.h"
#include "OctreeBuilderStatistics.h"
#include "../Voxelizer/BaseVoxelizer.h"
OctreeBuilderStatistics BaseOctreeBuilder::BuildOctree(BaseVoxelizer& voxelizer, unsigned8 level, unsigned8 maxSingle)
{
// Initialize some variables used for building
unsigned8 singlePassDepth = (maxSingle < level) ? maxSingle : level;
unsigned8 singlePassLevel = level - singlePassDepth;
maxTreeDepth = level;
maxSinglePassLayers = maxSingle;
OctreeBuilderStatistics stats(level);
stats.type = GetTreeType();
Stopwatch watch;
Stopwatch mainWatch; mainWatch.Reset();
// Find out which subtrees to builds
watch.Reset();
mValidCoords = voxelizer.GetValidCoords(singlePassLevel);
std::sort(mValidCoords.begin(), mValidCoords.end(), [&](const glm::uvec3& coord1, const glm::uvec3& coord2) { return SubTreeCompare(coord1, coord2); });
stats.validCoordBuildtime = watch.GetTime();
// Do a pass through all voxels in the scene for preprocessing
watch.Reset();
if (UsePreprocessing())
{
Stopwatch smallWatch;
InitPreprocessing();
unsigned i = 0;
for (const glm::uvec3& coord : mValidCoords)
{
smallWatch.Reset();
if (verbose) printf("Preprocessing subtree %llu / %llu at coordinate (%u, %u, %u).\n", (unsigned64)(i + 1), (unsigned64)(mValidCoords.size()), coord.x, coord.y, coord.z);
InitCurPreprocessPass(coord);
voxelizer.Voxelize(level, singlePassDepth, coord, [&](const VoxelInfo& info, bool best)
{
if (best) PreProcessNode(info);
else PreProcessMissingNode(info);
}, RequiresColors(), RequiresNormals(), RequiresReflectivity());
FinalizeCurPreprocessPass(coord);
if (verbose) printf("Subtree processed in %d ms.\n", (int)(watch.GetTime() * 1000));
i++;
}
FinalizePreprocessing();
}
// Voxelize the scene and build the tree
InitTree();
Stopwatch smallWatch;
unsigned i = 0;
for (const glm::uvec3& coord : mValidCoords)
{
smallWatch.Reset();
if (verbose) printf("Processing subtree %llu / %llu at coordinate (%u, %u, %u).\n", (unsigned64)(i + 1), (unsigned64)(mValidCoords.size()), coord.x, coord.y, coord.z);
if (!IsSinglePass() && !mForceRebuild && CancelCurPassTree(coord))
{
if (verbose) printf("Subtree processing cancelled.\n");
}
else
{
InitCurPassTree(coord);
watch.Reset();
voxelizer.Voxelize(level, singlePassDepth, coord, [&](const VoxelInfo& info, bool best)
{
if (best) AddNode(info);
else AddMissingNode(info);
}, RequiresColors(), RequiresNormals(), RequiresReflectivity());
stats.depthPeelingTime += watch.GetTime(); watch.Reset();
FinalizeCurPassTree(coord);
stats.finalizationTime += watch.GetTime();
if (verbose) printf("Subtree processed in %d ms.\n", (int)(smallWatch.GetTime() * 1000));
}
i++;
}
watch.Reset();
FinalizeTree();
stats.finalizationTime += watch.GetTime();
// Calculate and show statistics
stats.totalTime = mainWatch.GetTime();
stats.octreeNodesPerLevel = GetOctreeNodesPerLevel();
stats.dagNodesPerLevel = GetNodesPerLevel();
if (verbose) stats.Print();
TerminateTree();
return stats;
}
bool BaseOctreeBuilder::SubTreeCompare(const glm::uvec3& coord1, const glm::uvec3& coord2) const
{
if (coord1.z != coord2.z)
return coord1.z < coord2.z;
if (coord1.y != coord2.y)
return coord1.y < coord2.y;
if (coord1.x != coord2.x)
return coord1.x < coord2.x;
return false;
}

View File

@@ -0,0 +1,91 @@
#pragma once
#ifndef GLM_FORCE_RADIANS
#define GLM_FORCE_RADIANS
#endif
#include <vector>
#include "../../inc/glm/glm.hpp"
#include "../../core/Defines.h"
struct VoxelInfo;
struct OctreeBuilderStatistics;
class BaseVoxelizer;
// BaseOctreeBuilder handles octree generation with any base voxelizer.
// Note that this class is abstract and will call several methods that any actual octree builder can implement.
class BaseOctreeBuilder
{
public:
BaseOctreeBuilder() :
mForceRebuild(false)
{}
virtual ~BaseOctreeBuilder() {}
// Given a voxelizer with a loaded scene, buil
OctreeBuilderStatistics BuildOctree(BaseVoxelizer& voxelizer, unsigned8 level, unsigned8 maxSingle);
virtual std::string GetTreeType() = 0;
void SetOutputFile(std::string outputFile) { mOutputFile = outputFile; }
std::string GetOutputFile() const { return mOutputFile; }\
void SetVerbose(bool verbose) { this->verbose = verbose; }
bool GetVerbose() const { return verbose; }
void SetForceRebuild(bool force) { mForceRebuild = force; }
bool GetForceRebuild() const { return mForceRebuild; }
protected:
virtual bool UsePreprocessing() const { return false; }
virtual bool RequiresColors() const = 0;
virtual bool RequiresNormals() const = 0;
virtual bool RequiresReflectivity() const = 0;
// Used to sort the subtrees for optimal building speed (some trees might be quicker to build in a certain order)
// Should return true if the first coord should go before the second
virtual bool SubTreeCompare(const glm::uvec3& coord1, const glm::uvec3& coord2) const;
// Functions to override for preprocessing
virtual bool CancelPreprocessing() { return false; }
virtual void InitPreprocessing() {}
virtual void InitCurPreprocessPass(glm::uvec3 coordinate) {}
virtual void PreProcessNode(const VoxelInfo& voxel) { printf("Warning: PreProcessNode() not implemented!"); }
virtual void PreProcessMissingNode(const VoxelInfo& voxel) {}
virtual void FinalizeCurPreprocessPass(glm::uvec3 coord) {}
virtual void FinalizePreprocessing() {}
// Functions to override for main tree building
virtual void InitTree() = 0;
virtual bool CancelCurPassTree(const glm::uvec3& coord) { return false; }
virtual void InitCurPassTree(glm::uvec3 coord) = 0;
virtual void AddNode(const VoxelInfo& voxel) = 0;
virtual void AddMissingNode(const VoxelInfo& voxel) = 0;
virtual void FinalizeCurPassTree(glm::uvec3 coord) = 0;
virtual void FinalizeTree() = 0;
virtual void TerminateTree() = 0;
virtual std::vector<size_t> GetOctreeNodesPerLevel() = 0;
virtual std::vector<size_t> GetNodesPerLevel() = 0;
// Some getters that are available during construction
// Returns "True" if this tree will be processed in a single pass
bool IsSinglePass() const { return maxSinglePassLayers >= maxTreeDepth; }
// Returns the depth of the main tree
unsigned8 GetTreeDepth() const { return maxTreeDepth; }
// Returns the maximum depth of a tree processing in a single pass
unsigned8 GetSinglePassTreeDepth() const { return maxTreeDepth < maxSinglePassLayers ? maxTreeDepth : maxSinglePassLayers; }
// Returns the level at which subtrees will be appended (e.g. TreeDepth - SinglePassTreeDepth)
unsigned8 GetAppendedTreeLevel() const { return IsSinglePass() ? 0 : (maxTreeDepth - maxSinglePassLayers); }
// Returns all coordinates that contain triangles in this scene.
const std::vector<glm::uvec3>& GetValidCoords() const { return mValidCoords; }
bool verbose;
private:
// Override cancelling of subtree building
bool mForceRebuild;
std::vector<glm::uvec3> mValidCoords;
std::string mOutputFile;
unsigned8 maxSinglePassLayers;
unsigned8 maxTreeDepth;
};

View File

@@ -0,0 +1,23 @@
#pragma once
#include "BaseOctreeBuilder.h"
#include "../../inc/glm/glm.hpp"
#include "../Voxelizer/VoxelInfo.h"
class BaseStandardOctreeBuilder : public BaseOctreeBuilder
{
public:
bool RequiresColors() const override { return false; }
bool RequiresNormals() const override { return false; }
bool RequiresReflectivity() const override { return false; }
protected:
virtual void PreProcessNode(const glm::uvec3& coordinate) {}
void PreProcessNode(const VoxelInfo& voxel) override { PreProcessNode(voxel.position); }
// Should add a node to the current pass tree at the given coordinate and color
virtual void AddNode(const glm::uvec3& coordinate) = 0;
void AddNode(const VoxelInfo& voxel) override { AddNode(voxel.position); }
virtual void AddMissingNode(const glm::uvec3& coordinate) { printf("Warning: Missing nodes not caught!"); }
void AddMissingNode(const VoxelInfo& voxel) override { AddMissingNode(voxel.position); }
private:
};

View File

@@ -0,0 +1,28 @@
#pragma once
#include <map>
#include "../../inc/glm/common.hpp"
#include "../Comparers.h"
#include "ColorQuantizerFactory.h"
#include "../../scene/Material/MaterialQuantizer/ColorAndValueQuantizer.h"
#include "../../scene/Material/SmallNormal.h"
#include "../../scene/Material/BitsMaterial.h"
template<typename T, typename TCompare>
class ColorAndValueQuantizerFactory
{
public:
static ColorAndValueQuantizer<T, TCompare>* Create(std::string quantizerType)
{
BaseColorQuantizer* colorQuantizer = ColorQuantizerFactory::Create(quantizerType);
if (colorQuantizer == NULL) return NULL;
return new ColorAndValueQuantizer<T, TCompare>(colorQuantizer);
}
static std::string GetNormalizedType(std::string quantizerType) { return ColorQuantizerFactory::GetNormalizedType(quantizerType); }
// Returns a string that can be used as a regex to match all color quantizer types and parameters
static std::string GetRegexMatcher() { return ColorQuantizerFactory::GetRegexMatcher(); }
};
typedef ColorAndValueQuantizerFactory<SmallNormal, NormalCompare> ColorAndNormalQuantizerFactory;
typedef ColorAndValueQuantizerFactory<BitsMaterial<16>, BitsMaterialComparer<16>> ColorAndOpacityQuantizerFactory;
typedef ColorAndValueQuantizerFactory<MaterialPair<SmallNormal, BitsMaterial<8>>, MaterialPairCompare<SmallNormal, NormalCompare, BitsMaterial<8>, BitsMaterialComparer<8>>> ColorAndNormalAndValueQuantizerFactory;

View File

@@ -0,0 +1,21 @@
#pragma once
#include "MaterialMultiRootOctreeBuilder.h"
#include "../../scene/Material/ColorChannel.h"
template<>
void MaterialMultiRootOctreeBuilder<ColorChannel>::AddNode(const glm::uvec3& coordinate, const Color& color)
{
mCurPassTree->AddLeafNode(coordinate, ColorChannel());
unsigned8 mask = 0xFF << (8 - mBitsPerChannel);
mCurPassTree->AddLeafNode(coordinate, 0, ColorChannel(color.GetR() & mask));
mCurPassTree->AddLeafNode(coordinate, 1, ColorChannel(color.GetG() & mask));
mCurPassTree->AddLeafNode(coordinate, 2, ColorChannel(color.GetB() & mask));
}
template<>
std::string MaterialMultiRootOctreeBuilder<ColorChannel>::GetTreeType()
{
return "mcc";
}

View File

@@ -0,0 +1,63 @@
#include "ColorQuantizerFactory.h"
#include "../ColorHelper.h"
#include <regex>
#include "../../scene/Material/MaterialQuantizer/ColorQuantizer/BaseColorQuantizer.h"
#include "../../scene/Material/MaterialQuantizer/ColorQuantizer/MaxErrorClusterer.h"
#include "../../scene/Material/MaterialQuantizer/ColorQuantizer/XiangClusterer.h"
#include "../../scene/Material/MaterialQuantizer/ColorQuantizer/XiangCIELABClusterer.h"
float ColorQuantizerFactory::GetMaxCIELABError(std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>* quantizationMap)
{
float maxError = 0;
for (auto value : *quantizationMap)
{
float error = ColorHelper::GetDeltaEFromRGB(value.first, value.second);
if (error > maxError)
maxError = error;
}
return maxError;
}
BaseColorQuantizer* ColorQuantizerFactory::Create(std::string quantizerType)
{
if (quantizerType == "") return NULL;
std::string maxErrorMatcherRegexStr = "de([0-9]*.[0-9]+|[0-9]+.?)";
std::string xiangLabMatcherRegexStr = "lab([0-9]+)";
std::string xiangMatcherRegexStr = "([0-9]+)";
std::regex maxErrorMatcher(maxErrorMatcherRegexStr);
std::regex xiangLabMatcher(xiangLabMatcherRegexStr);
std::regex xiangMatcher(xiangMatcherRegexStr);
std::smatch sm;
if (std::regex_match(quantizerType, sm, maxErrorMatcher))
{
float maxError = std::stof(sm.str(1));
return new MaxErrorClusterer(maxError);
}
else if (std::regex_match(quantizerType, sm, xiangLabMatcher))
{
unsigned colorCount = std::stoi(sm.str(1));
return new XiangCIELABClusterer(colorCount);
}
else if (std::regex_match(quantizerType, sm, xiangMatcher))
{
unsigned colorCount = std::stoi(sm.str(1));
return new XiangClusterer(colorCount);
}
return NULL;
}
std::string ColorQuantizerFactory::GetNormalizedType(std::string quantizerType)
{
auto quantizer = Create(quantizerType);
if (quantizer == NULL) return "";
std::string normalizedType = quantizer->GetQuantizerDescriptor();
delete quantizer;
return normalizedType;
}
std::string ColorQuantizerFactory::GetRegexMatcher()
{
return "[0-9]+|lab[0-9]+|de([0-9]*.[0-9]+|[0-9]+)";
}

View File

@@ -0,0 +1,17 @@
#pragma once
#include <map>
#include "../../inc/glm/common.hpp"
#include "../Comparers.h"
class BaseColorQuantizer;
class ColorQuantizerFactory
{
public:
static float GetMaxCIELABError(std::map<glm::u8vec3, glm::u8vec3, u8vec3comparer>* quantizationMap);
static BaseColorQuantizer* Create(std::string quantizerType);
static std::string GetNormalizedType(std::string quantizerType);
// Returns a string that can be used as a regex to match all color quantizer types and parameters
static std::string GetRegexMatcher();
};

View File

@@ -0,0 +1,79 @@
#pragma once
#include "../../scene/TextureCompressor/CompressedTexture.h"
#include "../../scene/TextureCompressor/BlockCompressedTexture.h"
#include "../../scene/TextureCompressor/TightlyPackedTexture.h"
#include "../../scene/TextureCompressor/PaletteBlockTexture.h"
#include "../../scene/TextureCompressor/DagBasedTexture.h"
#include "../../scene/TextureCompressor/MultiRootBasedTexture.h"
#include <regex>
enum CompressionType
{
BASIC, BLOCK, TIGHT, TIGHTBLOCK, PALETTEBLOCK, DAGBASED, MULTIROOTBASED
};
template<typename T>
class CompressedTextureFactory
{
public:
static CompressionType GetCompressionType(std::string type)
{
if (type == "b")
return BASIC;
if (type == "p")
return PALETTEBLOCK;
if (type == "d")
return DAGBASED;
if (type == "m")
return MULTIROOTBASED;
if (std::regex_match(type, std::regex("[0-9]+")))
return BLOCK;
if (std::regex_match(type, std::regex("t([0-9]+)")))
return TIGHTBLOCK;
return TIGHT;
}
static CompressedTexture<T>* GetCompressedTexture(std::string type)
{
CompressedTexture<T>* texture = NULL;
std::smatch sm;
if (type == "" || type == "t")
{
texture = new TightlyPackedTexture<T>();
}
else if (type == "b") // b for basic texture (no compression)
{
texture = new BasicTexture<T>();
}
else if (type == "p") // p for palette
{
texture = new PaletteBlockTexture<T>();
}
else if (type == "d")
{
texture = new DagBasedTexture<T>();
}
else if (type == "m")
{
return new MultiRootBasedTexture<T>();
}
else if (std::regex_match(type, sm, std::regex("[0-8]+")))
{
unsigned blockSize = (unsigned)std::stoi(sm.str(0));
texture = new BlockCompressedTexture<T>(blockSize);
}
else if (std::regex_match(type, sm, std::regex("t([0-8]+)")))
{
unsigned blockSize = (unsigned)std::stoi(sm.str(1));
texture = new BlockCompressedTexture<T, TightlyPackedTexture<T>>(blockSize);
}
return texture;
}
// Returns a string that can be used as a regex to match all compressed texture types
static std::string GetRegexMatcher()
{
return std::string("p|b|d|m|[0-9]+|t[0-9]*");
}
};

View File

@@ -0,0 +1,245 @@
#pragma once
#include "BaseMaterialOctreeBuilder.h"
#include "../../inc/glm/common.hpp"
#include <unordered_set>
#include "../../core/Hashers.h"
#include "../../scene/Octree/MaterialLibraryTree.h"
#include "../../scene/Material/MaterialQuantizer/BaseQuantizer.h"
template<typename T, typename Comparer>
class HierarchicalMaterialOctreeBuilder : public BaseMaterialOctreeBuilder<T>
{
public:
HierarchicalMaterialOctreeBuilder(BaseQuantizer<T, Comparer>* quantizer = NULL) :
mTree(NULL),
mMaterialLibrary(NULL),
mReduceMaterials(quantizer != NULL),
mSceneMaterials(std::vector<T>()),
mReplacers(NULL),
mQuantizer(quantizer)
{}
~HierarchicalMaterialOctreeBuilder() override
{
if (mReplacers != NULL)
delete mReplacers;
if (mTree != NULL)
delete mTree;
if (mMaterialLibrary != NULL)
delete mMaterialLibrary;
}
std::string GetTreeType() {
return "h" + MaterialAbbreviation<T>()() +
(mReduceMaterials ? mQuantizer->GetQuantizerDescriptor() : "");
}
protected:
bool MaterialHasDefaults() const { return !DefaultMaterials<T>()().empty(); }
MaterialLibraryTree<T, Comparer, T::CHANNELSPERPIXEL>* CreateTree(unsigned8 depth) const
{
return new MaterialLibraryTree<T, Comparer, T::CHANNELSPERPIXEL>(depth);
}
MaterialLibraryTree<T, Comparer, T::CHANNELSPERPIXEL>* CreateTree(unsigned8 depth, MaterialLibrary<T, Comparer, T::CHANNELSPERPIXEL>* lib) const
{
return new MaterialLibraryTree<T, Comparer, T::CHANNELSPERPIXEL>(depth, lib);
}
std::string GetSinglePassTreeFilename(glm::uvec3 coord)
{
char buffer[255];
sprintf(buffer, "%s_%u_(%u_%u_%u)", GetOutputFile().c_str(), GetTreeDepth(), coord.x, coord.y, coord.z);
return std::string(buffer);
}
bool UsePreprocessing() const override { return !MaterialHasDefaults(); }
void PreProcessNode(const glm::uvec3& coordinate, const T& mat) override { mSceneMaterials.push_back(mat); }
void PreProcessMissingNode(const glm::uvec3& coordinate, const T& mat) override { mSceneMaterials.push_back(mat); }
void FinalizeCurPreprocessPass(glm::uvec3 coord) override { CalculateUniqueSceneMaterials(); }
void InitTree() override
{
// Get scene materials if this is a material type that doesn't need preprocessing
if (mSceneMaterials.empty())
mSceneMaterials = DefaultMaterials<T>()();
if (mReduceMaterials)
{
// Quantize the scene materials
CalculateQuantizedMaterials();
// Store the quantized values in the mSceneMaterials
mSceneMaterials = std::vector<T>();
for (auto it = mReplacers->begin(); it != mReplacers->end(); it++)
mSceneMaterials.push_back(it->second);
CalculateUniqueSceneMaterials();
}
// Initialize the material library
Stopwatch watch; watch.Reset();
if (verbose) printf("Initializing the material library...");
mMaterialLibrary = new MaterialLibrary<T, Comparer, T::CHANNELSPERPIXEL>();
for (auto mat : mSceneMaterials)
mMaterialLibrary->AddMaterial(mat);
mMaterialLibrary->Finalize();
if (verbose) printf("Material library initialized in %u ms.\n", (int)(watch.GetTime() * 1000));
}
void FinalizeTree() override
{
Stopwatch watch;
if (!IsSinglePass())
{
// If this tree was constructred in steps, we should have cache files by now to build the main tree from
unsigned8 mainTreeDepth = GetTreeDepth();
unsigned8 subTreeDepth = GetSinglePassTreeDepth();
unsigned8 mainTreeLevel = mainTreeDepth - subTreeDepth;
mTree = CreateTree(GetTreeDepth(), mMaterialLibrary);
unsigned32 i = 1;
for (const glm::uvec3& coord : GetValidCoords())
{
if (verbose) printf("Reading subtree at %u / %u (%u, %u, %u) from cache...\n", i, (unsigned32)GetValidCoords().size(), coord.x, coord.y, coord.z);
MaterialLibraryTree<T, Comparer, T::CHANNELSPERPIXEL>* subTree = (MaterialLibraryTree<T, Comparer, T::CHANNELSPERPIXEL>*)OctreeLoader::ReadCache(GetTreeType(), GetSinglePassTreeDepth(), GetSinglePassTreeFilename(coord), verbose);
if (verbose) printf("Appending subtree...");
watch.Reset();
mTree->AppendAndMerge(coord, mainTreeLevel, subTree);
delete subTree;
if (verbose) printf("Subtree appended in %d ms.\n", (int)(watch.GetTime() * 1000));
i++;
}
mTree->ClearOrphans();
}
// Propagate the materials
watch.Reset();
if (verbose) printf("Propagating materials in the tree... ");
mTree->PropagateMaterials();
if (verbose) printf("Materials propagated in %d ms.\n", (int)(watch.GetTime() * 1000));
// Generate the material texture
watch.Reset();
if (verbose) printf("Generating material texture... ");
mTree->GetMaterialTexture();
if (verbose) printf("Material texture generated in %d ms\n", (int)(watch.GetTime() * 1000));
// Delete the cache files
if (!IsSinglePass())
{
for (const glm::uvec3& coord : GetValidCoords())
OctreeLoader::DeleteCache(GetTreeType(), GetSinglePassTreeDepth(), GetSinglePassTreeFilename(coord));
}
}
// Step to finalize the main tree (for example storing it to a file)
void TerminateTree() override
{
OctreeLoader::WriteCache(mTree, GetTreeType(), GetOutputFile(), verbose);
delete mTree;
mTree = NULL;
mSceneMaterials.clear();
if (mReplacers != NULL) delete mReplacers;
mReplacers = NULL;
}
bool CancelCurPassTree(const glm::uvec3& coord) override
{
return OctreeLoader::VerifyCache(GetTreeType(), GetSinglePassTreeDepth(), GetSinglePassTreeFilename(coord));
}
// Initialize the tree for the current pass.
void InitCurPassTree(glm::uvec3 coord) override
{
mTree = CreateTree(GetSinglePassTreeDepth(), mMaterialLibrary);
}
// Terminate the tree in the current pass. This means it should also be appended to the main tree and deleted
void FinalizeCurPassTree(glm::uvec3 coord) override
{
Stopwatch watch;
// Remove unused leafs and stuff
mTree->ClearOrphans();
// Convert the subtree to a DAG
if (verbose) printf("Converting subtree to DAG...\n");
watch.Reset();
mTree->ToDAG();
if (verbose) printf("Converting took %u ms.\n", (unsigned)(watch.GetTime() * 1000.0));
if (!IsSinglePass())
{
// Write the subtree to a cache file
OctreeLoader::WriteCache(mTree, GetTreeType(), GetSinglePassTreeFilename(coord), verbose);
delete mTree;
mTree = NULL;
}
}
// Should add a node to the current pass tree at the given coordinate and material
void AddNode(const glm::uvec3& coordinate, const T& mat) override
{
auto replacer = mat;
if (mReduceMaterials)
{
auto replacerPair = mReplacers->find(mat);
if (replacerPair != mReplacers->end())
replacer = replacerPair->second;
else
{
replacer = ParallelNearestFinder<T>()(mat, mSceneMaterials);
mReplacers->insert(std::make_pair(mat, replacer));
}
}
mTree->AddLeafNode(coordinate, replacer);
}
void AddMissingNode(const glm::uvec3& coordinate, const T& mat) override
{
if (!mTree->HasLeaf(coordinate))
AddNode(coordinate, mat);
}
std::vector<size_t> GetOctreeNodesPerLevel() override { return mTree->GetOctreeNodesPerLevel(); }
std::vector<size_t> GetNodesPerLevel() override { return mTree->GetNodesPerLevel(); }
void CalculateUniqueSceneMaterials() { CollectionHelper::Unique<T, Comparer>(mSceneMaterials); }
void CalculateQuantizedMaterials()
{
if (verbose) printf("Quantizing/merging %llu materials...", (unsigned64)(mSceneMaterials.size()));
Stopwatch watch; watch.Reset();
DebugQuantize();
mReplacers = mQuantizer->QuantizeMaterials(mSceneMaterials);
if (verbose) printf("Quantized materials in %d ms.\n", (int)(watch.GetTime() * 1000));
watch.Reset();
}
void DebugQuantize() {}
MaterialLibraryTree<T, Comparer, T::CHANNELSPERPIXEL>* mTree;
bool mReduceMaterials;
std::vector<T> mSceneMaterials;
MaterialLibrary<T, Comparer, T::CHANNELSPERPIXEL>* mMaterialLibrary;
std::map<T, T, Comparer>* mReplacers;
BaseQuantizer<T, Comparer>* mQuantizer;
};
//template<> void HierarchicalMaterialOctreeBuilder<Color, ColorCompare>::DebugQuantize()
//{
// std::vector<std::string> types = { "lab256", "lab1024", "lab4096", "lab16384" };
// for (std::string type : types)
// {
// printf("%s: ", type.c_str());
// BaseColorQuantizer* quantizer = ColorQuantizerFactory::Create(type);
// auto temp = quantizer->QuantizeMaterials(mSceneMaterials);
// delete temp;
// }
//}
template<> bool HierarchicalMaterialOctreeBuilder<SmallNormal, NormalCompare>::MaterialHasDefaults() const { return false; }
template<> void HierarchicalMaterialOctreeBuilder<ColorAndNormal, ColorAndNormalCompare>::PreProcessMissingNode(const glm::uvec3& coordinate, const ColorAndNormal& mat)
{
mSceneMaterials.push_back(mat);
}

View File

@@ -0,0 +1,233 @@
#include "MaterialLibraryMultiRootOctreeBuilder.h"
#include "../Util/Stopwatch.h"
#include "OctreeLoader.h"
#include "../CollectionHelper.h"
#include "../../inc/tbb/parallel_for.h"
#include "../../inc/tbb/concurrent_queue.h"
MaterialLibraryMultiRootOctreeBuilder::MaterialLibraryMultiRootOctreeBuilder(BaseQuantizer<Color, ColorCompare>* quantizer) :
BaseMaterialOctreeBuilder(),
mTree(NULL),
mIntermediateTree(NULL),
mQuantizeColors(quantizer != NULL),
mQuantizer(quantizer),
mSceneColors(std::vector<Color>()),
mColorReplacers(std::unordered_map<Color, Color>())
{
// TODO: when building in steps, use preprocessing to find the materials in the upper levels
}
MaterialLibraryMultiRootOctreeBuilder::~MaterialLibraryMultiRootOctreeBuilder() {}
bool MaterialLibraryMultiRootOctreeBuilder::IsLimitedColors() const { return mQuantizeColors || !IsSinglePass(); }
bool MaterialLibraryMultiRootOctreeBuilder::UsePreprocessing() const { return IsLimitedColors(); }
std::string MaterialLibraryMultiRootOctreeBuilder::GetTreeType() { return "blc" + (mQuantizer == NULL ? "" : mQuantizer->GetQuantizerDescriptor()); }
void MaterialLibraryMultiRootOctreeBuilder::InitTree()
{
mTree = new MaterialLibraryMultiRoot<Color, ColorCompare>(GetTreeDepth());
}
void MaterialLibraryMultiRootOctreeBuilder::FinalizeTree()
{
// Store the amount of nodes the octree had for the first few levels
auto nodesPerLevel = mTree->GetOctreeNodesPerLevel();
}
void MaterialLibraryMultiRootOctreeBuilder::TerminateTree()
{
OctreeLoader::WriteCache(mTree, GetTreeType(), GetOutputFile(), verbose);
delete mTree;
mSceneColors.clear();
mColorReplacers.clear();
}
void MaterialLibraryMultiRootOctreeBuilder::InitCurPassTree(glm::uvec3 coord)
{
mIntermediateTree = new MaterialTree<Color, ColorCompare>(GetSinglePassTreeDepth());
mIntermediateTree->UseLeafMap(true);
//for (auto color : mSceneColors)
// mIntermediateTree->AddMaterial(Color(color));
//mIntermediateTree->FinalizeMaterials();
}
void MaterialLibraryMultiRootOctreeBuilder::FinalizeCurPassTree(glm::uvec3 coord)
{
Stopwatch watch;
if (mIntermediateTree->GetNodeCount() > 1) // Only append the tree (and compress) if it is not empty
{
// Convert the intermediate tree to a DAG before building the material library multiroot tree:
if (verbose) printf("Converting intermediate tree to a DAG...\n");
watch.Reset();
mIntermediateTree->ToDAG();
if (verbose) printf("Converting took %d ms.\n", (int)(watch.GetTime() * 1000));
// Propagate the materials in the intermediate tree
if (verbose) printf("Propagating materials in the intermediate tree...");
watch.Reset();
mIntermediateTree->PropagateMaterials(Color::WeightedAverage);
if (verbose) printf("Propagated in %d ms.\n", (int)(watch.GetTime() * 1000));
// Quantize the colors in the material tree
if (IsLimitedColors())
{
if (verbose) printf("Replacing materials by their quantized counterparts.");
std::vector<Color> curPassMaterials = mIntermediateTree->GetUniqueMaterials();
CollectionHelper::Unique(curPassMaterials, ColorCompare());
std::vector<Color> curPassQuantizedMaterials(curPassMaterials.size());
if (verbose) printf(".");
// For each material in the curPassMaterials, find the closest material
tbb::concurrent_queue<size_t> newReplacers;
tbb::parallel_for(size_t(0), curPassMaterials.size(), [&](const size_t& i)
{
auto cachedReplacer = mColorReplacers.find(curPassMaterials[i]);
if (cachedReplacer != mColorReplacers.end())
curPassQuantizedMaterials[i] = cachedReplacer->second;
else
{
curPassQuantizedMaterials[i] = NearestFinder<Color>()(curPassMaterials[i], mSceneColors);
newReplacers.push(i);
}
});
// Add the newly found replacers to the cache
for (auto i = newReplacers.unsafe_begin(); i != newReplacers.unsafe_end(); ++i)
mColorReplacers.insert(std::pair<Color, Color>(curPassMaterials[*i], curPassQuantizedMaterials[*i]));
// Update the current scene color map
std::unordered_map<Color, Color> quantizedColors;
for (size_t i = 0; i < curPassMaterials.size(); i++)
quantizedColors.insert(std::pair<Color, Color>(curPassMaterials[i], curPassQuantizedMaterials[i]));
if (verbose) printf(".");
mIntermediateTree->ReplaceMaterials(quantizedColors);
if (verbose) printf("Replaced in %d ms.\n", (int)(watch.GetTime() * 1000));
if (verbose) printf("%llu new materials quantized, cache now contains %llu materials.\n", (unsigned64)newReplacers.unsafe_size(), (unsigned64)mColorReplacers.size());
}
// Build the actual MaterialLibaryMultiRootTree based on the intermediate tree
if (verbose) printf("Building multiroot tree based on intermediate tree...");
watch.Reset();
auto curPassTree = new MaterialLibraryMultiRoot<Color, ColorCompare>(mIntermediateTree->GetMaxLevel());
if (IsLimitedColors()) curPassTree->CreateMaterialLibrary(mSceneColors);
curPassTree->BaseOn(mIntermediateTree, false);
delete mIntermediateTree;
curPassTree->ShaveEquals();
curPassTree->ToDAG();
curPassTree->FillEmptySpace(false);
if (verbose) printf("Multiroot tree build in %d ms.\n", (int)(watch.GetTime() * 1000));
// Convert the current pass tree to a DAG
if (verbose) printf("Converting the current pass multiroot tree to a DAG...\n");
watch.Reset();
curPassTree->ToDAG();
if (verbose) printf("Converted in %d ms.\n", (int)(watch.GetTime() * 1000));
auto octreeNodesPerLevel = curPassTree->GetOctreeNodesPerLevel();
if (IsSinglePass()) // Means we just constructed the root, so no need to append
{
delete mTree;
mTree = curPassTree;
}
else
{
if (verbose) printf("Appending subtree... ");
watch.Reset();
mTree->Append(coord, GetAppendedTreeLevel(), curPassTree);
delete curPassTree;
if (verbose) printf("Appending took %d ms.\n", (int)(watch.GetTime() * 1000));
if (verbose) printf("Converting current tree to DAG...\n");
watch.Reset();
mTree->ToDAG(GetAppendedTreeLevel());
if (verbose) printf("Converted in %d ms.\n", (int)(watch.GetTime() * 1000));
}
}
else
{
delete mIntermediateTree;
}
}
void MaterialLibraryMultiRootOctreeBuilder::AddMissingNode(const glm::uvec3& coordinate, const Color& color)
{
if (!mIntermediateTree->HasLeaf(coordinate))
AddNode(coordinate, color);
}
void MaterialLibraryMultiRootOctreeBuilder::AddNode(const glm::uvec3& coordinate, const Color& color)
{
if (IsLimitedColors())
{
auto replacer = mColorReplacers.find(color);
if (replacer == mColorReplacers.end())
{
Color replacement = NearestFinder<Color>()(color, mSceneColors);
mColorReplacers.insert(std::pair<Color, Color>(color, replacement));
replacer = mColorReplacers.find(color);
}
assert(replacer != mColorReplacers.end());
mIntermediateTree->AddLeafNode(coordinate, replacer->second);
}
else
{
mIntermediateTree->AddLeafNode(coordinate, Color(color));
}
}
//-----------------------------------------------------------------------------------
// ---------------------------------PREPROCESSING------------------------------------
//-----------------------------------------------------------------------------------
void MaterialLibraryMultiRootOctreeBuilder::PreProcessNode(const glm::uvec3& coordinate, const Color& color)
{
mSceneColors.push_back(color);
}
void MaterialLibraryMultiRootOctreeBuilder::FinalizePreprocessing()
{
CalculateUniqueSceneColors();
if (mQuantizer != NULL)
{
if (verbose) printf("Quantizing %llu materials...", (unsigned64)mSceneColors.size());
Stopwatch watch; watch.Reset();
auto quantizedMaterials = mQuantizer->QuantizeMaterials(mSceneColors);
mSceneColors.clear();
for (auto quantizedMaterial : *quantizedMaterials)
{
mColorReplacers.insert(std::pair<Color, Color>(quantizedMaterial.first, quantizedMaterial.second));
mSceneColors.push_back(quantizedMaterial.second);
}
CalculateUniqueSceneColors();
if (verbose) printf("Quantized in %u ms, %llu materials left\n", (unsigned)(watch.GetTime() * 1000), (unsigned64)mSceneColors.size());
}
else
{
for (Color color : mSceneColors)
mColorReplacers.insert(std::pair<Color, Color>(color, color));
}
}
void MaterialLibraryMultiRootOctreeBuilder::CalculateUniqueSceneColors()
{
// Remove duplicates from scene colors
CollectionHelper::Unique(mSceneColors, ColorCompare());
}
//-----------------------------------------------------------------------------------
// -----------------------------------STATISTICS-------------------------------------
//-----------------------------------------------------------------------------------
std::vector<size_t> MaterialLibraryMultiRootOctreeBuilder::GetOctreeNodesPerLevel()
{
return mTree->GetOctreeNodesPerLevel();
}
std::vector<size_t> MaterialLibraryMultiRootOctreeBuilder::GetNodesPerLevel()
{
return mTree->GetNodesPerLevel();
}

View File

@@ -0,0 +1,50 @@
#pragma once
#include "BaseMaterialOctreeBuilder.h"
#include "../../scene/Material/Color.h"
#include "../../scene/Octree/MaterialLibraryMultiRootTree.h"
#include "../../scene/Octree/MaterialTree.h"
#include "../Comparers.h"
#include "../../scene/Material/MaterialQuantizer/BaseQuantizer.h"
class MaterialLibraryMultiRootOctreeBuilder : public BaseMaterialOctreeBuilder<Color>
{
public:
MaterialLibraryMultiRootOctreeBuilder(BaseQuantizer<Color, ColorCompare>* quantizer = NULL);
~MaterialLibraryMultiRootOctreeBuilder() override;
std::string GetTreeType() override;
protected:
// Initialize the main tree
void InitTree() override;
void FinalizeTree() override;
// Step to finalize the main tree (for example storing it to a file)
void TerminateTree() override;
bool IsLimitedColors() const;
bool UsePreprocessing() const override;
// Initialize the tree for the current pass.
void InitCurPassTree(glm::uvec3 coord) override;
// Terminate the tree in the current pass. This means it should also be appended to the main tree and deleted
void FinalizeCurPassTree(glm::uvec3 coord) override;
// Should add a node to the current pass tree at the given coordinate and color
void AddNode(const glm::uvec3& coordinate, const Color& color) override;
void AddMissingNode(const glm::uvec3& coordinate, const Color& color) override;
void PreProcessNode(const glm::uvec3& coordinate, const Color& color) override;
void FinalizePreprocessing() override;
void CalculateUniqueSceneColors();
std::vector<size_t> GetOctreeNodesPerLevel() override;
std::vector<size_t> GetNodesPerLevel() override;
private:
MaterialLibraryMultiRoot<Color, ColorCompare>* mTree;
MaterialTree<Color, ColorCompare>* mIntermediateTree;
bool mQuantizeColors;
BaseQuantizer<Color, ColorCompare>* mQuantizer;
std::vector<Color> mSceneColors;
std::unordered_map<Color, Color> mColorReplacers;
};

View File

@@ -0,0 +1,113 @@
#pragma once
#include "BaseMaterialOctreeBuilder.h"
#include "../../scene/Octree/LeafMaterialMultiRootTree.h"
#include "../Util/Stopwatch.h"
#include "OctreeLoader.h"
template <typename T>
class MaterialMultiRootOctreeBuilder : public BaseMaterialOctreeBuilder<Color>
{
private:
LeafMaterialMultiRootTree<T>* mTree;
LeafMaterialMultiRootTree<T>* mCurPassTree;
unsigned8 mBitsPerChannel;
std::string mTreeType;
public:
MaterialMultiRootOctreeBuilder(std::string treeType, unsigned8 bitsPerChannel) :
BaseMaterialOctreeBuilder(),
mTree(NULL),
mCurPassTree(NULL),
mBitsPerChannel(bitsPerChannel),
mTreeType(treeType)
{}
MaterialMultiRootOctreeBuilder(std::string treeType) : MaterialMultiRootOctreeBuilder(treeType, 8) {}
~MaterialMultiRootOctreeBuilder() override {}
std::string GetTreeType() override { bool explicitSpecializationGiven = false; assert(explicitSpecializationGiven); return ""; }
protected:
// Initialize the main tree
void InitTree() override
{
mTree = (LeafMaterialMultiRootTree<T>*)OctreeLoader::CreateTree(mTreeType, GetTreeDepth());
}
void FinalizeTree() override
{
// Convert the final tree to a DAG
if (verbose) printf("Converting final tree to DAG...\n");
Stopwatch watch; watch.Reset();
mTree->ToDAG();
if (verbose) printf("Done in %u ms, %llu nodes left.\n", (unsigned)(watch.GetTime() * 1000), (unsigned64)mTree->GetNodeCount());
}
// Step to finalize the main tree (for example storing it to a file)
void TerminateTree() override
{
OctreeLoader::WriteCache(mTree, GetTreeType(), GetOutputFile(), verbose);
delete mTree;
}
// Initialize the tree for the current pass.
void InitCurPassTree(glm::uvec3 coord) override
{
mCurPassTree = (LeafMaterialMultiRootTree<T>*)OctreeLoader::CreateTree(mTreeType, GetSinglePassTreeDepth());
}
// Terminate the tree in the current pass. This means it should also be appended to the main tree and deleted
void FinalizeCurPassTree(glm::uvec3 coord) override
{
Stopwatch watch;
if (mCurPassTree->GetNodeCount() > 1) // Only append the tree (and compress) if it is not empty
{
if (IsSinglePass()) // Means we just constructed the root, so no need to append
{
delete mTree;
mTree = mCurPassTree;
}
else
{
// Convert the subtree to a DAG first, this saved time when appending and converting the total tree
// Benchmark (Total build time for subtrees of depth 10, final tree of depth 12, Release mode with pool):
// 209.922 seconds without early converting
// 163.645 seconds with early converting
if (verbose) printf("Converting subtree to DAG...\n");
watch.Reset();
mCurPassTree->ToDAG();
if (verbose) printf("Converting took %d ms.\n", (int)(watch.GetTime() * 1000));
if (verbose) printf("Appending subtree... ");
mTree->Append(coord, GetAppendedTreeLevel(), mCurPassTree);
delete mCurPassTree;
if (verbose) printf("Converting current tree to DAG...\n");
mTree->ToDAG(GetAppendedTreeLevel());
}
}
else
{
delete mCurPassTree;
}
}
void AddMissingNode(const glm::uvec3& coordinate, const Color& color) override
{
if (!mCurPassTree->HasLeaf(coordinate))
AddNode(coordinate, color);
}
// Should add a node to the current pass tree at the given coordinate and color
void AddNode(const glm::uvec3& coordinate, const Color& color) override
{
((Root*)mCurPassTree)->AddLeafNode(coordinate);
}
std::vector<size_t> GetOctreeNodesPerLevel() override { return mTree->GetOctreeNodesPerLevel(); }
std::vector<size_t> GetNodesPerLevel() override { return mTree->GetNodesPerLevel(); }
};

View File

@@ -0,0 +1,151 @@
#pragma once
#include "MaterialMultiRootOctreeBuilder.h"
#include "../../scene/Octree/MultiRootBitsTree.h"
#include "../BitHelper.h"
template<>
void MaterialMultiRootOctreeBuilder<BitsMaterial<1>>::AddNode(const glm::uvec3& coordinate, const Color& color)
{
mCurPassTree->AddLeafNode(coordinate, BitsMaterial<1>(1));
for (unsigned8 c = 0; c < 3; c++)
for (unsigned8 i = 0; i < mBitsPerChannel; i++)
{
auto material = BitsMaterial<1>(color[c], i);
if (material.GetValue() != 0)
mCurPassTree->AddLeafNode(coordinate, mBitsPerChannel * c + i, material);
}
}
template<>
void MaterialMultiRootOctreeBuilder<BitsMaterial<2>>::AddNode(const glm::uvec3& coordinate, const Color& color)
{
unsigned N = 2;
mCurPassTree->AddLeafNode(coordinate, BitsMaterial<2>(1));
unsigned treesPerChannel = mBitsPerChannel / N + ((mBitsPerChannel % N) != 0 ? 1 : 0);
unsigned8 mask = BitHelper::GetLSMaskUntil<unsigned8>(8 - mBitsPerChannel);
for (unsigned8 c = 0; c < 3; c++)
{
unsigned8 maskedColor = color[c] & mask;
for (unsigned8 i = 0; i < treesPerChannel; i++)
{
auto material = BitsMaterial<2>(maskedColor, N * i);
if (material.GetValue() != 0)
mCurPassTree->AddLeafNode(coordinate, treesPerChannel * c + i, material);
}
}
}
template<>
void MaterialMultiRootOctreeBuilder<BitsMaterial<3>>::AddNode(const glm::uvec3& coordinate, const Color& color)
{
unsigned N = 3;
mCurPassTree->AddLeafNode(coordinate, BitsMaterial<3>(1));
unsigned treesPerChannel = mBitsPerChannel / N + ((mBitsPerChannel % N) != 0 ? 1 : 0);
unsigned8 mask = BitHelper::GetLSMaskUntil<unsigned8>(8 - mBitsPerChannel);
for (unsigned8 c = 0; c < 3; c++)
{
unsigned8 maskedColor = color[c] & mask;
for (unsigned8 i = 0; i < treesPerChannel; i++)
{
auto material = BitsMaterial<3>(maskedColor, N * i);
if (material.GetValue() != 0)
mCurPassTree->AddLeafNode(coordinate, treesPerChannel * c + i, material);
}
}
}
template<>
void MaterialMultiRootOctreeBuilder<BitsMaterial<4>>::AddNode(const glm::uvec3& coordinate, const Color& color)
{
unsigned N = 4;
mCurPassTree->AddLeafNode(coordinate, BitsMaterial<4>(1));
unsigned treesPerChannel = mBitsPerChannel / N + ((mBitsPerChannel % N) != 0 ? 1 : 0);
unsigned8 mask = BitHelper::GetLSMaskUntil<unsigned8>(8 - mBitsPerChannel);
for (unsigned8 c = 0; c < 3; c++)
{
unsigned8 maskedColor = color[c] & mask;
for (unsigned8 i = 0; i < treesPerChannel; i++)
{
auto material = BitsMaterial<4>(maskedColor, N * i);
if (material.GetValue() != 0)
mCurPassTree->AddLeafNode(coordinate, treesPerChannel * c + i, material);
}
}
}
template<>
void MaterialMultiRootOctreeBuilder<BitsMaterial<5>>::AddNode(const glm::uvec3& coordinate, const Color& color)
{
unsigned N = 5;
mCurPassTree->AddLeafNode(coordinate, BitsMaterial<5>(1));
unsigned treesPerChannel = mBitsPerChannel / N + ((mBitsPerChannel % N) != 0 ? 1 : 0);
unsigned8 mask = BitHelper::GetLSMaskUntil<unsigned8>(8 - mBitsPerChannel);
for (unsigned8 c = 0; c < 3; c++)
{
unsigned8 maskedColor = color[c] & mask;
for (unsigned8 i = 0; i < treesPerChannel; i++)
{
auto material = BitsMaterial<5>(maskedColor, N * i);
if (material.GetValue() != 0)
mCurPassTree->AddLeafNode(coordinate, treesPerChannel * c + i, material);
}
}
}
template<>
void MaterialMultiRootOctreeBuilder<BitsMaterial<6>>::AddNode(const glm::uvec3& coordinate, const Color& color)
{
unsigned N = 6;
mCurPassTree->AddLeafNode(coordinate, BitsMaterial<6>(1));
unsigned treesPerChannel = mBitsPerChannel / N + ((mBitsPerChannel % N) != 0 ? 1 : 0);
unsigned8 mask = BitHelper::GetLSMaskUntil<unsigned8>(8 - mBitsPerChannel);
for (unsigned8 c = 0; c < 3; c++)
{
unsigned8 maskedColor = color[c] & mask;
for (unsigned8 i = 0; i < treesPerChannel; i++)
{
auto material = BitsMaterial<6>(maskedColor, N * i);
if (material.GetValue() != 0)
mCurPassTree->AddLeafNode(coordinate, treesPerChannel * c + i, material);
}
}
}
template<>
void MaterialMultiRootOctreeBuilder<BitsMaterial<7>>::AddNode(const glm::uvec3& coordinate, const Color& color)
{
unsigned N = 7;
mCurPassTree->AddLeafNode(coordinate, BitsMaterial<7>(1));
unsigned treesPerChannel = mBitsPerChannel / N + ((mBitsPerChannel % N) != 0 ? 1 : 0);
unsigned8 mask = BitHelper::GetLSMaskUntil<unsigned8>(8 - mBitsPerChannel);
for (unsigned8 c = 0; c < 3; c++)
{
unsigned8 maskedColor = color[c] & mask;
for (unsigned8 i = 0; i < treesPerChannel; i++)
{
auto material = BitsMaterial<7>(maskedColor, N * i);
if (material.GetValue() != 0)
mCurPassTree->AddLeafNode(coordinate, treesPerChannel * c + i, material);
}
}
}
template<>
void MaterialMultiRootOctreeBuilder<BitsMaterial<8>>::AddNode(const glm::uvec3& coordinate, const Color& color)
{
mCurPassTree->AddLeafNode(coordinate, BitsMaterial<8>());
unsigned8 mask = BitHelper::GetLSMaskUntil<unsigned8>(8 - mBitsPerChannel);
mCurPassTree->AddLeafNode(coordinate, 0, BitsMaterial<8>(color.GetR() & mask));
mCurPassTree->AddLeafNode(coordinate, 1, BitsMaterial<8>(color.GetG() & mask));
mCurPassTree->AddLeafNode(coordinate, 2, BitsMaterial<8>(color.GetB() & mask));
}
template<> std::string MaterialMultiRootOctreeBuilder<BitsMaterial<1>>::GetTreeType() { return "m" + std::to_string(mBitsPerChannel) + "b1"; }
template<> std::string MaterialMultiRootOctreeBuilder<BitsMaterial<2>>::GetTreeType() { return "m" + std::to_string(mBitsPerChannel) + "b2"; }
template<> std::string MaterialMultiRootOctreeBuilder<BitsMaterial<3>>::GetTreeType() { return "m" + std::to_string(mBitsPerChannel) + "b3"; }
template<> std::string MaterialMultiRootOctreeBuilder<BitsMaterial<4>>::GetTreeType() { return "m" + std::to_string(mBitsPerChannel) + "b4"; }
template<> std::string MaterialMultiRootOctreeBuilder<BitsMaterial<5>>::GetTreeType() { return "m" + std::to_string(mBitsPerChannel) + "b5"; }
template<> std::string MaterialMultiRootOctreeBuilder<BitsMaterial<6>>::GetTreeType() { return "m" + std::to_string(mBitsPerChannel) + "b6"; }
template<> std::string MaterialMultiRootOctreeBuilder<BitsMaterial<7>>::GetTreeType() { return "m" + std::to_string(mBitsPerChannel) + "b7"; }
template<> std::string MaterialMultiRootOctreeBuilder<BitsMaterial<8>>::GetTreeType() { return "m" + std::to_string(mBitsPerChannel) + "b8"; }

View File

@@ -0,0 +1,120 @@
#include "MultiRootOctreeBuilder.h"
#include "../../scene/Octree/MultiRootTree.h"
#include "../Util/Stopwatch.h"
#include "OctreeLoader.h"
MultiRootOctreeBuilder::MultiRootOctreeBuilder(unsigned8 bitsPerChannel) :
BaseMaterialOctreeBuilder(),
mTree(NULL),
mCurPassTree(NULL),
mBitsPerChannel(bitsPerChannel)
{}
MultiRootOctreeBuilder::MultiRootOctreeBuilder() : MultiRootOctreeBuilder(8)
{}
MultiRootOctreeBuilder::~MultiRootOctreeBuilder() {}
std::string MultiRootOctreeBuilder::GetTreeType() { return "m" + std::to_string(mBitsPerChannel); }
void MultiRootOctreeBuilder::InitTree()
{
mTree = new MultiRootTree<>(GetTreeDepth(), 3 * mBitsPerChannel);
}
void MultiRootOctreeBuilder::FinalizeTree()
{
// Convert the final tree to a DAG
Stopwatch watch;
if (verbose) printf("Converting final tree to DAG...\n");
watch.Reset();
mTree->ToDAG();
if (verbose) printf("Done in %u ms, %llu nodes left.\n", (unsigned)(watch.GetTime() * 1000), (unsigned64)mTree->GetNodeCount());
}
void MultiRootOctreeBuilder::TerminateTree()
{
OctreeLoader::WriteCache(mTree, GetTreeType(), GetOutputFile(), verbose);
delete mTree;
}
void MultiRootOctreeBuilder::InitCurPassTree(glm::uvec3 coord)
{
mCurPassTree = new MultiRootTree<>(GetSinglePassTreeDepth(), 3 * mBitsPerChannel);
}
void MultiRootOctreeBuilder::FinalizeCurPassTree(glm::uvec3 coord)
{
Stopwatch watch;
if (mCurPassTree->GetNodeCount() > 1) // Only append the tree (and compress) if it is not empty
{
if (IsSinglePass()) // Means we just constructed the root, so no need to append
{
delete mTree;
mTree = mCurPassTree;
}
else
{
// Convert the subtree to a DAG first, this saved time when appending and converting the total tree
// Benchmark (Total build time for subtrees of depth 10, final tree of depth 12, Release mode with pool):
// 209.922 seconds without early converting
// 163.645 seconds with early converting
if (verbose) printf("Converting subtree to DAG...\n");
watch.Reset();
mCurPassTree->ToDAG();
if (verbose) printf("Converting took %d ms.\n", (int)(watch.GetTime() * 1000));
if (verbose) printf("Appending subtree... ");
mTree->Append(coord, GetAppendedTreeLevel(), mCurPassTree);
delete mCurPassTree;
if (verbose) printf("Converting current tree to DAG...\n");
mTree->ToDAG(GetAppendedTreeLevel());
}
}
else
{
delete mCurPassTree;
}
}
void MultiRootOctreeBuilder::UpdateMultiRoot(glm::uvec3 coord, glm::u8 color, unsigned32 slaveRootOffset)
{
//glm::u8 testColor = 0;
// Add nodes to the tree for the color
for (unsigned8 i = 0; i < mBitsPerChannel; i++)
{
glm::u8 mask = 1 << i;
if ((color & mask) == mask)
{
mCurPassTree->AddLeafNode(coord, slaveRootOffset + i);
//testColor += mask;
}
}
}
void MultiRootOctreeBuilder::AddMissingNode(const glm::uvec3& coordinate, const Color& color)
{
if (!mCurPassTree->HasLeaf(coordinate))
AddNode(coordinate, color);
}
void MultiRootOctreeBuilder::AddNode(const glm::uvec3& coordinate, const Color& color)
{
mCurPassTree->AddLeafNode(coordinate);
UpdateMultiRoot(coordinate, color.GetR(), 0);
UpdateMultiRoot(coordinate, color.GetG(), mBitsPerChannel);
UpdateMultiRoot(coordinate, color.GetB(), 2 * mBitsPerChannel);
}
std::vector<size_t> MultiRootOctreeBuilder::GetOctreeNodesPerLevel()
{
return mTree->GetOctreeNodesPerLevel();
}
std::vector<size_t> MultiRootOctreeBuilder::GetNodesPerLevel()
{
return mTree->GetNodesPerLevel();
}

View File

@@ -0,0 +1,40 @@
#pragma once
#include "BaseMaterialOctreeBuilder.h"
#include "../../scene/Octree/MultiRootTree.h"
class MultiRootOctreeBuilder : public BaseMaterialOctreeBuilder<Color>
{
public:
MultiRootOctreeBuilder();
MultiRootOctreeBuilder(unsigned8 bitsPerChannel);
~MultiRootOctreeBuilder() override;
std::string GetTreeType() override;
protected:
// Initialize the main tree
void InitTree() override;
void FinalizeTree() override;
// Step to finalize the main tree (for example storing it to a file)
void TerminateTree() override;
// Initialize the tree for the current pass.
void InitCurPassTree(glm::uvec3 coord) override;
// Terminate the tree in the current pass. This means it should also be appended to the main tree and deleted
void FinalizeCurPassTree(glm::uvec3 coord) override;
void AddMissingNode(const glm::uvec3& coordinate, const Color& color) override;
// Should add a node to the current pass tree at the given coordinate and color
void AddNode(const glm::uvec3& coordinate, const Color& color) override;
std::vector<size_t> GetOctreeNodesPerLevel() override;
std::vector<size_t> GetNodesPerLevel() override;
private:
void UpdateMultiRoot(glm::uvec3 coordinate, glm::u8 color, unsigned32 slaveRootOffset);
MultiRootTree<>* mTree;
MultiRootTree<>* mCurPassTree;
unsigned8 mBitsPerChannel;
};

View File

@@ -0,0 +1,24 @@
#include "NormalQuantizerFactory.h"
#include "../../scene/Material/MaterialQuantizer/NormalQuantizer.h"
BaseQuantizer<SmallNormal, NormalCompare>* NormalQuantizerFactory::Create(std::string quantizerType)
{
if (quantizerType == "") return NULL;
unsigned8 bits = std::stoi(quantizerType);
return new NormalQuantizer(bits);
}
std::string NormalQuantizerFactory::GetNormalizedType(std::string quantizerType)
{
auto quantizer = Create(quantizerType);
if (quantizer == NULL) return "";
std::string normalizedType = quantizer->GetQuantizerDescriptor();
delete quantizer;
return normalizedType;
}
std::string NormalQuantizerFactory::GetRegexMatcher()
{
return "4|6|8|10|12|14|16|18|20|22|24|26|28|30|32";
}

View File

@@ -0,0 +1,18 @@
#pragma once
#include <map>
#include "../../inc/glm/common.hpp"
#include "../../scene/Material/MaterialQuantizer/BaseQuantizer.h"
#include "../../scene/Material/SmallNormal.h"
typedef BaseQuantizer<SmallNormal, NormalCompare> BaseNormalQuantizer;
class NormalQuantizerFactory
{
public:
static BaseNormalQuantizer* Create(std::string quantizerType);
static std::string GetNormalizedType(std::string quantizerType);
// Returns a string that can be used as a regex to match all normal quantizer types and parameters
static std::string GetRegexMatcher();
};

View File

@@ -0,0 +1,217 @@
#include "OctreeBuilder.h"
#include <regex>
#include "TreeTypeParser.h"
#include "SettingsParser.h"
#include "../PathHelper.h"
#include "../StringHelper.h"
#include "../../scene/Material/Color.h"
#include "../../scene/Material/SmallNormal.h"
#include "../../scene/Material/ColorAndOpacity.h"
#include "../../scene/Material/ColorAndNormal.h"
#include "../../scene/Material/ColorAndNormalAndValue.h"
#include "../../scene/Octree/BaseTree.h"
#include "../../scene/Octree/ColorChannelMultiRootTree.h"
#include "../../scene/Octree/MultiRootBitsTree.h"
#include "../../scene/Octree/MaterialLibraryTree.h"
#include "ColorQuantizerFactory.h"
#include "NormalQuantizerFactory.h"
#include "ColorAndValueQuantizerFactory.h"
#include "CompressedTextureFactory.h"
#include "../Voxelizer/TriangleMeshVoxelizer.h"
#include "../Voxelizer/PVMVoxelizer.h"
#include "StandardOctreeBuilder.h"
#include "MultiRootOctreeBuilder.h"
#include "ColorChannelMultiRootOctreeBuilder.h"
#include "MultiBitsMultiRootOctreeBuilder.h"
#include "HierarchicalMaterialOctreeBuilder.h"
#include "UniqueIndexMaterialOctreeBuilder.h"
#include "UniqueIndexShiftColoredOctreeBuilder.h"
#include "RandomOctreeBuilder.h"
#include "MaterialLibraryMultiRootOctreeBuilder.h"
#include "../../PropertyLoader.h"
#include "../Util/Stopwatch.h"
std::string OctreeBuilder::mTreeType = "s";
OctreeBuilder::OctreeBuilder() {}
OctreeBuilder::~OctreeBuilder() {}
BaseOctreeBuilder* OctreeBuilder::mInstance = NULL;
BaseQuantizer<Color, ColorCompare>* OctreeBuilder::mColorQuantizer = NULL;
BaseQuantizer<SmallNormal, NormalCompare>* OctreeBuilder::mNormalQuantizer = NULL;
BaseQuantizer<ColorAndNormal, ColorAndNormalCompare>* OctreeBuilder::mColorAndNormalQuantizer = NULL;
BaseQuantizer<ColorAndOpacity, ColorAndOpacityCompare>* OctreeBuilder::mColorAndOpacityQuantizer = NULL;
BaseQuantizer<ColorAndNormalAndValue, ColorAndNormalAndValueCompare>* OctreeBuilder::mColorAndNormalAndValueQuantizer = NULL;
BaseOctreeBuilder* OctreeBuilder::Instance() {
return mInstance;
}
void OctreeBuilder::SetTreeType(std::string type)
{
if (mInstance != NULL && type != mTreeType)
{
Destroy();
Create(type);
}
}
std::string OctreeBuilder::GetTreeType()
{
return mTreeType;
}
void OctreeBuilder::Create()
{
// Destroy any old octree builders (to prevent memory leaks)
Destroy();
// Create the new octree builder
Create(SettingsParser::GetTreeTypeFromSettings());
}
void OctreeBuilder::Destroy()
{
if (mInstance != NULL)
delete mInstance;
mInstance = NULL;
if (mColorQuantizer != NULL)
delete mColorQuantizer;
mColorQuantizer = NULL;
if (mColorAndNormalQuantizer != NULL) delete mColorAndNormalQuantizer;
mColorAndNormalQuantizer = NULL;
}
OctreeBuilderStatistics OctreeBuilder::BuildOctree()
{
PropertyLoader::Create();
return BuildOctree(SettingsParser::GetMaxLevelFromSettings(), PropertyLoader::Instance()->GetProperty("obj_to_dag"), PropertyLoader::Instance()->GetProperty("dag_file"));
}
OctreeBuilderStatistics OctreeBuilder::BuildOctree(unsigned8 depth)
{
PropertyLoader::Create();
return BuildOctree(depth, PropertyLoader::Instance()->GetProperty("obj_to_dag"), PropertyLoader::Instance()->GetProperty("dag_file"));
}
OctreeBuilderStatistics OctreeBuilder::BuildOctree(unsigned8 depth, std::string sceneFile, std::string dagFile)
{
mInstance->SetOutputFile(dagFile);
mInstance->SetVerbose(SettingsParser::GetVerboseFromSettings());
mInstance->SetForceRebuild(SettingsParser::GetForceRebuildFromSettings());
std::string filetype = PathHelper::GetExtension(sceneFile);
StringHelper::ToUpper(filetype);
BaseVoxelizer* voxelizer;
if (filetype == "PVM" || filetype == "DAT")
voxelizer = new PVMVoxelizer();
else
voxelizer = new TriangleMeshVoxelizer();
voxelizer->Initialize();
voxelizer->LoadScene(sceneFile);
OctreeBuilderStatistics res = mInstance->BuildOctree(*voxelizer, depth, (unsigned8)PropertyLoader::Instance()->GetIntProperty("octreebuilder_max_single_pass_layers"));
voxelizer->UnloadScene();
delete voxelizer;
return res;
}
void OctreeBuilder::Create(std::string type)
{
TreeTypeDescriptor descr = TreeTypeParser::GetTreeTypeDescriptor(type);
if (descr.material == "c")
mColorQuantizer = ColorQuantizerFactory::Create(descr.quantizer);
else if (descr.material == "n")
mNormalQuantizer = NormalQuantizerFactory::Create(descr.quantizer);
else if (descr.material == "cn")
mColorAndNormalQuantizer = ColorAndNormalQuantizerFactory::Create(descr.quantizer);
else if (descr.material == "cnr")
mColorAndNormalAndValueQuantizer = ColorAndNormalAndValueQuantizerFactory::Create(descr.quantizer);
else if (descr.material == "co")
mColorAndOpacityQuantizer = ColorAndOpacityQuantizerFactory::Create(descr.quantizer);
if (descr.globalType == UNIQUEINDEX)
{
// Parse the number of levels without material information (LOD) if it isn't empty.
unsigned8 levelsWithoutMaterial = TreeTypeParser::GetLevelsWithoutMaterials(descr);
if (descr.additionalTypeInfo != "" && descr.additionalTypeInfo[0] == 's')
{
assert(descr.material == "c");
mInstance = new UniqueIndexShiftColoredOctreeBuilder(descr.textureCompressor, mColorQuantizer, levelsWithoutMaterial);
}
else
{
if (descr.material == "c")
mInstance = new UniqueIndexMaterialOctreeBuilder<Color, ColorCompare>(descr.textureCompressor, mColorQuantizer, levelsWithoutMaterial);
else if (descr.material == "n")
mInstance = new UniqueIndexMaterialOctreeBuilder<SmallNormal, NormalCompare>(descr.textureCompressor, mNormalQuantizer, levelsWithoutMaterial);
else if (descr.material == "cn")
mInstance = new UniqueIndexMaterialOctreeBuilder<ColorAndNormal, ColorAndNormalCompare>(descr.textureCompressor, mColorAndNormalQuantizer, levelsWithoutMaterial);
else if (descr.material == "cnr")
mInstance = new UniqueIndexMaterialOctreeBuilder<ColorAndNormalAndValue, ColorAndNormalAndValueCompare>(descr.textureCompressor, mColorAndNormalAndValueQuantizer, levelsWithoutMaterial);
else if (descr.material == "co")
mInstance = new UniqueIndexMaterialOctreeBuilder<ColorAndOpacity, ColorAndOpacityCompare>(descr.textureCompressor, mColorAndOpacityQuantizer, levelsWithoutMaterial);
}
}
else if (descr.globalType == HIERARCHICAL && descr.additionalTypeInfo == "")
{
if (descr.material == "c")
mInstance = new HierarchicalMaterialOctreeBuilder<Color, ColorCompare>(mColorQuantizer);
else if (descr.material == "n")
mInstance = new HierarchicalMaterialOctreeBuilder<SmallNormal, NormalCompare>(mNormalQuantizer);
else if (descr.material == "cn")
mInstance = new HierarchicalMaterialOctreeBuilder<ColorAndNormal, ColorAndNormalCompare>(mColorAndNormalQuantizer);
else if (descr.material == "cnr")
mInstance = new HierarchicalMaterialOctreeBuilder<ColorAndNormalAndValue, ColorAndNormalAndValueCompare>(mColorAndNormalAndValueQuantizer);
else if (descr.material == "co")
mInstance = new HierarchicalMaterialOctreeBuilder<ColorAndOpacity, ColorAndOpacityCompare>(mColorAndOpacityQuantizer);
}
else if (descr.globalType == MULTIROOT)
{
if (descr.additionalTypeInfo == "cc")
mInstance = new MaterialMultiRootOctreeBuilder<ColorChannel>(type);
else if (descr.additionalTypeInfo == "" || (descr.additionalTypeInfo.size() == 1 && descr.additionalTypeInfo[0] >= 49 && descr.additionalTypeInfo[0] <= 56))
{
unsigned8 bitsPerChannel = TreeTypeParser::GetBitsPerChannel(descr);
mInstance = new MultiRootOctreeBuilder(bitsPerChannel);
}
else
{
unsigned8 bitsPerChannel = TreeTypeParser::GetBitsPerChannel(descr);
unsigned8 bitsPerTree = TreeTypeParser::GetBitsPerTree(descr);
switch (bitsPerTree)
{
case 1: mInstance = new MaterialMultiRootOctreeBuilder<BitsMaterial<1>>(type, bitsPerChannel); break;
case 2: mInstance = new MaterialMultiRootOctreeBuilder<BitsMaterial<2>>(type, bitsPerChannel); break;
case 3: mInstance = new MaterialMultiRootOctreeBuilder<BitsMaterial<3>>(type, bitsPerChannel); break;
case 4: mInstance = new MaterialMultiRootOctreeBuilder<BitsMaterial<4>>(type, bitsPerChannel); break;
case 5: mInstance = new MaterialMultiRootOctreeBuilder<BitsMaterial<5>>(type, bitsPerChannel); break;
case 6: mInstance = new MaterialMultiRootOctreeBuilder<BitsMaterial<6>>(type, bitsPerChannel); break;
case 7: mInstance = new MaterialMultiRootOctreeBuilder<BitsMaterial<7>>(type, bitsPerChannel); break;
case 8: mInstance = new MaterialMultiRootOctreeBuilder<BitsMaterial<8>>(type, bitsPerChannel); break;
}
}
}
else if (descr.globalType == RANDOM)
{
mInstance = new RandomOctreeBuilder();
}
else if (descr.globalType == BITTREES)
{
if (descr.additionalTypeInfo == "l" && descr.material == "c")
mInstance = new MaterialLibraryMultiRootOctreeBuilder(mColorQuantizer);
}
else
{
mInstance = new StandardOctreeBuilder();
}
mTreeType = type;
}

View File

@@ -0,0 +1,46 @@
#pragma once
#include "../Defines.h"
#include "OctreeBuilderStatistics.h"
#include "BaseOctreeBuilder.h"
#include "../../scene/Material/MaterialQuantizer/ColorAndValueQuantizer.h"
//#include "../../scene/Material/MaterialQuantizer/BaseQuantizer.h"
//#include "../../scene/Material/SmallNormal.h"
class Root;
class BaseColorQuantizer;
class SmallNormal;
struct NormalCompare;
class TriangleMeshVoxelizer;
class PVMVoxelizer;
// Singleton wrapper that reads the settings and creates the correct octreebuilder
class OctreeBuilder
{
friend BaseOctreeBuilder;
public:
static void Create();
static void SetTreeType(std::string type);
static std::string GetTreeType();
static void Destroy();
static OctreeBuilderStatistics BuildOctree();
static OctreeBuilderStatistics BuildOctree(unsigned8 depth);
static OctreeBuilderStatistics BuildOctree(unsigned8 depth, std::string sceneFile, std::string dagFile);
static BaseOctreeBuilder* Instance();
private:
static BaseOctreeBuilder* mInstance;
static BaseQuantizer<Color, ColorCompare>* mColorQuantizer;
static BaseQuantizer<SmallNormal, NormalCompare>* mNormalQuantizer;
static BaseQuantizer<ColorAndNormal, ColorAndNormalCompare>* mColorAndNormalQuantizer;
static BaseQuantizer<ColorAndOpacity, ColorAndOpacityCompare>* mColorAndOpacityQuantizer;
static BaseQuantizer<ColorAndNormalAndValue, ColorAndNormalAndValueCompare>* mColorAndNormalAndValueQuantizer;
static void Create(std::string type);
static std::string mTreeType;
OctreeBuilder();
~OctreeBuilder();
};

View File

@@ -0,0 +1,73 @@
#pragma once
#include <vector>
#include <string>
#include "../Defines.h"
struct OctreeBuilderStatistics
{
public:
unsigned8 levels;
std::string type;
double validCoordBuildtime;
double depthPeelingTime;
double finalizationTime;
double totalTime;
std::vector<size_t> octreeNodesPerLevel;
std::vector<size_t> dagNodesPerLevel;
OctreeBuilderStatistics(unsigned8 levels)
{
this->levels = levels;
validCoordBuildtime = depthPeelingTime = finalizationTime = totalTime = 0;
octreeNodesPerLevel = std::vector<size_t>(levels + 1);
dagNodesPerLevel = std::vector<size_t>(levels + 1);
}
size_t GetOctreeNodeCount() const
{
size_t octreeNodeCount = 0;
for (unsigned8 level = 0; level <= levels; level++)
octreeNodeCount += octreeNodesPerLevel[level];
return octreeNodeCount;
}
size_t GetDAGNodeCount() const
{
size_t dagNodeCount = 0;
for (unsigned8 level = 0; level <= levels; level++)
dagNodeCount += dagNodesPerLevel[level];
return dagNodeCount;
}
long double GetCompression() const
{
return (long double)GetOctreeNodeCount() / (long double)GetDAGNodeCount();
}
long double GetCompression(unsigned8 level) const
{
return (long double)octreeNodesPerLevel[level] / (long double)dagNodesPerLevel[level];
}
void Print() const
{
printf("DAG created with the following properties:\n");
printf("Total time : %10u ms\n", (unsigned)(totalTime * 1000));
printf("Valid subtree calculation time : %10u ms\n", (unsigned)(validCoordBuildtime * 1000));
printf("Depth peeling time : %10u ms\n", (unsigned)(depthPeelingTime * 1000));
printf("Subtree finalization time : %10u ms\n", (unsigned)(finalizationTime * 1000));
printf("Total nodes : %10zu\n", GetOctreeNodeCount());
printf("DAG nodes : %10zu\n", GetDAGNodeCount());
printf("Compression : %10.2Lf\n", GetCompression());
printf("\n");
printf("Statistics per level:\n");
printf("Level Octree (#) DAG (#) Compression\n");
for (unsigned8 level = 0; level <= levels; level++)
printf("%5d %12zu %12zu %12.2Lf\n", level, octreeNodesPerLevel[level], dagNodesPerLevel[level], GetCompression(level));
}
};

View File

@@ -0,0 +1,476 @@
#include "OctreeConverter.h"
#include "OctreeLoader.h"
#include "SettingsParser.h"
#include "TreeTypeParser.h"
#include "ColorQuantizerFactory.h"
#include "CompressedTextureFactory.h"
#include "../Util/Stopwatch.h"
#include "../../scene/Material/MaterialQuantizer/ColorQuantizer/BaseColorQuantizer.h"
#include "../../PropertyLoader.h"
#include <regex>
#include "../../scene/Octree/BaseTree.h"
#include "../../scene/Octree/Tree.h"
#include "../../scene/Octree/MaterialLibraryTree.h"
#include "../../scene/Octree/HierarchicalShiftingColoredTree.h"
#include "../../scene/Octree/HierarchicalColorsOnlyTree.h"
#include "../../scene/Octree/MaterialLibraryUniqueIndexTree.h"
#ifdef _WIN32
#include <Windows.h>
#endif
bool OctreeConverter::Convert()
{
PropertyLoader::Create();
auto propertyLoader = PropertyLoader::Instance();
if (!propertyLoader->GetBoolProperty("octreebuilder_tryconvert") || !propertyLoader->GetBoolProperty("octreebuilder_usecache"))
return false;
bool verbose = propertyLoader->GetProperty("octreebuilder_verbose") != "0";
if (verbose) printf("Trying to convert an existing tree to the wanted treetype.\n");
std::string octreeType = propertyLoader->GetProperty("octree_type");
unsigned8 maxLevel = propertyLoader->GetIntProperty("shader_max_level");
std::string filename = propertyLoader->GetProperty("dag_file");
bool res = ConvertTo(octreeType, maxLevel, filename, verbose);
if (verbose)
{
if (res) printf("Conversion succeeded\n");
if (!res) printf("No valid conversion found\n");
}
return res;
}
bool OctreeConverter::ConvertTo(std::string destinationType, unsigned8 destinationDepth, std::string dagFileName, bool verbose)
{
#ifdef _WIN32
// Go through all .oct files that can support the given dag file.
// For each of the files found, try conversion.
std::string dagStrippedFilename = dagFileName;
const size_t last_slash_idx = dagStrippedFilename.find_last_of("\\/");
if (std::string::npos != last_slash_idx)
{
dagStrippedFilename.erase(0, last_slash_idx + 1);
}
std::string validFileWildcard = std::string(dagFileName + "*.oct");
std::regex fileNameMatcher(dagStrippedFilename + "_([0-9]+)_(.+).oct");
std::smatch sm;
WIN32_FIND_DATA FindFileData;
HANDLE hFind = FindFirstFile(validFileWildcard.c_str(), &FindFileData);
LARGE_INTEGER filesize;
// Find all convertable files
std::vector<FileInfo> convertableFiles;
while (hFind != INVALID_HANDLE_VALUE)
{
// Try conversion
std::string foundFilename(FindFileData.cFileName);
filesize.LowPart = FindFileData.nFileSizeLow;
filesize.HighPart = FindFileData.nFileSizeHigh;
// Extract the level and type from the filename
if (std::regex_match(foundFilename, sm, fileNameMatcher))
{
unsigned8 sourceDepth = std::stoi(sm.str(1));
std::string sourceType = sm.str(2);
if (CanConvert(sourceType, destinationType, sourceDepth, destinationDepth))
convertableFiles.push_back(FileInfo(foundFilename, sourceType, sourceDepth, filesize.QuadPart));
}
if (!FindNextFile(hFind, &FindFileData))
{
FindClose(hFind);
hFind = INVALID_HANDLE_VALUE;
}
}
// Sort them on filesize, assuming that smaller files are always quicker to convert
std::sort(convertableFiles.begin(), convertableFiles.end(), [&](const FileInfo& a, const FileInfo& b)
{
return a.filesize < b.filesize;
});
for (const FileInfo& info : convertableFiles)
{
if (Convert(info.treeType, destinationType, info.depth, destinationDepth, dagFileName, verbose))
return true;
}
#endif
return false;
}
// Check if a certain conversion is valid (theore
bool OctreeConverter::CanConvert(std::string sourceType, std::string destinationType, unsigned8 sourceDepth, unsigned8 destinationDepth)
{
// Converting to the same type always works
if (sourceType == destinationType && sourceDepth == destinationDepth)
return true;
if (sourceDepth != destinationDepth)
return false; // Can't change the depth...
TreeTypeDescriptor destDescr = TreeTypeParser::GetTreeTypeDescriptor(destinationType);
TreeTypeDescriptor sourceDescr = TreeTypeParser::GetTreeTypeDescriptor(sourceType);
std::string normalizedSourceQuantizerType = ColorQuantizerFactory::GetNormalizedType(sourceDescr.quantizer);
std::string normalizedDestinationQuantizerType = ColorQuantizerFactory::GetNormalizedType(destDescr.quantizer);
// Any tree type can be converted to standard if the source depth is at least as deep as the destination depth
if (destDescr.globalType == STANDARD) return true;
else if (destDescr.globalType == HIERARCHICAL)
{
return sourceDepth == destinationDepth
&& sourceDescr.globalType == HIERARCHICAL
&& sourceDescr.additionalTypeInfo == "" && (destDescr.additionalTypeInfo == "" || destDescr.additionalTypeInfo == "s")
&& destDescr.material == sourceDescr.material
&& (normalizedSourceQuantizerType == "" || normalizedSourceQuantizerType == normalizedDestinationQuantizerType);
}
else if (destDescr.globalType == UNIQUEINDEX)
{
return sourceDepth == destinationDepth
&& sourceDescr.globalType == UNIQUEINDEX && sourceDescr.material == "c"
&& destDescr.material == "c"
&& (normalizedSourceQuantizerType == "" || normalizedSourceQuantizerType == normalizedDestinationQuantizerType)
&& sourceDescr.additionalTypeInfo == "" && destDescr.additionalTypeInfo == "";
}
else if (destDescr.globalType == ONLYMATERIAL)
{
return sourceDepth == destinationDepth
&& destDescr.material == "c" && sourceDescr.material == "c"
&& sourceDescr.globalType == HIERARCHICAL
&& (normalizedSourceQuantizerType == "" || normalizedSourceQuantizerType == normalizedDestinationQuantizerType);
}
return false;
}
bool OctreeConverter::Convert(std::string sourceType, std::string destinationType, unsigned8 sourceDepth, unsigned8 destinationDepth, std::string dagFileName, bool verbose)
{
if (!CanConvert(sourceType, destinationType, sourceDepth, destinationDepth)) return false;
TreeTypeDescriptor destDescr = TreeTypeParser::GetTreeTypeDescriptor(destinationType);
switch (destDescr.globalType)
{
case HIERARCHICAL:
if (destDescr.material == "c")
{
if (destDescr.additionalTypeInfo == "s")
return ConvertToHierarchicalColorShift(sourceType, destinationType, sourceDepth, destinationDepth, dagFileName, verbose);
else
return ConvertToHierarchicalColored(sourceType, destinationType, sourceDepth, destinationDepth, dagFileName, verbose);
}
break;
case UNIQUEINDEX: return ConvertToUniqueIndex(sourceType, destinationType, sourceDepth, destinationDepth, dagFileName, verbose);
case STANDARD: return ConvertToStandard(sourceType, sourceDepth, destinationDepth, dagFileName, verbose);
case ONLYMATERIAL: return ConvertToOnlyMaterials(sourceType, destinationType, sourceDepth, destinationDepth, dagFileName, verbose);
}
return false;
}
bool OctreeConverter::ConvertToStandard(std::string sourceType, unsigned8 sourceDepth, unsigned8 destinationDepth, std::string dagFileName, bool verbose)
{
if (verbose) printf("Found valid octree file: %s, converting...\n", OctreeLoader::GetFullFilename(sourceType, sourceDepth, dagFileName).c_str());
BaseTree* originalTree = OctreeLoader::ReadCache(sourceType, sourceDepth, dagFileName);
if (originalTree == NULL) return false;
Tree<>* standardTree = (Tree<>*)OctreeLoader::CreateTree("s", destinationDepth);
originalTree->Shave(destinationDepth);
standardTree->MoveShallow(originalTree);
delete originalTree;
standardTree->ToDAG();
OctreeLoader::WriteCache(standardTree, "s", dagFileName, verbose);
delete standardTree;
return true;
}
static void CompressHierarchicalTreeColors(MaterialLibraryTree<Color, ColorCompare>* tree, std::string compressor)
{
std::vector<Color> leafMaterials = tree->GetLeafMaterials();
tbb::parallel_sort(leafMaterials.begin(), leafMaterials.end(), ColorCompare());
leafMaterials.erase(std::unique(leafMaterials.begin(), leafMaterials.end()), leafMaterials.end());
std::vector<glm::u8vec3> leafColors(leafMaterials.size());
tbb::parallel_for(size_t(0), leafMaterials.size(), [&](size_t i)
{
leafColors[i] = leafMaterials[i].GetColor();
});
tbb::parallel_sort(leafColors.begin(), leafColors.end(), u8vec3comparer());
leafColors.erase(std::unique(leafColors.begin(), leafColors.end()), leafColors.end());
auto colorQuantizer = ColorQuantizerFactory::Create(compressor);
auto colorReplacers = colorQuantizer->QuantizeColors(leafColors);
std::map<Color, Color, ColorCompare> materialReplacers;
for (auto colorReplacer = colorReplacers->begin(); colorReplacer != colorReplacers->end(); std::advance(colorReplacer, 1))
materialReplacers.insert(std::make_pair(Color(colorReplacer->first), Color(colorReplacer->second)));
delete colorReplacers;
delete colorQuantizer;
tree->ReplaceLeafMaterials(materialReplacers);
}
bool OctreeConverter::ConvertToHierarchicalColored(std::string sourceType, std::string destinationType, unsigned8 sourceDepth, unsigned8 destinationDepth, std::string dagFileName, bool verbose)
{
// Only works if trees are of the same depth. Higher depth will mean the colors are averaged which might lead to wrong results
if (sourceDepth != destinationDepth)
return false;
TreeTypeDescriptor sourceDescr = TreeTypeParser::GetTreeTypeDescriptor(sourceType);
TreeTypeDescriptor destDescr = TreeTypeParser::GetTreeTypeDescriptor(destinationType);
// Make sure that the destination type is actually what is expected
if (destDescr.globalType != HIERARCHICAL || destDescr.material != "c" || destDescr.additionalTypeInfo != "")
return false;
// Only colored and hierarchical trees can be converted, so they need to match this regex
if (sourceDescr.globalType != HIERARCHICAL || sourceDescr.additionalTypeInfo != "" || sourceDescr.material != "c")
return false;
std::string normalizedSourceQuantizerType = ColorQuantizerFactory::GetNormalizedType(sourceDescr.quantizer);
std::string normalizedDestinationQuantizerType = ColorQuantizerFactory::GetNormalizedType(destDescr.quantizer);
// Only trees with full colors or the same compression scheme as the destination type can be compressed
if (normalizedSourceQuantizerType != "" && normalizedSourceQuantizerType != normalizedDestinationQuantizerType)
return false;
// Now there are 4 options:
// Source is hierarchical fully colored
// Source is not-hierarchical fully colored
// Source is hierarchical compressed colored
// Source is not-hierarchical compressed colored
if (sourceDescr.globalType == HIERARCHICAL)
{
// Apparently we already have the desired type, so just return true
if (normalizedDestinationQuantizerType == normalizedSourceQuantizerType)
return true;
// Convertert fully colored hierarchical root to compressed colored hierarchical root
if (verbose) printf("Converting fully colored tree in %s to compressed colored tree...\n", OctreeLoader::GetFullFilename(sourceType, sourceDepth, dagFileName).c_str());
MaterialLibraryTree<Color, ColorCompare>* source = (MaterialLibraryTree<Color, ColorCompare>*)OctreeLoader::ReadCache(sourceType, sourceDepth, dagFileName);
if (source == NULL)
{
if (verbose) printf("Reading the source tree failed...\n");
return false;
}
if (verbose) printf("Quantizing original materials...\n");
CompressHierarchicalTreeColors(source, normalizedDestinationQuantizerType);
if (verbose) printf("Clearing propagation...\n");
source->ClearPropagation();
if (verbose) printf("Recalculating DAG (since the materials are now compressed).\n");
source->ToDAG();
if (verbose) printf("Propagating materials...\n");
source->PropagateMaterials();
OctreeLoader::WriteCache(source, destinationType, dagFileName, verbose);
delete source;
return true;
}
return false;
}
bool OctreeConverter::ConvertToUniqueIndex(std::string sourceType, std::string destinationType, unsigned8 sourceDepth, unsigned8 destinationDepth, std::string dagFileName, bool verbose)
{
// Conversion currently only allowed from other UniqueIndexRoots
if (sourceDepth != destinationDepth) return false;
TreeTypeDescriptor sourceDescr = TreeTypeParser::GetTreeTypeDescriptor(sourceType);
TreeTypeDescriptor destDescr = TreeTypeParser::GetTreeTypeDescriptor(destinationType);
std::string normalizedSourceQuantizerType = ColorQuantizerFactory::GetNormalizedType(sourceDescr.quantizer);
std::string normalizedDestinationQuantizerType = ColorQuantizerFactory::GetNormalizedType(destDescr.quantizer);
// Check the source and destination type to be valid
if (destDescr.globalType != UNIQUEINDEX || destDescr.material != "c")
return false;
if ((/*sourceDescr.globalType != HIERARCHICAL && sourceDescr.globalType != COLORED && */sourceDescr.globalType != UNIQUEINDEX) || sourceDescr.material != "c")
return false;
// Only trees with full colors or the same compression scheme as the destination type can be compressed
if (normalizedSourceQuantizerType != "" && normalizedSourceQuantizerType != normalizedDestinationQuantizerType)
return false;
if (sourceDescr.additionalTypeInfo != "" || destDescr.additionalTypeInfo != "")
return false;
if (sourceDescr.globalType == UNIQUEINDEX)
{
// Check if the current type is not already the desired type
if (sourceDescr.textureCompressor == destDescr.quantizer && normalizedDestinationQuantizerType == normalizedSourceQuantizerType) return true;
// If not, load the source tree
if (verbose) printf("Reading tree in %s...\n", OctreeLoader::GetFullFilename(sourceType, sourceDepth, dagFileName).c_str());
MaterialLibraryUniqueIndexTree<Color, ColorCompare>* source = (MaterialLibraryUniqueIndexTree<Color, ColorCompare>*)OctreeLoader::ReadCache(sourceType, sourceDepth, dagFileName);
if (source == NULL)
{
if (verbose) printf("Reading the source tree failed...\n");
return false;
}
if (normalizedSourceQuantizerType != normalizedDestinationQuantizerType)
{
// Quantize all materials
if (verbose) printf("Quantizing materials...");
Stopwatch watch; watch.Reset();
std::vector<Color> materials = source->GetUniqueMaterials();
BaseColorQuantizer* colorQuantizer = ColorQuantizerFactory::Create(normalizedDestinationQuantizerType);
std::map<Color, Color, ColorCompare>* colorReplacers = colorQuantizer->QuantizeMaterials(materials);
delete colorQuantizer;
std::unordered_map<Color, Color> colorReplacersUnorderedMap;
for (auto colorReplacer : *colorReplacers) colorReplacersUnorderedMap.insert(colorReplacer);
delete colorReplacers;
if (verbose) printf("Materials quantized in %u ms.\n Replacing materials...", (unsigned)(watch.GetTime() * 1000.0));
watch.Reset();
source->ReplaceMaterials(colorReplacersUnorderedMap);
if (verbose) printf("Materials replaced in %u ms.\n", (unsigned)(watch.GetTime() * 1000.0));
}
if (sourceDescr.textureCompressor != destDescr.textureCompressor)
{
// Replace the compressed texture type
if (verbose) printf("Replacing texture compressor...");
Stopwatch watch; watch.Reset();
source->ReplaceCompressedTexture(CompressedTextureFactory<MaterialLibraryPointer>::GetCompressedTexture(destDescr.textureCompressor));
if (verbose) printf("Texture compressor replaced in %u ms.\n", (unsigned)(watch.GetTime() * 1000.0));
}
else
{
if (verbose) printf("Recompressing texture with quantized materials...");
Stopwatch watch; watch.Reset();
source->Recompress();
if (verbose) printf("Done in %u ms.\n", (unsigned)(watch.GetTime() * 1000.0));
}
OctreeLoader::WriteCache(source, destinationType, dagFileName, verbose);
delete source;
return true;
}
return false;
}
bool OctreeConverter::ConvertToHierarchicalColorShift(std::string sourceType, std::string destinationType, unsigned8 sourceDepth, unsigned8 destinationDepth, std::string dagFileName, bool verbose)
{
if (sourceDepth != destinationDepth)
return false;
TreeTypeDescriptor sourceDescr = TreeTypeParser::GetTreeTypeDescriptor(sourceType);
TreeTypeDescriptor destDescr = TreeTypeParser::GetTreeTypeDescriptor(destinationType);
// Make sure the source and destination type is valid
assert(destDescr.globalType == HIERARCHICAL && destDescr.material == "c" && destDescr.additionalTypeInfo == "s");
assert(sourceDescr.globalType == HIERARCHICAL && sourceDescr.material == "c");
std::string normalizedSourceQuantizerType = ColorQuantizerFactory::GetNormalizedType(sourceDescr.quantizer);
std::string normalizedDestinationQuantizerType = ColorQuantizerFactory::GetNormalizedType(destDescr.quantizer);
// Only trees with full colors or the same compression scheme as the destination type can be compressed
if (normalizedSourceQuantizerType != "" && normalizedSourceQuantizerType != normalizedDestinationQuantizerType)
return false;
// Remove the second characted, as that is the one that indicates that we want a shifting tree
std::string hierarchicalColoredDestinationType = std::string(destinationType).erase(1, 1);
// If we're dealing with a normal "colored octree" that isn't hierarchical, or has a different compression, convert it to hierarchical first:
if (TreeTypeParser::GetGlobalType(sourceType) != HIERARCHICAL || normalizedDestinationQuantizerType != normalizedSourceQuantizerType)
{
if (verbose) printf("Checking cache for hierarchical tree with correct compression...");
if (OctreeLoader::VerifyCache(hierarchicalColoredDestinationType, sourceDepth, dagFileName))
{
if (verbose) printf("Cache file found.\n");
}
else
{
if (verbose) printf("Nothing found.\nConverting non-hierarchical tree %s to hierarchical tree with correct compression.\n", OctreeLoader::GetFullFilename(sourceType, sourceDepth, dagFileName).c_str());
if (OctreeConverter::ConvertToHierarchicalColored(sourceType, hierarchicalColoredDestinationType, sourceDepth, destinationDepth, dagFileName, verbose))
return false;
}
}
// By now we're sure that a fully colored or compressed colored hierarchical octree with the same compression exists
// Let's convert it to a shifter:
if (verbose) printf("Reading source tree to new shifting tree...");
auto source = (MaterialLibraryTree<Color, ColorCompare>*)OctreeLoader::ReadCache(hierarchicalColoredDestinationType, destinationDepth, dagFileName);
auto dest = (HierarchicalShiftingColoredTree*)OctreeLoader::CreateTree(destinationType, destinationDepth);
std::vector<Color> sourceMaterials = source->GetMaterials();
dest->MoveShallow(source);
delete source;
dest->SetMaterials(sourceMaterials);
if (verbose) printf("\nReplacing original colors by color shifts...\n");
dest->ReplaceColorsByShifts();
if (verbose) printf("Converting the tree containing color shifts to a DAG...\n");
dest->ToDAG();
OctreeLoader::WriteCache(dest, destinationType, dagFileName, verbose);
delete dest;
if (verbose) printf("Conversion succeeded");
return true;
}
bool OctreeConverter::ConvertToOnlyMaterials(std::string sourceType, std::string destinationType, unsigned8 sourceDepth, unsigned8 destinationDepth, std::string dagFileName, bool verbose)
{
if (sourceDepth != destinationDepth)
return false;
TreeTypeDescriptor destDescr = TreeTypeParser::GetTreeTypeDescriptor(destinationType);
if (destDescr.globalType != ONLYMATERIAL)
return false;
if (destDescr.material == "c")
return ConvertToOnlyColors(sourceType, destinationType, destinationDepth, dagFileName, verbose);
// No valid type found
return false;
}
bool OctreeConverter::ConvertToOnlyColors(std::string sourceType, std::string destinationType, unsigned8 destinationDepth, std::string dagFileName, bool verbose)
{
TreeTypeDescriptor destDescr = TreeTypeParser::GetTreeTypeDescriptor(destinationType);
TreeTypeDescriptor sourceDescr = TreeTypeParser::GetTreeTypeDescriptor(sourceType);
// Make sure that the destination type is actually what is expected
if (destDescr.globalType != ONLYMATERIAL || destDescr.material != "c") // Make sure the destination is actual the type we want
return false;
// Only colored and hierarchical trees can be converted to only color trees
if (sourceDescr.globalType != HIERARCHICAL || sourceDescr.material != "c")
return false;
std::string normalizedSourceQuantizerType = ColorQuantizerFactory::GetNormalizedType(sourceDescr.quantizer);
std::string normalizedDestinationQuantizerType = ColorQuantizerFactory::GetNormalizedType(destDescr.quantizer);
// Only trees with full colors or the same compression scheme as the destination type can be compressed
if (normalizedSourceQuantizerType != "" && normalizedSourceQuantizerType != normalizedDestinationQuantizerType)
return false;
// Remove the second characted, as that is the one that indicates that we want a shifting tree
std::string hierarchicalColoredDestinationType = "h" + std::string(destinationType).erase(0, 1);
// If we're dealing with a normal "colored octree" that isn't hierarchical, or has a different compression, convert it to hierarchical first:
if (TreeTypeParser::GetGlobalType(sourceType) != HIERARCHICAL || normalizedDestinationQuantizerType != normalizedSourceQuantizerType)
{
if (verbose) printf("Checking cache for hierarchical tree with correct compression...");
if (OctreeLoader::VerifyCache(hierarchicalColoredDestinationType, destinationDepth, dagFileName))
{
if (verbose) printf("Cache file found.\n");
}
else
{
if (verbose) printf("Nothing found.\nConverting non-hierarchical tree %s to hierarchical tree with correct compression.\n", OctreeLoader::GetFullFilename(sourceType, destinationDepth, dagFileName).c_str());
if (OctreeConverter::ConvertToHierarchicalColored(sourceType, hierarchicalColoredDestinationType, destinationDepth, destinationDepth, dagFileName, verbose))
return false;
}
}
// By now we're sure that a fully colored or compressed colored hierarchical octree with the same compression exists
// Let's convert it to a tree with only colors:
if (verbose) printf("Reading source tree and moving to new tree with colors only...");
auto source = (MaterialLibraryTree<Color, ColorCompare>*)OctreeLoader::ReadCache(hierarchicalColoredDestinationType, destinationDepth, dagFileName);
auto dest = (HierarchicalColorsOnlyTree*)OctreeLoader::CreateTree(destinationType, destinationDepth);
std::vector<Color> sourceMaterials = source->GetMaterials();
dest->MoveShallow(source);
delete source;
dest->SetMaterials(sourceMaterials);
if (verbose) printf("\nConverting to DAG while merging empty nodes...\n");
auto quantizer = ColorQuantizerFactory::Create(normalizedDestinationQuantizerType);
dest->ToDAG(quantizer);
OctreeLoader::WriteCache(dest, destinationType, dagFileName, verbose);
delete dest;
if (verbose) printf("Conversion succeeded");
return true;
}

View File

@@ -0,0 +1,36 @@
#pragma once
#include <string>
#include "../Defines.h"
class OctreeConverter
{
public:
// Calls ConvertTo for the required tree type based on the settings
static bool Convert();
// Given some destination type, tries to find some cache file that can be converted
static bool ConvertTo(std::string destinationType, unsigned8 destinationDepth, std::string dagFileName, bool verbose = false);
// Given a source and destination type, converts one to the other and stores the converted tree. Returns true if conversion succeeded
static bool Convert(std::string sourceType, std::string destinationType, unsigned8 sourceDepth, unsigned8 destinationDepth, std::string dagFileName, bool verbose = false);
static bool CanConvert(std::string sourceType, std::string destinationType, unsigned8 sourceDepth, unsigned8 destinationDept);
private:
static bool ConvertToStandard(std::string sourceType, unsigned8 sourceDepth, unsigned8 destinationDepth, std::string dagFileName, bool verbose = false);
static bool ConvertToHierarchicalColored(std::string sourceType, std::string destinationType, unsigned8 sourceDepth, unsigned8 destinationDepth, std::string dagFileName, bool verbose = false);
static bool ConvertToHierarchicalColorShift(std::string sourceType, std::string destinationType, unsigned8 sourceDepth, unsigned8 destinationDepth, std::string dagFileName, bool verbose = false);
static bool ConvertToUniqueIndex(std::string sourceType, std::string destinationType, unsigned8 sourceDepth, unsigned8 destinationDepth, std::string dagFileName, bool verbose = false);
static bool ConvertToOnlyMaterials(std::string sourceType, std::string destinationType, unsigned8 sourceDepth, unsigned8 destinationDepth, std::string dagFileName, bool verbose = false);
static bool ConvertToOnlyColors(std::string sourceType, std::string destinationType, unsigned8 destinationDepth, std::string dagFilename, bool verbose = false);
struct FileInfo{
std::string filename;
unsigned64 filesize;
std::string treeType;
unsigned8 depth;
FileInfo(std::string filename, std::string treetype, unsigned8 depth, unsigned64 filesize) : filename(filename), filesize(filesize), treeType(treetype), depth(depth) { }
};
};

View File

@@ -0,0 +1,782 @@
#include "OctreeLoader.h"
#include "TreeTypeParser.h"
#include "SettingsParser.h"
#include "../../scene/Material/Color.h"
#include "../../scene/Material/SmallNormal.h"
#include "../../scene/Material/ColorAndNormal.h"
#include "../../scene/Material/ColorAndNormalAndValue.h"
#include "../../scene/Material/ColorAndOpacity.h"
#include "../../scene/Octree/BaseTree.h"
#include "../../scene/Octree/Tree.h"
#include "../../scene/Octree/MaterialTree.h"
#include "../../scene/Octree/MultiRootTree.h"
#include "../../scene/Octree/ColorChannelMultiRootTree.h"
#include "../../scene/Octree/MultiRootBitsTree.h"
#include "../../scene/Octree/MaterialLibraryTree.h"
#include "../../scene/Octree/HierarchicalShiftingColoredTree.h"
#include "../../scene/Octree/HierarchicalColorsOnlyTree.h"
#include "../../scene/Octree/MaterialLibraryUniqueIndexTree.h"
#include "../../scene/Octree/UniqueIndexShiftTree.h"
#include "../../scene/Octree/MaterialLibraryMultiRootTree.h"
#include "../../scene/Octree/IMaterialTexture.h"
#include "../../scene/Octree/IBlockTexture.h"
#include "../../scene/PoolBuilder/StandardPoolBuilder.h"
#include "CompressedTextureFactory.h"
#include "PoolBuilderFactory.h"
#include "../../scene/Material/MaterialLibraryPointer.h"
#include "../../scene/Material/SignedIntMaterial.h"
#include "ColorQuantizerFactory.h"
#include "../../PropertyLoader.h"
#include "../Util/Stopwatch.h"
#include "../StringHelper.h"
#include "../MathHelper.h"
bool OctreeLoader::VerifyCache()
{
bool useCache = SettingsParser::GetUseCacheFromSettings();
if (!useCache)
return false;
bool verbose = SettingsParser::GetVerboseFromSettings();
if (verbose) printf("Verifying cache... ");
Stopwatch watch; watch.Reset();
auto octreeType = SettingsParser::GetTreeTypeFromSettings();
unsigned8 maxLevel = SettingsParser::GetMaxLevelFromSettings();
std::string filename = SettingsParser::GetFilenameFromSettings();
bool cacheValid = VerifyCache(octreeType, maxLevel, filename);
if (verbose) printf("Cache is %s, confirmed in %d ms\n", cacheValid ? "valid" : "invalid", (int)(watch.GetTime() * 1000));
return cacheValid;
}
bool OctreeLoader::VerifyCache(std::string treeType, unsigned8 maxLevel, std::string filename)
{
std::string outputFile = GetFullFilename(treeType, maxLevel, filename);
auto tree = CreateTree(treeType, maxLevel);
bool cacheValid = tree->VerifyTree(outputFile.c_str());
delete tree;
return cacheValid;
}
BaseTree* OctreeLoader::ReadCache()
{
bool verbose = SettingsParser::GetVerboseFromSettings();
if (verbose) printf("Reading cache... ");
Stopwatch watch; watch.Reset();
auto octreeType = SettingsParser::GetTreeTypeFromSettings();
unsigned8 maxLevel = SettingsParser::GetMaxLevelFromSettings();
std::string filename = SettingsParser::GetFilenameFromSettings();
auto tree = ReadCache(octreeType, maxLevel, filename);
if (verbose) printf("Read tree with %llu nodes succesfully in %u ms\n", (unsigned64)tree->GetNodeCount(), (unsigned)(watch.GetTime() * 1000));
return tree;
}
BaseTree* OctreeLoader::ReadCache(std::string treeType, unsigned8 maxLevel, std::string filename, bool verbose)
{
Stopwatch watch;
watch.Reset();
std::string outputFile = GetFullFilename(treeType, maxLevel, filename);
if (verbose) printf("Reading file %s.oct.", outputFile.c_str());
auto tree = CreateTree(treeType, maxLevel);
bool readSuccessful = tree->ReadTree(outputFile.c_str());
if (!readSuccessful)
{
delete tree;
if (verbose) printf("Reading .oct file failed.\n");
return NULL;
}
if (verbose) printf("Read .oct file in %u ms.\n", (unsigned)(watch.GetTime() * 1000));
if (verbose) tree->PrintDebugInfo();
return tree;
}
void OctreeLoader::DeleteCache(std::string treeType, unsigned8 maxLevel, std::string filename)
{
try
{
std::remove(Tree<>::GetOctreeFileName(GetFullFilename(treeType, maxLevel, filename).c_str()).c_str());
}
catch (...)
{
printf("Deleting cache file %s failed", GetFullFilename(treeType.c_str(), maxLevel, filename).c_str());
}
}
bool OctreeLoader::WriteCache(BaseTree* tree, std::string treeType, std::string filename, bool verbose)
{
std::string outputFile = GetFullFilename(treeType, tree->GetMaxLevel(), filename);
// Write the final tree to a file
Stopwatch watch; watch.Reset();
if (verbose) printf("Writing DAG to \"%s\"... ", outputFile.c_str());
bool res = tree->WriteTree(outputFile.c_str());
if (verbose) printf("Writing took %d ms\n", (int)(watch.GetTime() * 1000));
return res;
}
bool OctreeLoader::VerifyPool()
{
bool useCache = SettingsParser::GetUsePoolCacheFromSettings();
if (!useCache)
return false;
std::string filename = SettingsParser::GetFilenameFromSettings();
unsigned8 maxLevel = SettingsParser::GetMaxLevelFromSettings();
std::string treeType = SettingsParser::GetTreeTypeFromSettings();
return VerifyPool(treeType, maxLevel, filename);
}
bool OctreeLoader::VerifyPool(std::string treeType, unsigned8 maxLevel, std::string filename)
{
auto fullFileName = GetFullFilename(treeType, maxLevel, filename);
StandardPoolBuilder builder;
return builder.VerifyCachedPool(fullFileName, maxLevel);
}
bool OctreeLoader::BuildNodePool(BaseTree* tree, std::string poolType, std::string poolFullFilename, std::vector<unsigned8>& outPool, unsigned& outPoolSize, bool verbose)
{
Stopwatch watch;
if (verbose) printf("Building node pool (%s)...", PoolBuilderFactory::GetReadableName(poolType).c_str());
BasePoolBuilder<BaseTree>* builder = PoolBuilderFactory::Create(poolType);
watch.Reset();
builder->BuildPool(tree, outPool);
outPoolSize = (unsigned32)MathHelper::DivideRoundUp(outPool.size(), TEXTURESIZE * TEXTURESIZE);
// Try and write the nodepool
builder->WritePool(poolFullFilename, outPool);
delete builder;
tree->WriteAdditionalPool(poolFullFilename.c_str());
//if (outPoolSize < 16) outPoolSize = 16;
outPool.resize(TEXTURESIZE * TEXTURESIZE * outPoolSize);
if (verbose) printf("Nodepool of size %u generated in %u ms.\n", outPoolSize, (unsigned)(watch.GetTime() * 1000));
return true;
}
bool OctreeLoader::ReadNodePool(BaseTree* tree, std::string poolType, std::string poolFileName, std::vector<unsigned8>& outPool, unsigned& outPoolSize, bool verbose)
{
Stopwatch watch;
watch.Reset();
BasePoolBuilder<BaseTree>* builder = PoolBuilderFactory::Create(poolType);
if (verbose) printf("Reading node pool (%s) directly... ", PoolBuilderFactory::GetReadableName(poolType).c_str());
bool success = builder->ReadPool(poolFileName, outPool);
if (success)
{
// if reading succeeded, return the
outPoolSize = (unsigned32)MathHelper::DivideRoundUp(outPool.size(), TEXTURESIZE * TEXTURESIZE);
//if (outPoolSize < 16) outPoolSize = 16;
outPool.resize(TEXTURESIZE * TEXTURESIZE * outPoolSize);
tree->ReadAdditionalPool(poolFileName.c_str());
if (verbose) printf("Reading node pool succeeded in %u ms.\n", (unsigned)(watch.GetTime() * 1000));
} else
if (verbose) printf("Reading directly failed.\n");
delete builder;
return success;
}
bool OctreeLoader::HasMaterialTexture(BaseTree* tree)
{
// If the tree can be cast to an IMaterialTexture interface, it has a material texture
return dynamic_cast<IMaterialTexture*>(tree) != NULL;
}
bool OctreeLoader::GetMaterialTexture(BaseTree* tree, std::string treeType, std::vector<unsigned char>& outMaterialTexture, unsigned& outMaterialTextureSize)
{
if (HasMaterialTexture(tree))
{
auto materialTextureTree = dynamic_cast<IMaterialTexture*>(tree);
outMaterialTexture = materialTextureTree->GetMaterialTexture();
outMaterialTextureSize = materialTextureTree->GetMaterialTextureSize();
return true;
}
return false;
}
bool OctreeLoader::HasBlockTexture(BaseTree* tree)
{
return dynamic_cast<IBlockTexture*>(tree) != NULL;
}
bool OctreeLoader::GetBlockPointerPool(BaseTree* tree, std::string treeType, std::vector<unsigned8>& outBlockPointerPool)
{
if (HasBlockTexture(tree))
{
auto uTree = dynamic_cast<IBlockTexture*>(tree);
outBlockPointerPool = uTree->GetBlockPointerPool();
return true;
}
return false;
}
bool OctreeLoader::GetBlockPool(BaseTree* tree, std::string treeType, std::vector<unsigned8>& outBlockPool)
{
if (HasBlockTexture(tree))
{
auto uTree = dynamic_cast<IBlockTexture*>(tree);
outBlockPool = uTree->GetBlockPool();
return true;
}
return false;
}
bool OctreeLoader::HasAdditionalProperties(BaseTree* tree)
{
return dynamic_cast<IAdditionalProperties*>(tree) != NULL;
}
bool OctreeLoader::GetAdditionalProperties(BaseTree* tree, std::string treeType, std::string poolType, std::map<std::string, std::string>& outAdditionalProperties)
{
if (HasAdditionalProperties(tree))
{
auto uTree = dynamic_cast<IAdditionalProperties*>(tree);
outAdditionalProperties = uTree->GetAdditionalProperties();
}
// Some types have specific additional properties:
auto descr = TreeTypeParser::GetTreeTypeDescriptor(treeType);
std::string material = "MT_NONE";
if (descr.material == "c") material = "MT_COLOR";
else if (descr.material == "n") material = "MT_NORMAL";
else if (descr.material == "cn") material = "MT_COLORNORMAL";
else if (descr.material == "cnr") material = "MT_COLORNORMALREFLECTIVITY";
else if (descr.material == "co") material = "MT_COLOROPACITY";
outAdditionalProperties.insert(std::make_pair("Material", material));
if (descr.material == "n" || descr.material == "cn" || descr.material == "cnr") outAdditionalProperties.insert(std::make_pair("NormalSize", std::to_string(SmallNormal::BITS)));
std::string globalTypeString;
switch (descr.globalType)
{
case MULTIROOT: globalTypeString = "TT_MULTIROOT"; break;
case HIERARCHICAL: globalTypeString = (descr.additionalTypeInfo == "s") ? "TT_HIERARCHICALSHIFT" : "TT_HIERARCHICAL"; break;
case ONLYMATERIAL: globalTypeString = "TT_ONLYMATERIAL"; break;
case UNIQUEINDEX: globalTypeString = (descr.additionalTypeInfo != "" && descr.additionalTypeInfo[0] == 's') ? "TT_UNIQUEINDEXSHIFT" : "TT_UNIQUEINDEX"; break;
case RANDOM: globalTypeString = "TT_RANDOM"; break;
case BITTREES: globalTypeString = "TT_BITTREES"; break;
case STANDARD: default: globalTypeString = "TT_STANDARD"; break;
}
outAdditionalProperties.insert(std::pair<std::string, std::string>("TreeType", globalTypeString));
std::string poolTypeString;
PoolBuilderType poolTypeEnum = PoolBuilderFactory::GetType(poolType);
switch (poolTypeEnum)
{
case ORIGINAL: poolTypeString = "PT_ORIGINAL"; break;
case STANDARDPOOL: poolTypeString = "PT_STANDARD"; break;
case ADAPTIVEDIRECT1: poolTypeString = "PT_ADAPTIVEDIRECT1"; break;
case ADAPTIVELOOKUP1: poolTypeString = "PT_ADAPTIVELOOKUP1"; break;
case ADAPTIVEDIRECT2: poolTypeString = "PT_ADAPTIVEDIRECT2"; break;
case ADAPTIVELOOKUP2: poolTypeString = "PT_ADAPTIVELOOKUP2"; break;
case VIRTUALNODES: poolTypeString = "PT_VIRTUALNODES"; break;
}
outAdditionalProperties.insert(std::make_pair("PoolType", poolTypeString));
if (descr.globalType == UNIQUEINDEX)
{
std::string compressionType = "";
CompressionType cType = CompressedTextureFactory<MaterialLibraryPointer>::GetCompressionType(descr.textureCompressor);
if (cType == BASIC)
compressionType = "BASIC_PACK";
else if (cType == TIGHT)
compressionType = "TIGHT_PACK";
else if (cType == BLOCK)
compressionType = "BLOCK_PACK";
else if (cType == TIGHTBLOCK)
compressionType = "TIGHTBLOCK_PACK";
else if (cType == PALETTEBLOCK)
compressionType = "PALETTE_PACK";
else if (cType == DAGBASED)
compressionType = "DAGBASED_PACK";
else if (cType == MULTIROOTBASED)
compressionType = "MULTIROOTBASED_PACK";
outAdditionalProperties.insert(std::pair<std::string, std::string>("TextureCompression", compressionType));
}
if (descr.globalType == MULTIROOT)
{
outAdditionalProperties.insert(std::pair<std::string, std::string>("BitsPerTree", std::to_string(TreeTypeParser::GetBitsPerTree(descr))));
outAdditionalProperties.insert(std::pair<std::string, std::string>("BitsPerChannel", std::to_string(TreeTypeParser::GetBitsPerChannel(descr))));
}
return true;
}
bool OctreeLoader::GetPool(
std::vector<unsigned8>& outPool, unsigned& outPoolSize,
std::vector<unsigned8>& outMaterialTexture, unsigned& outMaterialTextureSize,
std::vector<unsigned8>& outMaterialPool, unsigned& outMaterialPoolSize,
std::vector<unsigned8>& outBlockPointerPool, std::vector<unsigned8>& outBlockPool,
std::map<std::string, std::string>& outAdditionalProperties)
{
return GetPool(SettingsParser::GetTreeTypeFromSettings(), SettingsParser::GetPoolTypeFromSettings(), SettingsParser::GetMaxLevelFromSettings(), SettingsParser::GetFilenameFromSettings(), SettingsParser::GetUsePoolCacheFromSettings(),
outPool, outPoolSize, outMaterialTexture, outMaterialTextureSize, outMaterialPool, outMaterialPoolSize,
outBlockPointerPool, outBlockPool,
outAdditionalProperties, SettingsParser::GetVerboseFromSettings());
}
bool OctreeLoader::GetPool(std::string treeType, std::string poolType, unsigned8 maxLevel, std::string filename, bool usePoolCache,
std::vector<unsigned8>& outPool, unsigned& outPoolSize,
std::vector<unsigned8>& outMaterialTexture, unsigned& outMaterialTextureSize,
std::vector<unsigned8>& outMaterialPool, unsigned& outMaterialPoolSize,
std::vector<unsigned8>& outBlockPointerPool, std::vector<unsigned8>& outBlockPool,
std::map<std::string, std::string>& outAdditionalProperties,
bool verbose)
{
unsigned globalType = TreeTypeParser::GetGlobalType(treeType);
bool succes = false;
switch (globalType)
{
case HIERARCHICAL: case BITTREES:
succes = OctreeLoader::GetHierarchicalPool(treeType, poolType, maxLevel, filename, usePoolCache, outPool, outPoolSize, outMaterialTexture, outMaterialTextureSize, outAdditionalProperties, verbose); break;
case ONLYMATERIAL:
succes = OctreeLoader::GetOnlyMaterialPool(treeType, poolType, maxLevel, filename, usePoolCache, outPool, outPoolSize, outMaterialPool, outMaterialPoolSize, outMaterialTexture, outMaterialTextureSize, outAdditionalProperties, verbose); break;
case UNIQUEINDEX:
succes = OctreeLoader::GetUniqueIndexPool(treeType, poolType, maxLevel, filename, usePoolCache, outPool, outPoolSize, outMaterialTexture, outMaterialTextureSize, outBlockPointerPool, outBlockPool, outAdditionalProperties, verbose); break;
default:
succes = OctreeLoader::GetStandardPool(treeType, poolType, maxLevel, filename, usePoolCache, outPool, outPoolSize, outAdditionalProperties, verbose); break;
}
return succes;
}
bool OctreeLoader::GetStandardPool(std::vector<unsigned8>& outPool, unsigned& outPoolSize, std::map<std::string, std::string>& outAdditionalProperties)
{
return GetStandardPool(
SettingsParser::GetTreeTypeFromSettings(), SettingsParser::GetPoolTypeFromSettings(), SettingsParser::GetMaxLevelFromSettings(), SettingsParser::GetFilenameFromSettings(), SettingsParser::GetUsePoolCacheFromSettings(),
outPool, outPoolSize, outAdditionalProperties, SettingsParser::GetVerboseFromSettings());
}
bool OctreeLoader::GetStandardPool(std::string treeType, std::string poolType, unsigned8 maxLevel, std::string filename, bool usePoolCache,
std::vector<unsigned8>& outPool, unsigned& outPoolSize, std::map<std::string, std::string>& outAdditionalProperties, bool verbose)
{
unsigned globalType = TreeTypeParser::GetGlobalType(treeType);
// Standard, Colored and Multiroot all use the same type of pool
if (globalType != STANDARD && globalType != MULTIROOT && globalType != RANDOM)
return false;
std::string poolFilename = GetFullFilename(treeType, maxLevel, filename);
if (usePoolCache)
{
// Try and read the pool cache
// Create a root of the correct type to try and read the pool
BaseTree* tree = CreateTree(treeType, maxLevel);
bool readSucces = ReadNodePool(tree, poolType, poolFilename, outPool, outPoolSize, verbose);
if (readSucces) readSucces &= GetAdditionalProperties(tree, treeType, poolType, outAdditionalProperties);
if (readSucces && verbose) PrintGPUMemoryRequirements(outPool.size(), 0, 0, 0);
delete tree;
if (readSucces)
return true;
}
// If cache shouldn't be used or loading failed, load the tree file and build the pool from it
BaseTree* tree = ReadCache(treeType, maxLevel, filename, verbose);
if (tree == NULL)
return false;
bool succes = BuildNodePool(tree, poolType, poolFilename, outPool, outPoolSize, verbose);
if (succes) succes &= GetAdditionalProperties(tree, treeType, poolType, outAdditionalProperties);
if (verbose && succes) PrintGPUMemoryRequirements(treeType, poolType, maxLevel, tree);
delete tree;
return succes;
}
bool OctreeLoader::GetHierarchicalPool(std::vector<unsigned8>& outPool, unsigned& outPoolSize,
std::vector<unsigned char>& outMaterialTexture, unsigned& outMaterialTextureSize,
std::map<std::string, std::string>& outAdditionalProperties)
{
return GetHierarchicalPool(
SettingsParser::GetTreeTypeFromSettings(), SettingsParser::GetPoolTypeFromSettings(), SettingsParser::GetMaxLevelFromSettings(), SettingsParser::GetFilenameFromSettings(), SettingsParser::GetUsePoolCacheFromSettings(),
outPool, outPoolSize, outMaterialTexture, outMaterialTextureSize, outAdditionalProperties, SettingsParser::GetVerboseFromSettings());
}
bool OctreeLoader::GetHierarchicalPool(std::string treeType, std::string poolType, unsigned8 maxLevel, std::string filename, bool usePoolCache,
std::vector<unsigned8>& outPool, unsigned& outPoolSize, std::vector<unsigned char>& outMaterialTexture, unsigned& outMaterialTextureSize,
std::map<std::string, std::string>& outAdditionalProperties,
bool verbose)
{
unsigned globalType = TreeTypeParser::GetGlobalType(treeType);
if (globalType != HIERARCHICAL && globalType != BITTREES)
return false;
std::string poolFilename = GetFullFilename(treeType, maxLevel, filename);
if (usePoolCache)
{
// Try and read the pool cache
// Create a root of the correct type to try and read the pool
BaseTree* tree = CreateTree(treeType, maxLevel);
bool readSucces = ReadNodePool(tree, poolType, poolFilename, outPool, outPoolSize, verbose);
if (readSucces) readSucces &= GetMaterialTexture(tree, treeType, outMaterialTexture, outMaterialTextureSize);
if (readSucces) readSucces &= GetAdditionalProperties(tree, treeType, poolType, outAdditionalProperties);
if (readSucces && verbose) PrintGPUMemoryRequirements(outPool.size(), outMaterialTexture.size(), 0, 0);
delete tree;
if (readSucces)
return true;
}
// If cache shouldn't be used or loading failed, load the tree file and build the pool from it
BaseTree* tree = ReadCache(treeType, maxLevel, filename, verbose);
if (tree == NULL)
return false;
bool succes = BuildNodePool(tree, poolType, poolFilename, outPool, outPoolSize, verbose);
succes &= GetMaterialTexture(tree, treeType, outMaterialTexture, outMaterialTextureSize);
if (succes) succes &= GetAdditionalProperties(tree, treeType, poolType, outAdditionalProperties);
if (verbose) PrintGPUMemoryRequirements(treeType, poolType, maxLevel, tree);
delete tree;
return succes;
}
bool OctreeLoader::GetOnlyMaterialPool(
std::vector<unsigned8>& outGeometryPool, unsigned& outGeometryPoolSize,
std::vector<unsigned8>& outMaterialPool, unsigned& outMaterialPoolSize,
std::vector<unsigned char>& outMaterialTexture, unsigned& outMaterialTextureSize,
std::map<std::string, std::string>& outAdditionalProperties)
{
return GetOnlyMaterialPool(
SettingsParser::GetTreeTypeFromSettings(), SettingsParser::GetPoolTypeFromSettings(), SettingsParser::GetMaxLevelFromSettings(), SettingsParser::GetFilenameFromSettings(), SettingsParser::GetUsePoolCacheFromSettings(),
outGeometryPool, outGeometryPoolSize, outMaterialPool, outMaterialPoolSize, outMaterialTexture, outMaterialTextureSize, outAdditionalProperties, SettingsParser::GetVerboseFromSettings());
}
bool OctreeLoader::GetOnlyMaterialPool(std::string treeType, std::string poolType, unsigned8 maxLevel, std::string filename, bool usePoolCache,
std::vector<unsigned8>& outGeometryPool, unsigned& outGeometryPoolSize,
std::vector<unsigned8>& outMaterialPool, unsigned& outMaterialPoolSize,
std::vector<unsigned char>& outMaterialTexture, unsigned& outMaterialTextureSize,
std::map<std::string, std::string>& outAdditionalProperties,
bool verbose)
{
unsigned globalType = TreeTypeParser::GetGlobalType(treeType);
if (globalType != ONLYMATERIAL)
return false;
std::string poolFilename = GetFullFilename(treeType, maxLevel, filename);
bool geometryReadSucces = false;
bool materialReadSucces = false;
if (usePoolCache)
{
// Try and read the pool cache for both the
// Create a root of the correct type to try and read the pool
BaseTree* geometryTree = CreateTree("s", maxLevel);
geometryReadSucces = ReadNodePool(geometryTree, poolType, GetFullFilename("s", maxLevel, filename), outGeometryPool, outGeometryPoolSize);
delete geometryTree;
BaseTree* materialTree = CreateTree(treeType, maxLevel);
materialReadSucces &= ReadNodePool(materialTree, poolType, poolFilename, outMaterialPool, outMaterialPoolSize, verbose);
if (materialReadSucces)
materialReadSucces &= GetMaterialTexture(materialTree, treeType, outMaterialTexture, outMaterialTextureSize);
if (materialReadSucces) materialReadSucces &= GetAdditionalProperties(materialTree, treeType, poolType, outAdditionalProperties);
delete materialTree;
if (geometryReadSucces && materialReadSucces)
return true;
}
// If cache shouldn't be used or loading failed, load the tree file and build the pool from it
if (!geometryReadSucces)
{
BaseTree* geometryTree = ReadCache("s", maxLevel, filename, verbose);
if (geometryTree == NULL)
return false;
geometryReadSucces = BuildNodePool(geometryTree, poolType, poolFilename, outGeometryPool, outGeometryPoolSize, verbose);
delete geometryTree;
}
if (!materialReadSucces)
{
BaseTree* materialTree = ReadCache(treeType, maxLevel, filename, verbose);
if (materialTree == NULL)
return false;
materialReadSucces = BuildNodePool(materialTree, poolType, poolFilename, outMaterialPool, outMaterialPoolSize, verbose);
materialReadSucces &= GetMaterialTexture(materialTree, treeType, outMaterialTexture, outMaterialTextureSize);
if (materialReadSucces) materialReadSucces &= GetAdditionalProperties(materialTree, treeType, poolType, outAdditionalProperties);
delete materialTree;
}
return materialReadSucces && geometryReadSucces;
}
bool OctreeLoader::GetUniqueIndexPool(
std::vector<unsigned8>& outPool, unsigned& outPoolSize,
std::vector<unsigned8>& outMaterialTexture, unsigned& outMaterialTextureSize,
std::vector<unsigned8>& outBlockPointerPool, std::vector<unsigned8>& outBlockPool,
std::map<std::string, std::string>& outAdditionalProperties)
{
return GetUniqueIndexPool(
SettingsParser::GetTreeTypeFromSettings(), SettingsParser::GetPoolTypeFromSettings(), SettingsParser::GetMaxLevelFromSettings(), SettingsParser::GetFilenameFromSettings(), SettingsParser::GetUsePoolCacheFromSettings(),
outPool, outPoolSize, outMaterialTexture, outMaterialTextureSize, outBlockPointerPool, outBlockPool, outAdditionalProperties, SettingsParser::GetVerboseFromSettings());
}
bool OctreeLoader::GetUniqueIndexPool(std::string treeType, std::string poolType, unsigned8 maxLevel, std::string filename, bool usePoolCache,
std::vector<unsigned8>& outPool, unsigned& outPoolSize,
std::vector<unsigned8>& outMaterialTexture, unsigned& outMaterialTextureSize,
std::vector<unsigned8>& outBlockPointerPool, std::vector<unsigned8>& outBlockPool,
std::map<std::string, std::string>& outAdditionalProperties,
bool verbose)
{
unsigned globalType = TreeTypeParser::GetGlobalType(treeType);
if (globalType != UNIQUEINDEX)
return false;
std::string poolFilename = GetFullFilename(treeType, maxLevel, filename);
if (usePoolCache)
{
// Try and read the pool cache
// Create a root of the correct type to try and read the pool
BaseTree* tree = CreateTree(treeType, maxLevel);
bool readSucces = ReadNodePool(tree, poolType, poolFilename, outPool, outPoolSize, verbose);
if (readSucces) readSucces &= GetMaterialTexture(tree, treeType, outMaterialTexture, outMaterialTextureSize);
if (readSucces) readSucces &= GetBlockPointerPool(tree, treeType, outBlockPointerPool);
if (readSucces) readSucces &= GetBlockPool(tree, treeType, outBlockPool);
if (readSucces) readSucces &= GetAdditionalProperties(tree, treeType, poolType, outAdditionalProperties);
if (readSucces && verbose)
PrintGPUMemoryRequirements(outPool.size(), outMaterialTexture.size(), outBlockPool.size(), outBlockPointerPool.size());
delete tree;
if (readSucces)
return true;
}
// If cache shouldn't be used or loading failed, load the tree file and build the pool from it
BaseTree* tree = ReadCache(treeType, maxLevel, filename, verbose);
if (tree == NULL)
return false;
bool succes = BuildNodePool(tree, poolType, poolFilename, outPool, outPoolSize, verbose);
if (succes) succes &= GetMaterialTexture(tree, treeType, outMaterialTexture, outMaterialTextureSize);
if (succes) succes &= GetBlockPointerPool(tree, treeType, outBlockPointerPool);
if (succes) succes &= GetBlockPool(tree, treeType, outBlockPool);
if (succes) succes &= GetAdditionalProperties(tree, treeType, poolType, outAdditionalProperties);
if (verbose) PrintGPUMemoryRequirements(treeType, poolType, maxLevel, tree);
delete tree;
return succes;
}
size_t OctreeLoader::GetGPUMemoryRequirements()
{
return OctreeLoader::GetGPUMemoryRequirements(SettingsParser::GetTreeTypeFromSettings(), SettingsParser::GetPoolTypeFromSettings(), SettingsParser::GetMaxLevelFromSettings(), SettingsParser::GetFilenameFromSettings());
}
size_t OctreeLoader::GetGPUMemoryRequirements(std::string treeType, std::string poolType, unsigned8 maxLevel, std::string filename)
{
BaseTree* tree = OctreeLoader::ReadCache(treeType, maxLevel, filename);
return GetGPUMemoryRequirements(treeType, poolType, tree);
}
size_t OctreeLoader::GetGPUMemoryRequirements(std::string treeType, std::string poolType, BaseTree* tree)
{
// Every tree has the nodepool.
BasePoolBuilder<BaseTree>* builder = PoolBuilderFactory::Create(poolType);
size_t treePoolSize = builder->GetPoolSize(tree);
delete builder;
size_t res = treePoolSize;
if (HasMaterialTexture(tree))
{
auto materialTree = dynamic_cast<IMaterialTexture*>(tree);
size_t materialTextureSize = materialTree->GetMaterialTextureSize();
res += materialTextureSize * materialTextureSize * materialTree->GetMaterialTextureChannelsPerPixel(); // 2D texture with the given channels per pixel
}
if (HasBlockTexture(tree))
{
auto blockTextureTree = dynamic_cast<IBlockTexture*>(tree);
size_t blockPointerPoolMemoryRequirements = blockTextureTree->GetBlockPointerPoolSize();
size_t blockPoolMemoryRequirements = blockTextureTree->GetBlockPoolSize();
size_t blockTextureMemoryRequirements = blockPointerPoolMemoryRequirements + blockPoolMemoryRequirements;
res += blockTextureMemoryRequirements;
printf("Block texture required %.2f MB. (main: %.2f MB, additional: %.2f MB)\n",
(double)blockTextureMemoryRequirements / (1024.0 * 1024.0),
(double)blockPoolMemoryRequirements / (1024.0 * 1024.0),
(double)blockPointerPoolMemoryRequirements / (1024.0 * 1024.0));
}
return res;
}
size_t OctreeLoader::GetMainTreeGPUMemoryRequirements()
{
std::string treeType = SettingsParser::GetTreeTypeFromSettings();
BaseTree* tree = OctreeLoader::ReadCache(treeType, SettingsParser::GetMaxLevelFromSettings(), SettingsParser::GetFilenameFromSettings());
size_t res = GetGPUMemoryRequirements(treeType, SettingsParser::GetPoolTypeFromSettings(), tree);
return res;
}
size_t OctreeLoader::GetMainTreeGPUMemoryRequirements(std::string treeType, std::string poolType, BaseTree* tree)
{
BasePoolBuilder<BaseTree>* builder = PoolBuilderFactory::Create(poolType);
size_t treePoolSize = builder->GetPoolSize(tree);
delete builder;
return TEXTURESIZE * TEXTURESIZE * MathHelper::DivideRoundUp(treePoolSize, TEXTURESIZE * TEXTURESIZE);
}
void OctreeLoader::PrintGPUMemoryRequirements(std::string treeType, std::string poolType, unsigned8 maxLevel, BaseTree* tree)
{
size_t memoryRequirements = GetGPUMemoryRequirements(treeType, poolType, tree);
printf("Tree requires %11zu bytes (%8.2f MB).\n", memoryRequirements, (double)memoryRequirements / 1048576.0);
}
void OctreeLoader::PrintGPUMemoryRequirements(size_t poolSize, size_t materialTextureSize, size_t blockPoolSize, size_t blockPointerPoolSize)
{
size_t memoryRequirements = poolSize + materialTextureSize + blockPoolSize + blockPointerPoolSize;
printf("Tree requires %11zu bytes (%8.2f MB).\n", memoryRequirements, (double)memoryRequirements / 1048576.0);
}
std::string OctreeLoader::GetFullFilename()
{
PropertyLoader::Create();
auto propertyLoader = PropertyLoader::Instance();
std::string filename = propertyLoader->GetProperty("dag_file");
unsigned8 maxLevel = (unsigned8)std::stoi(propertyLoader->GetProperty("shader_max_level"));
std::string treeType = propertyLoader->GetProperty("octree_type");
return GetFullFilename(treeType, maxLevel, filename);
}
std::string OctreeLoader::GetFullFilename(std::string treeType, unsigned8 maxLevel, std::string filename)
{
// Hack: make sure filenames are consistent for soem special tree types
if (treeType == "m") treeType = "m8";
// Convert compression method to normalized compression method
TreeTypeDescriptor descr = TreeTypeParser::GetTreeTypeDescriptor(treeType);
if (descr.quantizer != "")
{
StringHelper::Replace(treeType, descr.quantizer, ColorQuantizerFactory::GetNormalizedType(descr.quantizer));
}
if (descr.globalType == UNIQUEINDEX && descr.additionalTypeInfo != "")
{
unsigned8 lod = TreeTypeParser::GetLevelsWithoutMaterials(descr);
if (lod != 0)
StringHelper::Replace(treeType, descr.additionalTypeInfo, std::to_string(lod) + "lod");
}
return filename + "_" + std::to_string(maxLevel) + "_" + treeType;
}
BaseTree* OctreeLoader::CreateTree(std::string type, unsigned8 maxLevel)
{
BaseTree* tree = NULL;
TreeTypeDescriptor descr = TreeTypeParser::GetTreeTypeDescriptor(type);
switch (descr.globalType)
{
case HIERARCHICAL:
{
if (descr.material == "c")
{
if (descr.additionalTypeInfo == "s")
tree = new HierarchicalShiftingColoredTree(maxLevel);
else
tree = new MaterialLibraryTree<Color, ColorCompare>(maxLevel);
}
else if (descr.material == "n")
tree = new MaterialLibraryTree<SmallNormal, NormalCompare, SmallNormal::BYTES>(maxLevel);
else if (descr.material == "cn")
tree = new MaterialLibraryTree<ColorAndNormal, ColorAndNormalCompare, ColorAndNormal::CHANNELSPERPIXEL>(maxLevel);
else if (descr.material == "cnr")
tree = new MaterialLibraryTree<ColorAndNormalAndValue, ColorAndNormalAndValueCompare, ColorAndNormalAndValue::CHANNELSPERPIXEL>(maxLevel);
else if (descr.material == "co")
tree = new MaterialLibraryTree<ColorAndOpacity, ColorAndOpacityCompare, ColorAndOpacity::CHANNELSPERPIXEL>(maxLevel);
break;
}
case ONLYMATERIAL:
{
tree = new HierarchicalColorsOnlyTree(maxLevel);
break;
}
case UNIQUEINDEX:
{
unsigned8 levelsWithoutMaterials = TreeTypeParser::GetLevelsWithoutMaterials(descr);
if (descr.additionalTypeInfo != "" && descr.additionalTypeInfo[0] == 's')
{
assert(descr.material != "n");
auto texture = CompressedTextureFactory<SignedIntMaterial>::GetCompressedTexture(descr.textureCompressor);
tree = new UniqueIndexShiftTree<Color, ColorCompare>(maxLevel, texture, levelsWithoutMaterials);
}
else
{
auto texture = CompressedTextureFactory<MaterialLibraryPointer>::GetCompressedTexture(descr.textureCompressor);
if (descr.material == "c")
tree = new MaterialLibraryUniqueIndexTree<Color, ColorCompare>(maxLevel, texture, levelsWithoutMaterials);
else if (descr.material == "n")
tree = new MaterialLibraryUniqueIndexTree<SmallNormal, NormalCompare, SmallNormal::CHANNELSPERPIXEL>(maxLevel, texture, levelsWithoutMaterials);
else if (descr.material == "cn")
tree = new MaterialLibraryUniqueIndexTree<ColorAndNormal, ColorAndNormalCompare, ColorAndNormal::CHANNELSPERPIXEL>(maxLevel, texture, levelsWithoutMaterials);
else if (descr.material == "cnr")
tree = new MaterialLibraryUniqueIndexTree<ColorAndNormalAndValue, ColorAndNormalAndValueCompare, ColorAndNormalAndValue::CHANNELSPERPIXEL>(maxLevel, texture, levelsWithoutMaterials);
else if (descr.material == "co")
tree = new MaterialLibraryUniqueIndexTree<ColorAndOpacity, ColorAndOpacityCompare, ColorAndOpacity::CHANNELSPERPIXEL>(maxLevel, texture, levelsWithoutMaterials);
else
delete texture;
}
break;
}
case MULTIROOT:
{
std::vector<std::string> additionalTypes(8);
additionalTypes[0] = "1";
additionalTypes[1] = "2";
additionalTypes[2] = "3";
additionalTypes[3] = "4";
additionalTypes[4] = "5";
additionalTypes[5] = "6";
additionalTypes[6] = "7";
additionalTypes[7] = "8";
if (descr.additionalTypeInfo == "cc")
{
tree = new LeafMaterialMultiRootTree<ColorChannel>(maxLevel, 3);
}
else if (CollectionHelper::Contains(additionalTypes, descr.additionalTypeInfo))
{
unsigned8 bitsPerChannel = TreeTypeParser::GetBitsPerChannel(descr);
tree = new MultiRootTree<>(maxLevel, 3 * bitsPerChannel);
}
else
{
unsigned8 bitsPerChannel = TreeTypeParser::GetBitsPerChannel(descr);
unsigned8 bitsPerTree = TreeTypeParser::GetBitsPerTree(descr);
unsigned8 treesPerChannel = bitsPerChannel / bitsPerTree + (bitsPerChannel % bitsPerTree == 0 ? 0 : 1);
switch (bitsPerTree)
{
case 1: tree = new LeafMaterialMultiRootTree<BitsMaterial<1>>(maxLevel, 3 * treesPerChannel); break;
case 2: tree = new LeafMaterialMultiRootTree<BitsMaterial<2>>(maxLevel, 3 * treesPerChannel); break;
case 3: tree = new LeafMaterialMultiRootTree<BitsMaterial<3>>(maxLevel, 3 * treesPerChannel); break;
case 4: tree = new LeafMaterialMultiRootTree<BitsMaterial<4>>(maxLevel, 3 * treesPerChannel); break;
case 5: tree = new LeafMaterialMultiRootTree<BitsMaterial<5>>(maxLevel, 3 * treesPerChannel); break;
case 6: tree = new LeafMaterialMultiRootTree<BitsMaterial<6>>(maxLevel, 3 * treesPerChannel); break;
case 7: tree = new LeafMaterialMultiRootTree<BitsMaterial<7>>(maxLevel, 3 * treesPerChannel); break;
case 8: tree = new LeafMaterialMultiRootTree<BitsMaterial<8>>(maxLevel, 3 * treesPerChannel); break;
}
}
break;
}
case RANDOM:
{
tree = new MaterialTree<BitsMaterial<1>, HashComparer<BitsMaterial<1>>>(maxLevel); break;
}
case BITTREES:
{
if (descr.additionalTypeInfo == "l")
tree = new MaterialLibraryMultiRoot<Color, ColorCompare>(maxLevel);
break;
}
default:
{
tree = new Tree<>(maxLevel);
break;
}
}
return tree;
}

View File

@@ -0,0 +1,109 @@
#pragma once
#include "OctreeBuilderStatistics.h"
#include "../Defines.h"
#include "../../inc/glm/vec3.hpp"
#include <map>
#include <string>
class BaseTree;
// Singleton wrapper that reads the settings and creates the correct octreebuilder
class OctreeLoader
{
public:
static const size_t TEXTURESIZE = 1024;
static BaseTree* CreateTree(std::string type, unsigned8 maxLevel);
// Reads the settings and verifies that the current file exists. Returns false if usecache is turned off.
static bool VerifyCache();
// Verify the cache. Returns true if the cache is valid.
static bool VerifyCache(std::string treeType, unsigned8 maxLevel, std::string filename);
static BaseTree* ReadCache();
static BaseTree* ReadCache(std::string treeType, unsigned8 maxLevel, std::string filename, bool verbose = false);
static void DeleteCache(std::string treeType, unsigned8 maxLevel, std::string filename);
static bool WriteCache(BaseTree* tree, std::string treeType, std::string filename, bool verbose);
static bool VerifyPool();
static bool VerifyPool(std::string treeType, unsigned8 maxLevel, std::string filename);
static bool GetPool(
std::vector<unsigned8>& outPool, unsigned& outPoolSize,
std::vector<unsigned8>& outMaterialTexture, unsigned& outMaterialTextureSize,
std::vector<unsigned8>& outMaterialPool, unsigned& outMaterialPoolSize,
std::vector<unsigned8>& outBlockPointerPool, std::vector<unsigned8>& outBlockPool,
std::map<std::string, std::string>& outAdditionalProperties);
static bool GetPool(std::string treeType, std::string poolType, unsigned8 maxLevel, std::string filename, bool usePoolCache,
std::vector<unsigned8>& outPool, unsigned& outPoolSize,
std::vector<unsigned8>& outMaterialTexture, unsigned& outMaterialTextureSize,
std::vector<unsigned8>& outMaterialPool, unsigned& outMaterialPoolSize,
std::vector<unsigned8>& outBlockPointerPool, std::vector<unsigned8>& outBlockPool,
std::map<std::string, std::string>& outAdditionalProperties,
bool verbose = false);
static bool GetStandardPool(
std::vector<unsigned8>& outPool, unsigned& outPoolSize, std::map<std::string, std::string>& outAdditionalProperties);
static bool GetStandardPool(std::string treeType, std::string poolType, unsigned8 maxLevel, std::string filename, bool usePoolCache,
std::vector<unsigned8>& outPool, unsigned& outPoolSize, std::map<std::string, std::string>& outAdditionalProperties, bool verbose = false);
static bool GetHierarchicalPool(
std::vector<unsigned8>& outPool, unsigned& outPoolSize,
std::vector<unsigned8>& outMaterialTexture, unsigned& outMaterialTextureSize,
std::map<std::string, std::string>& outAdditionalProperties);
static bool GetHierarchicalPool(std::string treeType, std::string poolType, unsigned8 maxLevel, std::string filename, bool usePoolCache,
std::vector<unsigned8>& outPool, unsigned& outPoolSize,
std::vector<unsigned8>& outMaterialTexture, unsigned& outMaterialTextureSize,
std::map<std::string, std::string>& outAdditionalProperties,
bool verbose = false);
static bool GetOnlyMaterialPool(
std::vector<unsigned8>& outGeometryPool, unsigned& outGeometryPoolSize,
std::vector<unsigned8>& outMaterialPool, unsigned& outMaterialPoolSize,
std::vector<unsigned8>& outMaterialTexture, unsigned& outMaterialTextureSize,
std::map<std::string, std::string>& outAdditionalProperties);
static bool GetOnlyMaterialPool(std::string treeType, std::string poolType, unsigned8 maxLevel, std::string filename, bool usePoolCache,
std::vector<unsigned8>& outGeometryPool, unsigned& outGeometryPoolSize,
std::vector<unsigned8>& outMaterialPool, unsigned& outMaterialPoolSize,
std::vector<unsigned8>& outMaterialTexture, unsigned& outMaterialTextureSize,
std::map<std::string, std::string>& outAdditionalProperties,
bool verbose = false);
static bool GetUniqueIndexPool(
std::vector<unsigned8>& outPool, unsigned& outPoolSize,
std::vector<unsigned8>& outMaterialTexture, unsigned& outMaterialTextureSize,
std::vector<unsigned8>& outBlockPointerPool, std::vector<unsigned8>& outBlockPool,
std::map<std::string, std::string>& outAdditionalProperties);
static bool GetUniqueIndexPool(std::string treeType, std::string poolType, unsigned8 maxLevel, std::string filename, bool usePoolCache,
std::vector<unsigned8>& outPool, unsigned& outPoolSize,
std::vector<unsigned8>& outMaterialTexture, unsigned& outMaterialTextureSize,
std::vector<unsigned8>& outBlockPointerPool, std::vector<unsigned8>& outBlockPool,
std::map<std::string, std::string>& outAdditionalProperties,
bool verbose = false);
// Returns the GPU memory requirements in bytes for the current settings (returns 0 if no tree is found)
static size_t GetGPUMemoryRequirements();
static size_t GetGPUMemoryRequirements(std::string treeType, std::string poolType, unsigned8 maxLevel, std::string filename);
static size_t GetGPUMemoryRequirements(std::string treeType, std::string poolType, BaseTree* tree);
static size_t GetMainTreeGPUMemoryRequirements();
static size_t GetMainTreeGPUMemoryRequirements(std::string treeType, std::string poolType, BaseTree* tree);
static std::string GetFullFilename();
static std::string GetFullFilename(std::string treeType, unsigned8 maxLevel, std::string filename);
private:
static bool BuildNodePool(BaseTree* tree, std::string poolType, std::string poolFullFilename, std::vector<unsigned8>& outPool, unsigned& outPoolSize, bool verbose = false);
static bool ReadNodePool(BaseTree* tree, std::string poolType, std::string poolFullFilename, std::vector<unsigned8>& outPool, unsigned& outPoolSize, bool verbose = false);
static bool GetMaterialTexture(BaseTree* tree, std::string treeType, std::vector<unsigned8>& outMaterialTexture, unsigned& outMaterialTextureSize);
static bool GetBlockPointerPool(BaseTree* tree, std::string treeType, std::vector<unsigned8>& outBlockPointerPool);
static bool GetBlockPool(BaseTree* tree, std::string treeType, std::vector<unsigned8>& outBlockPool);
static bool GetAdditionalProperties(BaseTree* tree, std::string treeType, std::string poolType, std::map<std::string, std::string>& outAdditionalProperties);
static bool HasMaterialTexture(BaseTree* treeType);
static bool HasBlockTexture(BaseTree* treeType);
static bool HasAdditionalProperties(BaseTree* treeType);
static size_t CeilToTextureSize(size_t size);
static void PrintGPUMemoryRequirements(std::string treeType, std::string poolType, unsigned8 maxLevel, BaseTree* tree);
static void PrintGPUMemoryRequirements(size_t poolSize, size_t materialTextureSize, size_t blockPoolSize, size_t blockPointerPoolSize);
};

View File

@@ -0,0 +1,76 @@
#pragma once
#include "../../scene/PoolBuilder/StandardPoolBuilder.h"
#include "../../scene/PoolBuilder/AdaptivePointerPoolBuilder.h"
#include "../../scene/PoolBuilder/OriginalPoolBuilder.h"
#include "../../scene/PoolBuilder/VirtualNodePoolBuilder.h"
enum PoolBuilderType
{
ORIGINAL, STANDARDPOOL, ADAPTIVELOOKUP1, ADAPTIVEDIRECT1, ADAPTIVELOOKUP2, ADAPTIVEDIRECT2, VIRTUALNODES
};
class PoolBuilderFactory
{
public:
static PoolBuilderType GetType(std::string type)
{
if (type == "o") return ORIGINAL;
if (type == "s") return STANDARDPOOL;
if (type == "al" || type.substr(0, 3) == "al2") return ADAPTIVELOOKUP2;
if (type == "ad" || type.substr(0, 3) == "ad2") return ADAPTIVEDIRECT2;
if (type.substr(0, 3) == "al1") return ADAPTIVELOOKUP1;
if (type.substr(0, 3) == "ad1") return ADAPTIVEDIRECT1;
if (type == "v") return VIRTUALNODES;
return STANDARDPOOL;
}
static BasePoolBuilder<BaseTree>* Create(std::string strType)
{
PoolBuilderType type = GetType(strType);
if (type == ADAPTIVEDIRECT1 || type == ADAPTIVEDIRECT2 || type == ADAPTIVELOOKUP1 || type == ADAPTIVELOOKUP2)
{
unsigned8 maskSize = (type == ADAPTIVEDIRECT1 || type == ADAPTIVELOOKUP1) ? 1 : 2;
bool useLookupTable = type == ADAPTIVELOOKUP1 || type == ADAPTIVELOOKUP2;
std::vector<unsigned8> bitSizes = maskSize == 1 ? std::vector<unsigned8> { 1 } : std::vector<unsigned8>{ 1, 2, 3 };
if (strType.length() > 3)
{
unsigned8 requiredNums = (unsigned8)(BitHelper::Exp2(maskSize) - 1);
std::string bitSizesStr = strType.substr(3, requiredNums);
for (unsigned8 i = 0; i < BitHelper::Exp2(maskSize) - 1; i++)
bitSizes[i] = bitSizesStr[i] - '0';
}
if (maskSize == 1)
return new AdaptivePointerPoolBuilder(useLookupTable, 1, bitSizes[0]);
else
return new AdaptivePointerPoolBuilder(useLookupTable, 2, bitSizes[0], bitSizes[1], bitSizes[2]);
}
switch (type)
{
case ORIGINAL: return new OriginalPoolBuilder(); break;
case VIRTUALNODES: return new VirtualNodePoolBuilder(); break;
case STANDARDPOOL: default: return new StandardPoolBuilder(); break;
}
}
static std::string GetReadableName(std::string strType)
{
PoolBuilderType type = GetType(strType);
switch (type)
{
case ORIGINAL: return "original";
case STANDARDPOOL: return "standard";
case ADAPTIVELOOKUP2: return "adaptive \\w lookup table";
case ADAPTIVEDIRECT2: return "adaptive \\w direct pointers";
case ADAPTIVELOOKUP1: return "adaptive \\w lookup table";
case ADAPTIVEDIRECT1: return "adaptive \\w direct pointers";
case VIRTUALNODES: return "virtual nodes";
default: return "unknown";
}
}
// Returns a string that can be used as a regex to match all compressed texture types
static std::string GetRegexMatcher()
{
return std::string("o|s|ad(1[1-4]?|2([1-4][1-4][1-4])?)|al(1[1-4]?|2([1-4][1-4][1-4])?)|v");
}
};

View File

@@ -0,0 +1,109 @@
#include "RandomOctreeBuilder.h"
#include "../Util/Stopwatch.h"
#include "OctreeLoader.h"
#include "../../inc/tbb/parallel_for.h"
RandomOctreeBuilder::RandomOctreeBuilder() :
BaseMaterialOctreeBuilder(),
mTree(NULL),
mCurPassTree(NULL),
mCurPassTreeCoord(glm::uvec3(0))
{}
RandomOctreeBuilder::~RandomOctreeBuilder() {}
std::string RandomOctreeBuilder::GetTreeType() { return "r"; }
void RandomOctreeBuilder::InitTree()
{
mTree = new MaterialTree<BitsMaterial<1>, HashComparer<BitsMaterial<1>>>(GetTreeDepth());
}
void RandomOctreeBuilder::FinalizeTree()
{
// Convert the octree to a DAG
Stopwatch watch;
if (verbose) printf("Converting final tree to DAG...\n");
watch.Reset();
mTree->ToDAG();
if (verbose) printf("Done in %u ms, %llu nodes left.\n", (unsigned)(watch.GetTime() * 1000), (unsigned64)mTree->GetNodeCount());
}
void RandomOctreeBuilder::TerminateTree()
{
OctreeLoader::WriteCache(mTree, GetTreeType(), GetOutputFile(), verbose);
delete mTree;
}
void RandomOctreeBuilder::InitCurPassTree(glm::uvec3 coord)
{
mCurPassTree = new MaterialTree<Color, ColorCompare>(GetSinglePassTreeDepth());
mCurPassTreeCoord = coord;
}
void RandomOctreeBuilder::FinalizeCurPassTree(glm::uvec3 coord)
{
Stopwatch watch;
unsigned8 mainTreeLevel = mTree->GetMaxLevel() - mCurPassTree->GetMaxLevel();
if (mCurPassTree->GetNodeCount() > 1) // Only append the tree (and compress) if it is not empty
{
//std::vector<BitsMaterial<1>> randomMaterials(mCurPassTree->GetNodeCount());
//tbb::parallel_for(size_t(0), randomMaterials.size(), [&](size_t i) { randomMaterials[i] = BitsMaterial<1>((std::rand() < (RAND_MAX / 2)) ? 1 : 0); });
//mCurPassTree->SetMaterials(randomMaterials);
mCurPassTree->PropagateMaterials([](const std::vector<Color>& materials, const std::vector<float>& weights){ return Color::WeightedAverage(materials, weights); });
std::vector<Color> colors = mCurPassTree->GetMaterials();
std::vector<BitsMaterial<1>> bitMaterials(mCurPassTree->GetNodeCount());
unsigned8 shift = 0;
tbb::parallel_for(size_t(0), bitMaterials.size(), [&](size_t i) { bitMaterials[i] = BitsMaterial<1>((colors[i].GetColor().r & (1 << shift)) >> shift); });
auto mCurPassBitTree = new MaterialTree<BitsMaterial<1>, HashComparer<BitsMaterial<1>>>(mCurPassTree->GetMaxLevel());
mCurPassBitTree->MoveShallow(mCurPassTree);
mCurPassBitTree->SetMaterials(bitMaterials);
delete mCurPassTree;
if (mainTreeLevel == 0) // Means we just constructed the root, so no need to append
{
delete mTree;
mTree = mCurPassBitTree;
}
else
{
//Convert the subtree to a DAG first, this saved time when appending and converting the total tree
//Benchmark (Total build time for subtrees of depth 10, final tree of depth 12, Release mode with pool):
// 209.922 seconds without early converting
// 163.645 seconds with early converting
if (verbose) printf("Converting subtree to DAG...\n");
watch.Reset();
mCurPassBitTree->ToDAG();
if (verbose) printf("Converting took %d ms.\n", (int)(watch.GetTime() * 1000));
if (verbose) printf("Appending subtree... ");
mTree->Append(mCurPassTreeCoord, mainTreeLevel, mCurPassBitTree);
delete mCurPassBitTree;
if (verbose) printf("Converting current tree to DAG...\n");
mTree->ToDAG(mainTreeLevel);
}
}
else
{
delete mCurPassTree;
}
}
void RandomOctreeBuilder::AddNode(const glm::uvec3& coordinate, const Color& color)
{
mCurPassTree->AddLeafNode(coordinate, color);
}
std::vector<size_t> RandomOctreeBuilder::GetOctreeNodesPerLevel()
{
return mTree->GetOctreeNodesPerLevel();
}
std::vector<size_t> RandomOctreeBuilder::GetNodesPerLevel()
{
return mTree->GetNodesPerLevel();
}

View File

@@ -0,0 +1,38 @@
#pragma once
#include "BaseMaterialOctreeBuilder.h"
#include "../../scene/Octree/MaterialTree.h"
#include "../Comparers.h"
#include "../../scene/Material/BitsMaterial.h"
#include "../../scene/Material/Color.h"
class RandomOctreeBuilder : public BaseMaterialOctreeBuilder<Color>
{
public:
RandomOctreeBuilder();
~RandomOctreeBuilder() override;
std::string GetTreeType() override;
protected:
// Initialize the main tree
void InitTree() override;
void FinalizeTree() override;
// Step to finalize the main tree (for example storing it to a file)
void TerminateTree() override;
// Initialize the tree for the current pass.
void InitCurPassTree(glm::uvec3 coord) override;
// Terminate the tree in the current pass. This means it should also be appended to the main tree and deleted
void FinalizeCurPassTree(glm::uvec3 coord) override;
// Should add a node to the current pass tree at the given coordinate and color
void AddNode(const glm::uvec3& coordinate, const Color& color) override;
std::vector<size_t> GetOctreeNodesPerLevel() override;
std::vector<size_t> GetNodesPerLevel() override;
private:
MaterialTree<BitsMaterial<1>, HashComparer<BitsMaterial<1>>>* mTree;
MaterialTree<Color, ColorCompare>* mCurPassTree;
glm::uvec3 mCurPassTreeCoord;
};

View File

@@ -0,0 +1,22 @@
#include "SettingsParser.h"
#include "../../PropertyLoader.h"
PropertyLoader* GetPropertyLoader()
{
PropertyLoader::Create();
return PropertyLoader::Instance();
}
std::string SettingsParser::GetTreeTypeFromSettings() { return GetPropertyLoader()->GetProperty("octree_type"); }
std::string SettingsParser::GetPoolTypeFromSettings() { return GetPropertyLoader()->GetProperty("pool_type"); }
unsigned8 SettingsParser::GetMaxLevelFromSettings() { return (unsigned8)GetPropertyLoader()->GetIntProperty("shader_max_level"); }
std::string SettingsParser::GetFilenameFromSettings() { return GetPropertyLoader()->GetProperty("dag_file"); }
bool SettingsParser::GetUsePoolCacheFromSettings()
{
// Only use the pool cache if the octreebuilder cache is used as well
return GetPropertyLoader()->GetBoolProperty("renderer_usecache") && GetPropertyLoader()->GetBoolProperty("octreebuilder_usecache");
}
bool SettingsParser::GetUseCacheFromSettings() { return GetPropertyLoader()->GetBoolProperty("octreebuilder_usecache"); }
bool SettingsParser::GetForceRebuildFromSettings() { return GetPropertyLoader()->GetBoolProperty("octreebuilder_forcerebuild"); }
bool SettingsParser::GetVerboseFromSettings() { return GetPropertyLoader()->GetBoolProperty("verbose"); }

View File

@@ -0,0 +1,17 @@
#pragma once
#include <string>
#include "../Defines.h"
class SettingsParser
{
public:
static std::string GetTreeTypeFromSettings();
static std::string GetPoolTypeFromSettings();
static unsigned8 GetMaxLevelFromSettings();
static std::string GetFilenameFromSettings();
static bool GetUsePoolCacheFromSettings();
static bool GetUseCacheFromSettings();
static bool GetForceRebuildFromSettings();
static bool GetVerboseFromSettings();
};

View File

@@ -0,0 +1,122 @@
#include "StandardOctreeBuilder.h"
#include "../Util/Stopwatch.h"
#include "OctreeLoader.h"
#define DAGFINALTREE
StandardOctreeBuilder::StandardOctreeBuilder() :
BaseStandardOctreeBuilder(),
mTree(NULL)
{}
StandardOctreeBuilder::~StandardOctreeBuilder() {}
std::string StandardOctreeBuilder::GetTreeType() { return "s"; }
void StandardOctreeBuilder::FinalizeTree()
{
// Read all subtrees from cache and append them
if (!IsSinglePass())
{
Stopwatch watch;
mTree = new Tree<>(GetTreeDepth());
int i = 1;
for (const glm::uvec3& coord : GetValidCoords())
{
if (verbose) printf("Reading subtree %u / %u at (%u, %u, %u) from cache...\n", i, (unsigned32)(GetValidCoords().size()), coord.x, coord.y, coord.z);
Tree<>* subTree = (Tree<>*)OctreeLoader::ReadCache(GetTreeType(), GetSinglePassTreeDepth(), GetSinglePassTreeFilename(coord), verbose);
if (subTree != NULL)
{
if (verbose) printf("Appending subtree... ");
watch.Reset();
mTree->AppendAndMerge(coord, GetAppendedTreeLevel(), subTree);
//mTree->Append(mCurPassTreeCoord, mainTreeLevel, mCurPassTree);
delete subTree;
if (verbose) printf("Appended subtree in %d ms.\n", (int)(watch.GetTime() * 1000));
}
//mTree->SortNodes();
//#ifdef DAGFINALTREE
// if (verbose) printf("Converting current tree to DAG...\n");
// mTree->ToDAG(mainTreeLevel);
//#endif
i++;
}
// Delete the cache files
if (verbose) printf("Deleting cache...");
watch.Reset();
for (const glm::uvec3& coord : GetValidCoords())
OctreeLoader::DeleteCache(GetTreeType(), GetSinglePassTreeDepth(), GetSinglePassTreeFilename(coord));
if (verbose) printf("Cache deleted in %d ms\n", (int)(watch.GetTime() * 1000));
}
}
void StandardOctreeBuilder::TerminateTree()
{
OctreeLoader::WriteCache(mTree, GetTreeType(), GetOutputFile(), GetVerbose());
delete mTree;
}
bool StandardOctreeBuilder::CancelCurPassTree(const glm::uvec3& coord)
{
return OctreeLoader::VerifyCache(GetTreeType(), GetSinglePassTreeDepth(), GetSinglePassTreeFilename(coord));
}
void StandardOctreeBuilder::InitCurPassTree(glm::uvec3 coord)
{
mTree = new Tree<>(GetSinglePassTreeDepth());
}
void StandardOctreeBuilder::FinalizeCurPassTree(glm::uvec3 coord)
{
Stopwatch watch;
if (!mTree->IsEmpty()) // Only append the tree (and compress) if it is not empty
{
// Convert the subtree to a DAG first, this saved time when appending and converting the total tree.
// If it is a single pass tree it needs to be converted anyway :)
// Benchmark (Total build time for subtrees of depth 10, final tree of depth 12, run in Release mode with pool):
// 209.922 seconds without early converting
// 163.645 seconds with early converting
if (verbose) printf("Converting %s to DAG...\n", IsSinglePass() ? "tree" : "subtree");
watch.Reset();
//mCurPassTree->SortNodes();
mTree->ToDAG();
if (verbose) printf("Converting took %d ms.\n", (int)(watch.GetTime() * 1000));
if (!IsSinglePass())
{
if (verbose) printf("Writing subtree to cache...");
watch.Reset();
OctreeLoader::WriteCache(mTree, GetTreeType(), GetSinglePassTreeFilename(coord), verbose);
if (verbose) printf("Wrote cache in %d ms.\n", (int)(watch.GetTime() * 1000));
delete mTree;
mTree = NULL;
}
}
}
void StandardOctreeBuilder::AddNode(const glm::uvec3& coordinate)
{
mTree->AddLeafNode(coordinate);
}
std::vector<size_t> StandardOctreeBuilder::GetOctreeNodesPerLevel()
{
return mTree->GetOctreeNodesPerLevel();
}
std::vector<size_t> StandardOctreeBuilder::GetNodesPerLevel()
{
return mTree->GetNodesPerLevel();
}
std::string StandardOctreeBuilder::GetSinglePassTreeFilename(const glm::uvec3& coord) const
{
char buffer[255];
sprintf(buffer, "%s_%u_(%u_%u_%u)", GetOutputFile().c_str(), GetTreeDepth(), coord.x, coord.y, coord.z);
return std::string(buffer);
}

View File

@@ -0,0 +1,35 @@
#pragma once
#include "BaseStandardOctreeBuilder.h"
#include "../../scene/Octree/Tree.h"
class StandardOctreeBuilder : public BaseStandardOctreeBuilder
{
public:
StandardOctreeBuilder();
~StandardOctreeBuilder() override;
std::string GetTreeType() override;
protected:
void InitTree() override {}
// Finalize the tree
void FinalizeTree() override;
// Step to finalize the main tree (for example storing it to a file)
void TerminateTree() override;
// Cancel the cur pass tree if there's already a cache file available for it
bool CancelCurPassTree(const glm::uvec3& coord) override;
// Initialize the tree for the current pass.
void InitCurPassTree(glm::uvec3 coord) override;
// Terminate the tree in the current pass. This means it should also be appended to the main tree and deleted
void FinalizeCurPassTree(glm::uvec3 coord) override;
// Should add a node to the current pass tree at the given coordinate and color
void AddNode(const glm::uvec3& coordinate) override;
std::vector<size_t> GetOctreeNodesPerLevel() override;
std::vector<size_t> GetNodesPerLevel() override;
private:
std::string GetSinglePassTreeFilename(const glm::uvec3& coord) const;
Tree<>* mTree;
};

View File

@@ -0,0 +1,137 @@
#include "TreeTypeParser.h"
#include "ColorQuantizerFactory.h"
#include "NormalQuantizerFactory.h"
#include "CompressedTextureFactory.h"
#include <regex>
TreeType TreeTypeParser::GetGlobalType(std::string type)
{
char globalTypeChar = type[0];
switch (globalTypeChar)
{
case 'm': return MULTIROOT;
case 'h': return HIERARCHICAL;
case 'o': return ONLYMATERIAL;
case 'u': return UNIQUEINDEX;
case 'r': return RANDOM;
case 'b': return BITTREES;
default: return STANDARD;
}
}
TreeTypeDescriptor TreeTypeParser::GetTreeTypeDescriptor(std::string type)
{
TreeTypeDescriptor res;
std::smatch sm;
res.globalType = GetGlobalType(type);
switch (res.globalType)
{
case ONLYMATERIAL:
{
std::string regexStr = "(o)(c)(" + ColorQuantizerFactory::GetRegexMatcher() + ")?";
std::regex normalMatcher(regexStr);
if (std::regex_match(type, sm, normalMatcher))
{
res.material = sm.str(2);
res.quantizer = sm.str(3);
}
break;
}
case HIERARCHICAL:
{
std::string regexStr = "(h(s?))(c|n|cn|cnr|co)(" + NormalQuantizerFactory::GetRegexMatcher() + "|" + ColorQuantizerFactory::GetRegexMatcher() + ")?";
std::regex normalMatcher(regexStr);
if (std::regex_match(type, sm, normalMatcher))
{
res.additionalTypeInfo = sm.str(2);
res.material = sm.str(3);
res.quantizer = sm.str(4);
}
break;
}
case UNIQUEINDEX:
{
std::string regexStr = "u(s?([0-9]+lod)?)?(" + CompressedTextureFactory<MaterialLibraryPointer>::GetRegexMatcher() + ")?(c|n|cn|cnr|co)(" + NormalQuantizerFactory::GetRegexMatcher() + "|" + ColorQuantizerFactory::GetRegexMatcher() + ")?";
std::regex uniqueIndexMatcher(regexStr);
if (std::regex_match(type, sm, uniqueIndexMatcher))
{
res.additionalTypeInfo = sm.str(1);
res.textureCompressor = sm.str(3);
res.material = sm.str(4);
res.quantizer = sm.str(5);
}
break;
}
case MULTIROOT:
{
std::string regexStr = "m(cc|(([1-8]?)(b[1-8])?))";
std::regex multiRootMatcher(regexStr);
if (std::regex_match(type, sm, multiRootMatcher))
{
res.additionalTypeInfo = sm.str(1);
res.material = "c";
}
break;
}
case BITTREES:
{
std::string regexStr = "blc(" + ColorQuantizerFactory::GetRegexMatcher() + ")?";
std::regex bittreesMatcher(regexStr);
if (std::regex_match(type, sm, bittreesMatcher))
{
res.additionalTypeInfo = "l";
res.material = "c";
res.quantizer = sm.str(1);
}
break;
}
}
return res;
}
unsigned8 TreeTypeParser::GetBitsPerTree(const TreeTypeDescriptor& type)
{
if (type.globalType != MULTIROOT) return 0;
if (type.additionalTypeInfo == "")
// Standard multiroot, one bit per channel
return 1;
if (type.additionalTypeInfo == "cc")
{ // Color Channel per root
return GetBitsPerChannel(type);
}
std::regex multiRootBitMatcher("[0-8]?b([1-8])");
std::smatch sm;
if (std::regex_match(type.additionalTypeInfo, sm, multiRootBitMatcher))
{ // MultiBitMultiRoot tree
return std::stoi(sm.str(1));
}
return 1;
}
unsigned8 TreeTypeParser::GetBitsPerChannel(const TreeTypeDescriptor& type)
{
if (type.globalType != MULTIROOT) return 0;
std::regex multiRootMatcher("([0-8])(b[1-8])?");
std::smatch sm;
if (std::regex_match(type.additionalTypeInfo, sm, multiRootMatcher))
{
return std::stoi(sm.str(1));
}
else
{
return 8;
}
}
unsigned8 TreeTypeParser::GetLevelsWithoutMaterials(const TreeTypeDescriptor& type)
{
if (type.globalType != UNIQUEINDEX) return 0;
std::regex lodMatcher("s?([0-9]+)lod");
std::smatch sm;
unsigned8 lod = 0;
if (std::regex_match(type.additionalTypeInfo, sm, lodMatcher))
lod = std::stoi(sm.str(1));
return lod;
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include "../Defines.h"
#include <string>
struct TreeTypeDescriptor
{
TreeType globalType;
std::string additionalTypeInfo;
std::string textureCompressor;
std::string material;
std::string quantizer;
};
class TreeTypeParser
{
public:
static TreeType GetGlobalType(std::string type);
static TreeTypeDescriptor GetTreeTypeDescriptor(std::string type);
static unsigned8 GetBitsPerTree(const TreeTypeDescriptor& type);
static unsigned8 GetBitsPerChannel(const TreeTypeDescriptor& type);
static unsigned8 GetLevelsWithoutMaterials(const TreeTypeDescriptor& type);
};

View File

@@ -0,0 +1,364 @@
#pragma once
#include "BaseMaterialOctreeBuilder.h"
#include <unordered_set>
#include "../../inc/glm/common.hpp"
#include "../../core/Hashers.h"
#include "../../scene/Material/MaterialLibraryPointer.h"
#include "../../scene/Octree/MaterialLibraryUniqueIndexTree.h"
#include "../../scene/Octree/MaterialTree.h"
template<typename T, typename Comparer>
class UniqueIndexMaterialOctreeBuilder : public BaseMaterialOctreeBuilder<T>
{
private:
static std::string GetSubtreeTextureCompressionType() { return "b"; } // Basic texture compression (e.g. no texture compression)
typedef MaterialLibraryUniqueIndexTree<T, Comparer, T::CHANNELSPERPIXEL> FinalTreeType;
typedef MaterialTree<T, Comparer> IntermediateTreeType;
public:
UniqueIndexMaterialOctreeBuilder(std::string textureCompressionType, BaseQuantizer<T, Comparer>* quantizer = NULL, unsigned32 levelsWithoutMaterials = 0) :
BaseMaterialOctreeBuilder(),
mTree(NULL),
mReduceMaterials(quantizer != NULL),
mTextureCompressionType(textureCompressionType),
mLevelsWithoutMaterials(levelsWithoutMaterials),
mSceneMaterials(std::vector<T>()),
mMaterialReplacers(std::unordered_map<T, T>()),
mCurPreprocessPassMaterials(std::vector<T>()),
mMainTreeMaterials(std::vector<std::pair<glm::uvec3, T>>()),
mIntermediateTree(NULL),
mQuantizer(quantizer)
{}
UniqueIndexMaterialOctreeBuilder(std::string textureCompressionType, unsigned32 levelsWithoutMaterials = 0) :
UniqueIndexMaterialOctreeBuilder(textureCompressionType, NULL, levelsWithoutMaterials)
{}
~UniqueIndexMaterialOctreeBuilder() override
{
if (mIntermediateTree != NULL) delete mIntermediateTree;
if (mTree != NULL) delete mTree;
}
std::string GetTreeType() override
{
return "u" +
(mLevelsWithoutMaterials == 0 ? "" : (std::to_string(mLevelsWithoutMaterials) + "lod"))
+ mTextureCompressionType + MaterialAbbreviation<T>()() +
(mReduceMaterials ? mQuantizer->GetQuantizerDescriptor() : "");
}
std::string GetSubtreeType()
{
return "u" +
(mLevelsWithoutMaterials == 0 ? "" : (std::to_string(mLevelsWithoutMaterials) + "lod"))
+ GetSubtreeTextureCompressionType() + MaterialAbbreviation<T>()() +
(mReduceMaterials ? mQuantizer->GetQuantizerDescriptor() : "");
}
protected:
bool SubTreeCompare(const glm::uvec3& coord1, const glm::uvec3& coord2) const override
{
// Sort them so that the trees with the lowest index will be processed first.
// Since indexes are given in a depth-first order, and the lowest indexes are given to the trees with the highest ChildIndex in each level,
for (unsigned8 bit = GetAppendedTreeLevel(); bit > 0; bit--)
{
unsigned mask = 1 << (bit - 1);
if ((coord1.z & mask) != (coord2.z & mask))
return (coord1.z & mask) > (coord2.z & mask);
if ((coord1.y & mask) != (coord2.y & mask))
return (coord1.y & mask) > (coord2.y & mask);
if ((coord1.x & mask) != (coord2.x & mask))
return (coord1.x & mask) > (coord2.x & mask);
}
return true;
}
// Initialize the main tree
void InitTree() override
{
auto texture = CompressedTextureFactory<MaterialLibraryPointer>::GetCompressedTexture(mTextureCompressionType);
mTree = new FinalTreeType(GetTreeDepth(), texture, mLevelsWithoutMaterials);
IntermediateTreeType* tempTree = new IntermediateTreeType(GetTreeDepth());
if (!IsSinglePass())
{
for (auto coordMaterial : mMainTreeMaterials)
{
tempTree->SetMaterial(coordMaterial.first, GetAppendedTreeLevel(), coordMaterial.second);
}
tempTree->PropagateMaterials(T::WeightedAverage);
mTree->BaseOn(tempTree);
}
mFirstPass = true;
}
bool UsePreprocessing() const override { return !IsSinglePass(); }
void InitPreprocessing() override
{
mSceneMaterials.clear();
mMaterialReplacers.clear();
mMainTreeMaterials.clear();
}
void InitCurPreprocessPass(glm::uvec3 coordinate) override
{
mCurPreprocessPassMaterials.clear();
}
void PreProcessNode(const glm::uvec3& coordinate, const T& color) override { mCurPreprocessPassMaterials.push_back(color); }
void FinalizeCurPreprocessPass(glm::uvec3 coord) override
{
if (mCurPreprocessPassMaterials.empty())
return;
tbb::parallel_sort(mCurPreprocessPassMaterials, Comparer());
std::vector<T> uniqueMaterials;
std::vector<float> uniqueMaterialWeights;
unsigned curMaterialCount = 0;
T lastSeenMaterial = mCurPreprocessPassMaterials[0];
for (auto color : mCurPreprocessPassMaterials)
{
if (!(lastSeenMaterial == color))
{
uniqueMaterials.push_back(lastSeenMaterial);
uniqueMaterialWeights.push_back((float)curMaterialCount);
lastSeenMaterial = color;
curMaterialCount = 0;
}
curMaterialCount++;
}
T avgMaterial = T::WeightedAverage(uniqueMaterials, uniqueMaterialWeights);
mMainTreeMaterials.push_back(std::pair<glm::uvec3, T>(coord, avgMaterial));
// Append the unique colors to the scene colors and compress them to keep the memory usage acceptable
mSceneMaterials.insert(mSceneMaterials.end(), uniqueMaterials.begin(), uniqueMaterials.end());
tbb::parallel_sort(mSceneMaterials, Comparer());
mSceneMaterials.erase(std::unique(mSceneMaterials.begin(), mSceneMaterials.end()), mSceneMaterials.end());
}
void QuantizeSceneMaterials()
{
if (mQuantizer == NULL || !mReduceMaterials) return;
if (verbose) printf("Quantizing/merging %llu %s...", (unsigned64)mSceneMaterials.size(), MaterialName<T>()());
Stopwatch watch; watch.Reset();
auto quantizedSceneMaterials = mQuantizer->QuantizeMaterials(mSceneMaterials);
// Replace the list of scene colors with the quantized scene colors
mSceneMaterials.clear();
for (auto color : *quantizedSceneMaterials)
mSceneMaterials.push_back(color.second);
tbb::parallel_sort(mSceneMaterials, Comparer());
mSceneMaterials.erase(std::unique(mSceneMaterials.begin(), mSceneMaterials.end()), mSceneMaterials.end());
mSceneMaterials.shrink_to_fit();
// Build a dictionary for quick lookup of original scene colors and their quantized counterparts
mMaterialReplacers = std::unordered_map<T, T>();
for (auto color : *quantizedSceneMaterials)
mMaterialReplacers.insert(std::make_pair(color.first, color.second));
// Clear the cur preprocessMaterials (to free up memory during tree construction)
mCurPreprocessPassMaterials = std::vector<T>();
delete quantizedSceneMaterials;
// Replace the old colors by the new ones
if (verbose) printf("Quantized %s in %d ms\n", MaterialName<T>()(), (int)(watch.GetTime() * 1000));
}
void FinalizeTree() override
{
Stopwatch watch;
if (!IsSinglePass())
{
unsigned32 i = 1;
for (const glm::uvec3& coord : GetValidCoords())
{
{ // Scope subTree variable
if (verbose) printf("Reading subtree %u / %u at (%u, %u, %u) from cache...\n", i, (unsigned32)(GetValidCoords().size()), coord.x, coord.y, coord.z);
FinalTreeType* subTree = (FinalTreeType*)OctreeLoader::ReadCache(GetSubtreeType(), GetSinglePassTreeDepth(), GetSinglePassTreeFilename(coord), verbose);
if (subTree != NULL)
{
// Append the subtree to the main tree
if (verbose) printf("Appending subtree... ");;
watch.Reset();
mTree->Append(coord, GetAppendedTreeLevel(), subTree);
delete subTree;
if (verbose) printf("Appending took %d ms.\n", (int)(watch.GetTime() * 1000));
}
}
// Convert the new part of the main tree to a DAG
if (verbose) printf("Converting current tree to DAG...\n");
watch.Reset();
mTree->ToDAG(GetAppendedTreeLevel());
if (verbose) printf("Converting took %d ms.\n", (int)(watch.GetTime() * 1000));
i++;
}
}
// Generate the material texture
watch.Reset();
if (verbose) printf("Generating material texture... ");
mTree->GetMaterialTexture();
if (verbose) printf("Material texture generated in %d ms\n", (int)(watch.GetTime() * 1000));
// Delete the cache files
if (!IsSinglePass())
{
if (verbose) printf("Deleting cache...");
watch.Reset();
for (const glm::uvec3& coord : GetValidCoords())
OctreeLoader::DeleteCache(GetSubtreeType(), GetSinglePassTreeDepth(), GetSinglePassTreeFilename(coord));
if (verbose) printf("Cache deleted in %d ms\n", (int)(watch.GetTime() * 1000));
}
}
// Step to finalize the main tree (for example storing it to a file)
void TerminateTree() override
{
OctreeLoader::WriteCache(mTree, GetTreeType(), GetOutputFile(), verbose);
delete mTree;
mTree = NULL;
mSceneMaterials.clear();
mMaterialReplacers.clear();
mMainTreeMaterials.clear();
}
// Don't build this subtree again if a cache file exists for it
bool CancelCurPassTree(const glm::uvec3& coord) override
{
return OctreeLoader::VerifyCache(GetSubtreeType(), GetSinglePassTreeDepth(), GetSinglePassTreeFilename(coord));
}
// Initialize the tree for the current pass.
void InitCurPassTree(glm::uvec3 coord) override
{
mIntermediateTree = new IntermediateTreeType(GetSinglePassTreeDepth());
mIntermediateTree->UseLeafMap(false);
}
// Terminate the tree in the current pass. This means it should also be appended to the main tree and deleted
void FinalizeCurPassTree(glm::uvec3 coord) override
{
Stopwatch watch;
if (mIntermediateTree->GetNodeCount() > 1) // Only append the tree (and compress) if it is not empty
{
mIntermediateTree->ToDAG();
// Propagate the materials
watch.Reset();
if (verbose) printf("Propagating materials in subtree... ");
mIntermediateTree->PropagateMaterials(T::WeightedAverage);
if (verbose) printf("Materials propagated in %d ms.\n", (int)(watch.GetTime() * 1000));
if (mFirstPass)
{
std::vector<T> uniqueMaterials = mIntermediateTree->GetUniqueMaterials();
mSceneMaterials.insert(mSceneMaterials.end(), uniqueMaterials.begin(), uniqueMaterials.end());
QuantizeSceneMaterials();
mFirstPass = false;
}
// Replace the materials by their quantized counterparts
if (mQuantizer != NULL)
{
if (verbose) printf("Replacing materials by their quantized counterparts.");
watch.Reset();
std::vector<T> curPassMaterials = mIntermediateTree->GetMaterials();
std::vector<T> curPassQuantizedMaterials(curPassMaterials.size());
auto quickQuantizer = dynamic_cast<QuickQuantizer<T>*>(mQuantizer);
if (verbose) printf(".");
// For each material in the curPassMaterials, find the closest material
tbb::parallel_for(size_t(0), curPassMaterials.size(), [&](size_t i)
{
auto cachedReplacer = mMaterialReplacers.find(curPassMaterials[i]);
if (cachedReplacer != mMaterialReplacers.end())
curPassQuantizedMaterials[i] = cachedReplacer->second;
else
{
// If we can use the quick quantizer, try it
if (quickQuantizer != NULL)
{
auto quickQuantizedValue = quickQuantizer->Quantize(curPassMaterials[i]);
auto quickQuantizedCachedReplacer = mMaterialReplacers.find(quickQuantizedValue);
if (quickQuantizedCachedReplacer != mMaterialReplacers.end())
curPassQuantizedMaterials[i] = quickQuantizedCachedReplacer->second;
return;
}
curPassQuantizedMaterials[i] = NearestFinder<T>()(curPassMaterials[i], mSceneMaterials);
}
});
// Update the current scene color map
std::unordered_map<T, T> quantizedMaterials;
for (size_t i = 0; i < curPassMaterials.size(); i++)
quantizedMaterials.insert(std::make_pair(curPassMaterials[i], curPassQuantizedMaterials[i]));
if (verbose) printf(".");
mIntermediateTree->ReplaceMaterials(quantizedMaterials);
if (verbose) printf("Replaced in %d ms.\n", (int)(watch.GetTime() * 1000));
}
// Create the UniqueIndexTree for this current pass
std::string subtreeCompressionType = mTextureCompressionType;
if (!IsSinglePass())
subtreeCompressionType = GetSubtreeTextureCompressionType();
auto texture = CompressedTextureFactory<MaterialLibraryPointer>::GetCompressedTexture(subtreeCompressionType);
auto curPassTree = new FinalTreeType(GetSinglePassTreeDepth(), texture, mLevelsWithoutMaterials);
// Finalize the current pass tree
if (verbose) printf("Finalizing subtree...");
watch.Reset();
curPassTree->BaseOn(mIntermediateTree); // Note that BaseOn will delete the intermediate tree, no need to do that manually
mIntermediateTree = NULL;
if (verbose) printf("Finalized in %d ms.\n", (int)(watch.GetTime() * 1000));
// Convert the subtree to a DAG first, this saved time when appending and converting the total tree
if (verbose) printf("Converting subtree to DAG...\n");
watch.Reset();
curPassTree->ToDAG();
if (verbose) printf("Converting took %d ms.\n", (int)(watch.GetTime() * 1000));
if (IsSinglePass()) // Means we just constructed the root, so no need to append
{
delete mTree;
mTree = curPassTree;
}
else
{
OctreeLoader::WriteCache(curPassTree, GetSubtreeType(), GetSinglePassTreeFilename(coord), verbose);
delete curPassTree;
}
}
else
{
delete mIntermediateTree;
}
}
// Should add a node to the current pass tree at the given coordinate and color
void AddNode(const glm::uvec3& coordinate, const T& color) override { mIntermediateTree->AddLeafNode(coordinate, color); }
void AddMissingNode(const glm::uvec3& coordinate, const T& color) override { if (!mIntermediateTree->HasLeaf(coordinate)) AddNode(coordinate, color); }
std::vector<size_t> GetOctreeNodesPerLevel() override { return mTree->GetOctreeNodesPerLevel(); }
std::vector<size_t> GetNodesPerLevel() override { return mTree->GetNodesPerLevel(); }
private:
std::string GetSinglePassTreeFilename(glm::uvec3 coord)
{
char buffer[255];
sprintf(buffer, "%s_%u_(%u_%u_%u)", GetOutputFile().c_str(), GetTreeDepth(), coord.x, coord.y, coord.z);
return std::string(buffer);
}
FinalTreeType* mTree;
bool mReduceMaterials;
bool mFirstPass;
std::string mTextureCompressionType;
unsigned32 mLevelsWithoutMaterials;
std::vector<T> mSceneMaterials;
std::unordered_map<T, T> mMaterialReplacers;
std::vector<T> mCurPreprocessPassMaterials;
std::vector<std::pair<glm::uvec3, T>> mMainTreeMaterials;
IntermediateTreeType* mIntermediateTree;
BaseQuantizer<T, Comparer>* mQuantizer;
};

View File

@@ -0,0 +1,330 @@
#include "UniqueIndexShiftColoredOctreeBuilder.h"
#include <unordered_map>
#include "OctreeLoader.h"
#include "CompressedTextureFactory.h"
#include "../Hashers.h"
#include "../Comparers.h"
#include "../Util/Stopwatch.h"
#include "../../scene/Octree/MaterialTree.h"
#include "../../scene/Material/Color.h"
#include "../../scene/Material/SignedIntMaterial.h"
#include "../../scene/Material/MaterialQuantizer/ColorQuantizer/BaseColorQuantizer.h"
#include "../../inc/tbb/parallel_sort.h"
#include "../../inc/tbb/concurrent_queue.h"
UniqueIndexShiftColoredOctreeBuilder::UniqueIndexShiftColoredOctreeBuilder(std::string textureCompressionType, BaseQuantizer<Color, ColorCompare>* quantizer, unsigned32 levelsWithoutMaterials) :
BaseMaterialOctreeBuilder(),
mTree(NULL),
mReduceColors(quantizer != NULL),
mTextureCompressionType(textureCompressionType),
mLevelsWithoutMaterials(levelsWithoutMaterials),
mSceneColors(std::vector<Color>()),
mColorReplacers(std::unordered_map<Color, Color>()),
mCurPreprocessPassColors(std::vector<Color>()),
mMainTreeColors(std::vector<std::pair<glm::uvec3, Color>>()),
mIntermediateTree(NULL),
mQuantizer(quantizer)
{}
UniqueIndexShiftColoredOctreeBuilder::~UniqueIndexShiftColoredOctreeBuilder() {}
std::string UniqueIndexShiftColoredOctreeBuilder::GetTreeType() {
return "us" +
(mLevelsWithoutMaterials == 0 ? "" : (std::to_string(mLevelsWithoutMaterials) + "lod"))
+ mTextureCompressionType + "c" +
(mReduceColors ? mQuantizer->GetQuantizerDescriptor() : "");
}
bool UniqueIndexShiftColoredOctreeBuilder::UsePreprocessing() const
{
return !IsSinglePass() || mReduceColors;
}
bool UniqueIndexShiftColoredOctreeBuilder::SubTreeCompare(const glm::uvec3& coord1, const glm::uvec3& coord2) const
{
// Sort them so that the trees with the lowest index will be processed first.
// Since indexes are given in a depth-first order, and the lowest indexes are given to the trees with the highest ChildIndex in each level,
for (unsigned8 bit = GetTreeDepth() - GetSinglePassTreeDepth(); bit > 0; bit--)
{
unsigned mask = 1 << (bit - 1);
if ((coord1.z & mask) != (coord2.z & mask))
return (coord1.z & mask) > (coord2.z & mask);
if ((coord1.y & mask) != (coord2.y & mask))
return (coord1.y & mask) > (coord2.y & mask);
if ((coord1.x & mask) != (coord2.x & mask))
return (coord1.x & mask) > (coord2.x & mask);
}
return true;
}
void UniqueIndexShiftColoredOctreeBuilder::InitTree()
{
auto texture = CompressedTextureFactory<SignedIntMaterial>::GetCompressedTexture(mTextureCompressionType);
mTree = new UniqueIndexShiftTree<Color, ColorCompare>(GetTreeDepth(), texture, mLevelsWithoutMaterials);
if (!IsSinglePass())
{
MaterialTree<Color, ColorCompare>* tempTree = new MaterialTree<Color, ColorCompare>(GetTreeDepth());
mTree->PrepareForMaterials(mSceneColors);
for (auto coordColor : mMainTreeColors)
{
tempTree->SetMaterial(coordColor.first, GetAppendedTreeLevel(), coordColor.second);
}
tempTree->PropagateMaterials(Color::WeightedAverage);
ReplaceByQuantized(tempTree);
mTree->BaseOn(tempTree);
// BaseOn calls delete on tempTree
}
}
void UniqueIndexShiftColoredOctreeBuilder::FinalizeTree()
{
Stopwatch watch;
// Generate the material texture
watch.Reset();
if (verbose) printf("Generating material texture... ");
mTree->GetMaterialTexture();
if (verbose) printf("Material texture generated in %d ms\n", (int)(watch.GetTime() * 1000));
}
void UniqueIndexShiftColoredOctreeBuilder::TerminateTree()
{
OctreeLoader::WriteCache(mTree, GetTreeType(), GetOutputFile(), verbose);
delete mTree;
mSceneColors.clear();
mColorReplacers.clear();
mMainTreeColors.clear();
}
void UniqueIndexShiftColoredOctreeBuilder::InitCurPassTree(glm::uvec3 coord)
{
mIntermediateTree = new MaterialTree<Color, ColorCompare>(GetSinglePassTreeDepth());
//mIntermediateTree->UseLeafMap(true);
}
void UniqueIndexShiftColoredOctreeBuilder::ReplaceByQuantized(MaterialTree<Color, ColorCompare>* tree)
{
std::vector<Color> curPassMaterials = tree->GetUniqueMaterials();
CollectionHelper::Unique(curPassMaterials, ColorCompare());
std::vector<Color> curPassQuantizedMaterials(curPassMaterials.size());
if (verbose) printf(".");
// For each material in the curPassMaterials, find the closest material
tbb::concurrent_queue<size_t> newReplacers;
tbb::parallel_for(size_t(0), curPassMaterials.size(), [&](const size_t& i)
{
auto cachedReplacer = mColorReplacers.find(curPassMaterials[i]);
if (cachedReplacer != mColorReplacers.end())
curPassQuantizedMaterials[i] = cachedReplacer->second;
else
{
curPassQuantizedMaterials[i] = NearestFinder<Color>()(curPassMaterials[i], mSceneColors);
newReplacers.push(i);
}
});
// Add the newly found replacers to the cache
for (auto i = newReplacers.unsafe_begin(); i != newReplacers.unsafe_end(); ++i)
mColorReplacers.insert(std::pair<Color, Color>(curPassMaterials[*i], curPassQuantizedMaterials[*i]));
// Update the current scene color map
std::unordered_map<Color, Color> quantizedColors;
for (size_t i = 0; i < curPassMaterials.size(); i++)
quantizedColors.insert(std::pair<Color, Color>(curPassMaterials[i], curPassQuantizedMaterials[i]));
if (verbose) printf(".");
tree->ReplaceMaterials(quantizedColors);
}
void UniqueIndexShiftColoredOctreeBuilder::FinalizeCurPassTree(glm::uvec3 coord)
{
Stopwatch watch;
if (mIntermediateTree->GetNodeCount() > 1) // Only append the tree (and compress) if it is not empty
{
// Propagate the materials
watch.Reset();
if (verbose) printf("Propagating materials in subtree... ");
mIntermediateTree->PropagateMaterials(Color::WeightedAverage);
if (!IsSinglePass())
{
Color expectedRootColor;
for (auto color : mMainTreeColors)
if (color.first == coord)
{
expectedRootColor = color.second;
break;
}
mIntermediateTree->SetMaterial(glm::uvec3(0), 0, expectedRootColor);
}
if (verbose) printf("Materials propagated in %d ms.\n", (int)(watch.GetTime() * 1000));
// Replace the materials by their quantized counterparts
// This is also necessary
if (mQuantizer != NULL || !IsSinglePass())
{
if (verbose) printf("Replacing materials by their quantized counterparts.");
watch.Reset();
ReplaceByQuantized(mIntermediateTree);
if (verbose) printf("Replaced in %d ms.\n", (int)(watch.GetTime() * 1000));
}
// Create the UniqueIndexTree for this current pass
std::string subtreeCompressionType = mTextureCompressionType;
if (!IsSinglePass()) subtreeCompressionType = "b";
auto texture = CompressedTextureFactory<SignedIntMaterial>::GetCompressedTexture(subtreeCompressionType);
UniqueIndexShiftTree<Color, ColorCompare>* curPassTree = new UniqueIndexShiftTree<Color, ColorCompare>(GetSinglePassTreeDepth(), texture, mLevelsWithoutMaterials);
if (!IsSinglePass())
curPassTree->PrepareForMaterials(mSceneColors);
// Finalize the current pass tree
if (verbose) printf("Finalizing subtree...");
watch.Reset();
curPassTree->BaseOn(mIntermediateTree); // Note that BaseOn will delete the intermediate tree, no need to do that manually
if (verbose) printf("Finalized in %d ms.\n", (int)(watch.GetTime() * 1000));
// Convert the subtree to a DAG first, this saved time (and valueable memory) when appending and converting the total tree
if (verbose) printf("Converting subtree to DAG...\n");
watch.Reset();
curPassTree->ToDAG();
if (verbose) printf("Converting took %d ms.\n", (int)(watch.GetTime() * 1000));
if (IsSinglePass()) // Means we just constructed the root, so no need to append
{
delete mTree;
mTree = curPassTree;
}
else
{
// Append the subtree to the main tree
if (verbose) printf("Appending subtree... ");;
watch.Reset();
mTree->Append(coord, GetAppendedTreeLevel(), curPassTree);
delete curPassTree;
if (verbose) printf("Appending took %d ms.\n", (int)(watch.GetTime() * 1000));
// Convert the new part of the main tree to a DAG
if (verbose) printf("Converting current tree to DAG...\n");
watch.Reset();
mTree->ToDAG(GetAppendedTreeLevel());
if (verbose) printf("Converting took %d ms.\n", (int)(watch.GetTime() * 1000));
}
}
else
{
delete mIntermediateTree;
}
}
void UniqueIndexShiftColoredOctreeBuilder::InitPreprocessing()
{
mSceneColors.clear();
mColorReplacers.clear();
mMainTreeColors.clear();
}
void UniqueIndexShiftColoredOctreeBuilder::InitCurPreprocessPass(glm::uvec3 coordinate)
{
mCurPreprocessPassColors.clear();
}
void UniqueIndexShiftColoredOctreeBuilder::PreProcessNode(const glm::uvec3& coordinate, const Color& color)
{
mCurPreprocessPassColors.push_back(color);
}
void UniqueIndexShiftColoredOctreeBuilder::FinalizeCurPreprocessPass(glm::uvec3 coord)
{
if (mCurPreprocessPassColors.empty())
return;
tbb::parallel_sort(mCurPreprocessPassColors, ColorCompare());
std::vector<Color> uniqueColors;
std::vector<float> uniqueColorWeights;
unsigned curColorCount = 0;
Color lastSeenColor = mCurPreprocessPassColors[0];
for (auto color : mCurPreprocessPassColors)
{
if (!(lastSeenColor == color))
{
uniqueColors.push_back(lastSeenColor);
uniqueColorWeights.push_back((float)curColorCount);
lastSeenColor = color;
}
curColorCount++;
}
Color avgColor = Color::WeightedAverage(uniqueColors, uniqueColorWeights);
mMainTreeColors.push_back(std::pair<glm::uvec3, Color>(coord, avgColor));
// Append the unique colors to the scene colors and compress them to keep the memory usage acceptable
mSceneColors.insert(mSceneColors.end(), uniqueColors.begin(), uniqueColors.end());
tbb::parallel_sort(mSceneColors, ColorCompare());
mSceneColors.erase(std::unique(mSceneColors.begin(), mSceneColors.end()), mSceneColors.end());
}
void UniqueIndexShiftColoredOctreeBuilder::FinalizePreprocessing()
{
// Quantize the scene colors
if (mQuantizer == NULL)
{
if (verbose) printf("Scene contains %llu colors...\n", (unsigned64)mSceneColors.size());
mColorReplacers = std::unordered_map<Color, Color>();
for (auto color : mSceneColors)
mColorReplacers.insert(std::pair<Color, Color>(color, color));
}
else
{
if (verbose) printf("Quantizing/merging %llu colors...", (unsigned64)mSceneColors.size());
Stopwatch watch; watch.Reset();
auto quantizedSceneColors = mQuantizer->QuantizeMaterials(mSceneColors);
// Replace the list of scene colors with the quantized scene colors
mSceneColors.clear();
for (auto color : *quantizedSceneColors)
mSceneColors.push_back(color.second);
tbb::parallel_sort(mSceneColors, ColorCompare());
mSceneColors.erase(std::unique(mSceneColors.begin(), mSceneColors.end()), mSceneColors.end());
mSceneColors.shrink_to_fit();
// Build a dictionary for quick lookup of original scene colors and their quantized counterparts
mColorReplacers = std::unordered_map<Color, Color>();
for (auto color : *quantizedSceneColors)
mColorReplacers.insert(std::pair<Color, Color>(Color(color.first), Color(color.second)));
delete quantizedSceneColors;
// Replace the old colors by the new ones
if (verbose) printf("Quantized colors in %d ms\n", (int)(watch.GetTime() * 1000));
}
// Clear the cur preprocessColors (to free up memory during tree construction)
mCurPreprocessPassColors = std::vector<Color>();
}
void UniqueIndexShiftColoredOctreeBuilder::AddMissingNode(const glm::uvec3& coordinate, const Color& color)
{
if (!mIntermediateTree->HasLeaf(coordinate))
AddNode(coordinate, color);
}
void UniqueIndexShiftColoredOctreeBuilder::AddNode(const glm::uvec3& coordinate, const Color& colorVec)
{
mIntermediateTree->AddLeafNode(coordinate, Color(colorVec));
}
std::vector<size_t> UniqueIndexShiftColoredOctreeBuilder::GetOctreeNodesPerLevel()
{
return mTree->GetOctreeNodesPerLevel();
}
std::vector<size_t> UniqueIndexShiftColoredOctreeBuilder::GetNodesPerLevel()
{
return mTree->GetNodesPerLevel();
}

View File

@@ -0,0 +1,65 @@
#pragma once
#include "BaseMaterialOctreeBuilder.h"
#include <unordered_set>
#include "../../scene/Material/Color.h"
#include "../../inc/glm/common.hpp"
#include "../../core/Hashers.h"
#include "../../scene/Octree/UniqueIndexShiftTree.h"
#include "../../scene/Octree/MaterialTree.h"
#include "../../scene/Material/MaterialQuantizer/BaseQuantizer.h"
struct u8vec3comparer;
struct ColorCompare;
class UniqueIndexShiftColoredOctreeBuilder : public BaseMaterialOctreeBuilder<Color>
{
public:
UniqueIndexShiftColoredOctreeBuilder(std::string textureCompressionType, BaseQuantizer<Color, ColorCompare>* quantizer = NULL, unsigned32 levelsWithoutMaterials = 0);
~UniqueIndexShiftColoredOctreeBuilder() override;
std::string GetTreeType() override;
protected:
bool SubTreeCompare(const glm::uvec3& coord1, const glm::uvec3& coord2) const override;
// Initialize the main tree
void InitTree() override;
bool UsePreprocessing() const override;
void InitPreprocessing() override;
void InitCurPreprocessPass(glm::uvec3 coordinate) override;
void PreProcessNode(const glm::uvec3& coordinate, const Color& color) override;
void FinalizeCurPreprocessPass(glm::uvec3 coord) override;
void FinalizePreprocessing() override;
void FinalizeTree() override;
// Step to finalize the main tree (for example storing it to a file)
void TerminateTree() override;
// Initialize the tree for the current pass.
void InitCurPassTree(glm::uvec3 coord) override;
// Terminate the tree in the current pass. This means it should also be appended to the main tree and deleted
void FinalizeCurPassTree(glm::uvec3 coord) override;
// Should add a node to the current pass tree at the given coordinate and color
void AddNode(const glm::uvec3& coordinate, const Color& color) override;
void AddMissingNode(const glm::uvec3& coordinate, const Color& color) override;
std::vector<size_t> GetOctreeNodesPerLevel() override;
std::vector<size_t> GetNodesPerLevel() override;
void ReplaceByQuantized(MaterialTree<Color, ColorCompare>* intermediateTree);
private:
UniqueIndexShiftTree<Color, ColorCompare>* mTree;
bool mReduceColors;
std::string mTextureCompressionType;
unsigned32 mLevelsWithoutMaterials;
std::vector<Color> mSceneColors;
std::unordered_map<Color, Color> mColorReplacers;
std::vector<Color> mCurPreprocessPassColors;
std::vector<std::pair<glm::uvec3, Color>> mMainTreeColors;
MaterialTree<Color, ColorCompare>* mIntermediateTree;
BaseQuantizer<Color, ColorCompare>* mQuantizer;
};