Initial commit: Final state of the master project
This commit is contained in:
87
Research/core/OctreeBuilder/BaseMaterialOctreeBuilder.h
Normal file
87
Research/core/OctreeBuilder/BaseMaterialOctreeBuilder.h
Normal 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));
|
||||
}
|
||||
105
Research/core/OctreeBuilder/BaseOctreeBuilder.cpp
Normal file
105
Research/core/OctreeBuilder/BaseOctreeBuilder.cpp
Normal 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;
|
||||
}
|
||||
91
Research/core/OctreeBuilder/BaseOctreeBuilder.h
Normal file
91
Research/core/OctreeBuilder/BaseOctreeBuilder.h
Normal 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;
|
||||
};
|
||||
|
||||
23
Research/core/OctreeBuilder/BaseStandardOctreeBuilder.h
Normal file
23
Research/core/OctreeBuilder/BaseStandardOctreeBuilder.h
Normal 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:
|
||||
};
|
||||
28
Research/core/OctreeBuilder/ColorAndValueQuantizerFactory.h
Normal file
28
Research/core/OctreeBuilder/ColorAndValueQuantizerFactory.h
Normal 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;
|
||||
@@ -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";
|
||||
}
|
||||
63
Research/core/OctreeBuilder/ColorQuantizerFactory.cpp
Normal file
63
Research/core/OctreeBuilder/ColorQuantizerFactory.cpp
Normal 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]+)";
|
||||
}
|
||||
17
Research/core/OctreeBuilder/ColorQuantizerFactory.h
Normal file
17
Research/core/OctreeBuilder/ColorQuantizerFactory.h
Normal 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();
|
||||
};
|
||||
|
||||
79
Research/core/OctreeBuilder/CompressedTextureFactory.h
Normal file
79
Research/core/OctreeBuilder/CompressedTextureFactory.h
Normal 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]*");
|
||||
}
|
||||
};
|
||||
245
Research/core/OctreeBuilder/HierarchicalMaterialOctreeBuilder.h
Normal file
245
Research/core/OctreeBuilder/HierarchicalMaterialOctreeBuilder.h
Normal 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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
113
Research/core/OctreeBuilder/MaterialMultiRootOctreeBuilder.h
Normal file
113
Research/core/OctreeBuilder/MaterialMultiRootOctreeBuilder.h
Normal 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(); }
|
||||
};
|
||||
|
||||
151
Research/core/OctreeBuilder/MultiBitsMultiRootOctreeBuilder.h
Normal file
151
Research/core/OctreeBuilder/MultiBitsMultiRootOctreeBuilder.h
Normal 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"; }
|
||||
120
Research/core/OctreeBuilder/MultiRootOctreeBuilder.cpp
Normal file
120
Research/core/OctreeBuilder/MultiRootOctreeBuilder.cpp
Normal 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();
|
||||
}
|
||||
40
Research/core/OctreeBuilder/MultiRootOctreeBuilder.h
Normal file
40
Research/core/OctreeBuilder/MultiRootOctreeBuilder.h
Normal 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;
|
||||
};
|
||||
|
||||
24
Research/core/OctreeBuilder/NormalQuantizerFactory.cpp
Normal file
24
Research/core/OctreeBuilder/NormalQuantizerFactory.cpp
Normal 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";
|
||||
}
|
||||
18
Research/core/OctreeBuilder/NormalQuantizerFactory.h
Normal file
18
Research/core/OctreeBuilder/NormalQuantizerFactory.h
Normal 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();
|
||||
};
|
||||
|
||||
217
Research/core/OctreeBuilder/OctreeBuilder.cpp
Normal file
217
Research/core/OctreeBuilder/OctreeBuilder.cpp
Normal 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;
|
||||
}
|
||||
46
Research/core/OctreeBuilder/OctreeBuilder.h
Normal file
46
Research/core/OctreeBuilder/OctreeBuilder.h
Normal 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();
|
||||
};
|
||||
|
||||
73
Research/core/OctreeBuilder/OctreeBuilderStatistics.h
Normal file
73
Research/core/OctreeBuilder/OctreeBuilderStatistics.h
Normal 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));
|
||||
}
|
||||
};
|
||||
476
Research/core/OctreeBuilder/OctreeConverter.cpp
Normal file
476
Research/core/OctreeBuilder/OctreeConverter.cpp
Normal 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;
|
||||
}
|
||||
36
Research/core/OctreeBuilder/OctreeConverter.h
Normal file
36
Research/core/OctreeBuilder/OctreeConverter.h
Normal 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) { }
|
||||
};
|
||||
};
|
||||
|
||||
782
Research/core/OctreeBuilder/OctreeLoader.cpp
Normal file
782
Research/core/OctreeBuilder/OctreeLoader.cpp
Normal 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;
|
||||
}
|
||||
109
Research/core/OctreeBuilder/OctreeLoader.h
Normal file
109
Research/core/OctreeBuilder/OctreeLoader.h
Normal 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);
|
||||
};
|
||||
|
||||
76
Research/core/OctreeBuilder/PoolBuilderFactory.h
Normal file
76
Research/core/OctreeBuilder/PoolBuilderFactory.h
Normal 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");
|
||||
}
|
||||
};
|
||||
109
Research/core/OctreeBuilder/RandomOctreeBuilder.cpp
Normal file
109
Research/core/OctreeBuilder/RandomOctreeBuilder.cpp
Normal 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();
|
||||
}
|
||||
|
||||
38
Research/core/OctreeBuilder/RandomOctreeBuilder.h
Normal file
38
Research/core/OctreeBuilder/RandomOctreeBuilder.h
Normal 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;
|
||||
};
|
||||
|
||||
22
Research/core/OctreeBuilder/SettingsParser.cpp
Normal file
22
Research/core/OctreeBuilder/SettingsParser.cpp
Normal 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"); }
|
||||
17
Research/core/OctreeBuilder/SettingsParser.h
Normal file
17
Research/core/OctreeBuilder/SettingsParser.h
Normal 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();
|
||||
};
|
||||
|
||||
122
Research/core/OctreeBuilder/StandardOctreeBuilder.cpp
Normal file
122
Research/core/OctreeBuilder/StandardOctreeBuilder.cpp
Normal 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);
|
||||
}
|
||||
35
Research/core/OctreeBuilder/StandardOctreeBuilder.h
Normal file
35
Research/core/OctreeBuilder/StandardOctreeBuilder.h
Normal 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;
|
||||
};
|
||||
|
||||
137
Research/core/OctreeBuilder/TreeTypeParser.cpp
Normal file
137
Research/core/OctreeBuilder/TreeTypeParser.cpp
Normal 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;
|
||||
}
|
||||
23
Research/core/OctreeBuilder/TreeTypeParser.h
Normal file
23
Research/core/OctreeBuilder/TreeTypeParser.h
Normal 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);
|
||||
};
|
||||
|
||||
364
Research/core/OctreeBuilder/UniqueIndexMaterialOctreeBuilder.h
Normal file
364
Research/core/OctreeBuilder/UniqueIndexMaterialOctreeBuilder.h
Normal 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;
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user