///////////////////////////////////////////////////////////////////////////
//
// System: Simplygon
// File: AggregationPipelineExample.cpp
// Language: C++
//
// Copyright (c) 2019 Microsoft. All rights reserved.
//
// This is private property, and it is illegal to copy or distribute in
// any form, without written authorization by the copyright owner(s).
//
///////////////////////////////////////////////////////////////////////////
//
// #Description#
//
// Reduce drawcalls by using the IAggregationProcessor to combine all the
// objects in a scene into a single new object that shares newly
// generated textures.
//
//
// A scene with multiple objects and materials is loaded.
// The scene contains 4 instances of the SimplygonMan,
// 1 textured Utah teapot and 1 textured plane.
//
// We run an AggregationProcessor on the scene and export the result as a
// Wavefront file.
//
// The resulting scene has a single texture per material channel (diffuse
// and specular). All textures have been combined into these new textures.
// All texture coordinates have been re-computed to map to the new
// textures.
//
// The 4 SimplygonMan meshes share texture coordinates since they had
// the same textures. The plane and Utah teapot have new unique
// UV-coordinates.
//
// See the output texture images to see how the textures have been
// combined.
//
///////////////////////////////////////////////////////////////////////////
#include "../Common/Example.h"
#include <chrono>
#ifdef __cpp_lib_filesystem // Check for C++17 filesystem
#include <filesystem>
namespace fs = std::filesystem;
#else
#ifndef _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING
#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING
#endif
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem::v1;
#endif
class ProgressObserver : public robserver
{
public:
virtual void Execute(
IObject * subject,
rid EventId,
void * EventParameterBlock,
unsigned int EventParameterBlockSize )
{
// only care for progress events
if( EventId == SG_EVENT_PROGRESS )
{
// get the progress in percent
int val = *((int *)EventParameterBlock);
// tell the process to continue
// this is required by the progress event
*((int *)EventParameterBlock) = 1;
// output the progress update
PrintProgressBar( val );
}
}
} progressObserver;
void RunExample(const std::string& readFrom, const std::string& writeTo, const bool &castMaterials, const bool &mergeGeometries);
int main(int argc, char* argv[])
{
InitExample();
// Set global variable. Using Orthonormal method for calculating
// tangentspace.
sg->SetGlobalSetting("DefaultTBNType", SG_TANGENTSPACEMETHOD_ORTHONORMAL);
std::string assetPath = GetAssetPath();
std::chrono::milliseconds elapsed(0);
int numRuns = 1;
double timeMultiplier = 1.0 / ((double)(numRuns > 1 ? (numRuns - 1) : 1) * 1000.0);
printf("Running scene aggregation, collapsing objects and baking material...\n");
elapsed = std::chrono::milliseconds::zero();
for (int run = 0; run < numRuns; ++run)
{
PrintProgressBar(0); //Initial progress bar
auto start = std::chrono::high_resolution_clock::now();
RunExample(assetPath + "/CombineScene/scene.obj", "CombinedScene_MergeAll", true, true);
if (run || numRuns == 1)
elapsed += std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
}
printf("\nDone, %.2f seconds\n\n", (double)elapsed.count() * timeMultiplier);
printf("Running scene aggregation, only baking material...\n");
elapsed = std::chrono::milliseconds::zero();
for (int run = 0; run < numRuns; ++run)
{
PrintProgressBar(0); //Initial progress bar
auto start = std::chrono::high_resolution_clock::now();
RunExample(assetPath + "/CombineScene/scene.obj", "CombinedScene_MergeMaterials", true, false);
if (run || numRuns == 1)
elapsed += std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
}
printf("\nDone, %.2f seconds\n\n", (double)elapsed.count() * timeMultiplier);
printf("Running scene aggregation, only merging objects...\n");
elapsed = std::chrono::milliseconds::zero();
for (int run = 0; run < numRuns; ++run)
{
PrintProgressBar(0); //Initial progress bar
auto start = std::chrono::high_resolution_clock::now();
RunExample(assetPath + "/CombineScene/scene.obj", "CombinedScene_MergeGeometries", false, true);
if (run || numRuns == 1)
elapsed += std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
}
printf("\nDone, %.2f seconds\n\n", (double)elapsed.count() * timeMultiplier);
printf("All LODs complete, shutting down...");
DeinitExample();
return 0;
}
void RunExample(const std::string& readFrom, const std::string& writeTo, const bool &castMaterials, const bool &mergeGeometries)
{
//Setup output paths
fs::path outputBasePath = fs::path( GetExecutablePath() ) / writeTo;
fs::remove_all( outputBasePath );
fs::create_directories( outputBasePath );
std::string outputGeomPath = ( outputBasePath / ( writeTo + ".obj" ) ).generic_u8string();
//Create a remeshing pipeline
spAggregationPipeline aggregationPipeline = sg->CreateAggregationPipeline();
///////////////////////////////////////////////////////////////////////////////////////////////
// SETTINGS
spPipelineSettings pipelineSettings = aggregationPipeline->GetPipelineSettings();
pipelineSettings->SetTextureOutputPath( ( outputBasePath / "Textures" ).generic_u8string().c_str() );
spAggregationSettings aggregationSettings = aggregationPipeline->GetAggregationSettings();
// Set the BaseAtlasOnOriginalTexCoords to true so that the new texture
// coords will be based on the original. This way the four identical
// SimplygonMan mesh instances will still share texture coords, and
// when packing the texture charts into the new atlas, only rotations
// multiples of 90 degrees are allowed.
aggregationSettings->SetBaseAtlasOnOriginalTexCoords(true);
aggregationSettings->SetMergeGeometries(mergeGeometries);
aggregationSettings->SetMergeMaterials(castMaterials);
spVisibilitySettings visibilitySettings = aggregationPipeline->GetVisibilitySettings();
visibilitySettings->SetUseVisibilityWeightsInTexcoordGenerator(true);
// Get the mapping image settings, a mapping image is needed to
// cast the new textures.
spMappingImageSettings mappingImageSettings = aggregationPipeline->GetMappingImageSettings();
mappingImageSettings->SetGenerateMappingImage(true);
mappingImageSettings->SetGutterSpace(1);
mappingImageSettings->SetWidth(2048);
mappingImageSettings->SetHeight(2048);
mappingImageSettings->SetUseFullRetexturing(true); //replace old UVs
// If BaseAtlasOnOriginalTexCoords is enabled and
// if charts are overlapping in the original texture coords, they will be separated if
// SeparateOverlappingCharts is set to true.
mappingImageSettings->SetChartAggregatorSeparateOverlappingCharts(false);
// Set the output texcoord level name for the retextured UVs
mappingImageSettings->SetTexCoordLevelName( "retextured" );
if (castMaterials)
{
spColorCaster diffuseCaster = sg->CreateColorCaster();
spColorCasterSettings diffuseCasterSetttings = diffuseCaster->GetColorCasterSettings();
diffuseCasterSetttings->SetMaterialChannel(SG_MATERIAL_CHANNEL_DIFFUSE);
spColorCaster specularCaster = sg->CreateColorCaster();
spColorCasterSettings specularCasterSetttings = specularCaster->GetColorCasterSettings();
specularCasterSetttings->SetMaterialChannel(SG_MATERIAL_CHANNEL_SPECULAR);
aggregationPipeline->AddMaterialCaster( diffuseCaster, 0 );
aggregationPipeline->AddMaterialCaster( specularCaster, 0 );
}
//END SETTINGS
///////////////////////////////////////////////////////////////////////////////////////////////
//Add observer
aggregationPipeline->AddObserver( &progressObserver, SG_EVENT_PROGRESS );
// Run the process
aggregationPipeline->RunSceneFromFile( readFrom.c_str(), outputGeomPath.c_str() );
spPipelineSerializer serializer = sg->CreatePipelineSerializer();
serializer->SavePipelineToFile( (GetExecutablePath() + writeTo + "/pipeline.json").c_str(), aggregationPipeline );
}