#pragma once #include "BaseMaterialOctreeBuilder.h" #include "../../inc/glm/common.hpp" #include #include "../../core/Hashers.h" #include "../../scene/Octree/MaterialLibraryTree.h" #include "../../scene/Material/MaterialQuantizer/BaseQuantizer.h" template class HierarchicalMaterialOctreeBuilder : public BaseMaterialOctreeBuilder { public: HierarchicalMaterialOctreeBuilder(BaseQuantizer* quantizer = NULL) : mTree(NULL), mMaterialLibrary(NULL), mReduceMaterials(quantizer != NULL), mSceneMaterials(std::vector()), 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()() + (mReduceMaterials ? mQuantizer->GetQuantizerDescriptor() : ""); } protected: bool MaterialHasDefaults() const { return !DefaultMaterials()().empty(); } MaterialLibraryTree* CreateTree(unsigned8 depth) const { return new MaterialLibraryTree(depth); } MaterialLibraryTree* CreateTree(unsigned8 depth, MaterialLibrary* lib) const { return new MaterialLibraryTree(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()(); if (mReduceMaterials) { // Quantize the scene materials CalculateQuantizedMaterials(); // Store the quantized values in the mSceneMaterials mSceneMaterials = std::vector(); 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(); 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* subTree = (MaterialLibraryTree*)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()(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 GetOctreeNodesPerLevel() override { return mTree->GetOctreeNodesPerLevel(); } std::vector GetNodesPerLevel() override { return mTree->GetNodesPerLevel(); } void CalculateUniqueSceneMaterials() { CollectionHelper::Unique(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* mTree; bool mReduceMaterials; std::vector mSceneMaterials; MaterialLibrary* mMaterialLibrary; std::map* mReplacers; BaseQuantizer* mQuantizer; }; //template<> void HierarchicalMaterialOctreeBuilder::DebugQuantize() //{ // std::vector 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::MaterialHasDefaults() const { return false; } template<> void HierarchicalMaterialOctreeBuilder::PreProcessMissingNode(const glm::uvec3& coordinate, const ColorAndNormal& mat) { mSceneMaterials.push_back(mat); }