///////////////////////////////////////////////////////////////////////////
//
// System: Simplygon
// File: AggregationProcessorExample.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"
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, bool cullGeometries = false, bool setupCamera = false);
void SetupCameraAndAddToSet(SimplygonSDK::real location[3], SimplygonSDK::real lookat[3], SimplygonSDK::spScene& scene, SimplygonSDK::spSelectionSet& cameraSelectionSet);
int main(int argc, char* argv[])
{
try
{
InitExample();
// Set global variable. Using Orthonormal method for calculating
// tangentspace.
sg->SetGlobalSetting("DefaultTBNType", SG_TANGENTSPACEMETHOD_ORTHONORMAL);
std::string assetPath = GetAssetPath();
printf("Running scene aggregation, collapsing objects and baking material...\n");
PrintProgressBar(0); //Initial progress bar
RunExample(assetPath + "/CombineScene/scene.obj", "CombinedScene_MergeAll", true, true);
printf("\nDone.\n\n");
printf("Running scene aggregation, only baking material...\n");
PrintProgressBar(0); //Initial progress bar
RunExample(assetPath + "/CombineScene/scene.obj", "CombinedScene_MergeMaterials", true, false);
printf("\nDone.\n\n");
printf("Running scene aggregation, only merging objects...\n");
PrintProgressBar(0); //Initial progress bar
RunExample(assetPath + "/CombineScene/scene.obj", "CombinedScene_MergeGeometries", false, true);
printf("\nDone.\n\n");
printf("Running scene aggregation, collapsing objects and baking material and applying visibility pass...\n");
PrintProgressBar(0); //Initial progress bar
RunExample(assetPath + "/CombineSceneVisibility/scene.obj", "CombineSceneVisibility_MergeAllAndCull", true, true, true);
printf("\nDone.\n\n");
printf("Running scene aggregation, collapsing objects and baking material and applying visibility pass with camera...\n");
PrintProgressBar(0); //Initial progress bar
RunExample(assetPath + "/CombineSceneVisibility/scene.obj", "CombineSceneVisibility_MergeAllAndCull", true, true, true, true);
printf("\nDone.\n\n");
printf("All LODs complete, shutting down...");
DeinitExample();
}
catch (const std::exception& ex)
{
std::cerr << ex.what() << std::endl;
return -1;
}
return 0;
}
void RunExample(const std::string& readFrom, const std::string& writeTo, const bool &castMaterials, const bool &mergeGeometries, bool cullGeometries, bool setupCamera)
{
std::string diffuseTextureName = writeTo + "_Diffuse.png";
std::string specularTextureName = writeTo + "_Specular.png";
// Load from file
spWavefrontImporter objReader = sg->CreateWavefrontImporter();
objReader->SetImportFilePath(readFrom.c_str());
objReader->SetExtractGroups(true);
if (!objReader->RunImport())
throw std::exception("Failed to load input file!");
// Get the scene
spScene scene = objReader->GetScene();
//setup visibility cameras if culling is enabled.
rid selectionSetID = -1;
real smallArea{};
if (setupCamera)
{
spSelectionSet cameraSelectionSet = sg->CreateSelectionSet();
selectionSetID = scene->GetSelectionSetTable()->AddSelectionSet(cameraSelectionSet);
//We want to make sure there aren't small patches of non visible triangles (surrounded by visible triangle)
//that are culled from the scene. It is better to keep those non-visible triangles if we want to do a good job
//with the reducer and the tex-coord generator
smallArea = (scene->GetRadius() / 40.0f) * (scene->GetRadius() / 40.0f);
real location[]{ 1.f, 1.f, 1.f };
real lookat[]{ 0.f , 0.f, 0.f };
SetupCameraAndAddToSet(location, lookat, scene, cameraSelectionSet);
}
spScene lodScene = sg->CreateScene();
lodScene->DeepCopy(scene);
// Create the spAggregationProcessor
spAggregationProcessor aggregationProcessor = sg->CreateAggregationProcessor();
// Set the input scene
aggregationProcessor->SetScene(lodScene);
spAggregationSettings aggregatorSettings = aggregationProcessor->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.
aggregatorSettings->SetBaseAtlasOnOriginalTexCoords(true);
aggregatorSettings->SetMergeGeometries(mergeGeometries);
aggregatorSettings->SetMergeMaterials(castMaterials);
aggregationProcessor->GetVisibilitySettings()->SetUseVisibilityWeightsInTexcoordGenerator(true);
aggregationProcessor->GetVisibilitySettings()->SetCullOccludedGeometry(cullGeometries);
aggregationProcessor->GetVisibilitySettings()->SetCameraSelectionSetID(selectionSetID);
if (setupCamera)
aggregationProcessor->GetVisibilitySettings()->SetFillNonVisibleAreaThreshold(smallArea);
// Get the mapping image settings, a mapping image is needed to
// cast the new textures.
spMappingImageSettings mappingImageSettings = aggregationProcessor->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);
//Add observer
aggregationProcessor->AddObserver(&progressObserver, SG_EVENT_PROGRESS);
// Run the process
aggregationProcessor->RunProcessing();
if (castMaterials)
{
//Clear the copied materials from the lod, and later populate it with newly generated materials
spTextureTable lodTexTable = lodScene->GetTextureTable();
lodTexTable->Clear();
spMaterialTable lodMatTable = lodScene->GetMaterialTable();
lodMatTable->Clear();
//Create a new material that we cast the new textures onto
spMaterial lodMaterial = sg->CreateMaterial();
lodMaterial->SetName("output_material");
lodMatTable->AddMaterial(lodMaterial);
lodMatTable->SetName("CombinedMaterials");
spColorCaster diffuseCaster = sg->CreateColorCaster();
diffuseCaster->SetColorType(SG_MATERIAL_CHANNEL_DIFFUSE);
diffuseCaster->SetSourceMaterials(scene->GetMaterialTable());
diffuseCaster->SetSourceTextures(scene->GetTextureTable());
diffuseCaster->SetOutputFilePath(diffuseTextureName.c_str());
diffuseCaster->SetMappingImage(aggregationProcessor->GetMappingImage());
diffuseCaster->RunProcessing();
//Setup new texture in LOD scene
AddSimplygonTexture(lodMaterial, lodTexTable, SG_MATERIAL_CHANNEL_DIFFUSE, diffuseTextureName.c_str());
spColorCaster specularCaster = sg->CreateColorCaster();
specularCaster->SetColorType(SG_MATERIAL_CHANNEL_SPECULAR);
specularCaster->SetSourceMaterials(scene->GetMaterialTable());
specularCaster->SetSourceTextures(scene->GetTextureTable());
specularCaster->SetOutputFilePath(specularTextureName.c_str());
specularCaster->SetMappingImage(aggregationProcessor->GetMappingImage());
specularCaster->RunProcessing();
//Setup new texture in LOD scene
AddSimplygonTexture(lodMaterial, lodTexTable, SG_MATERIAL_CHANNEL_SPECULAR, specularTextureName.c_str());
}
// Store to file
spWavefrontExporter objexp = sg->CreateWavefrontExporter();
std::string output_geometry_filename = GetExecutablePath() + writeTo + ".obj";
objexp->SetExportFilePath(output_geometry_filename.c_str());
objexp->SetScene(lodScene);
objexp->RunExport();
}
void SetupCameraAndAddToSet(SimplygonSDK::real location[3], SimplygonSDK::real lookat[3], SimplygonSDK::spScene& scene, SimplygonSDK::spSelectionSet& cameraSelectionSet)
{
auto sceneCamera = sg->CreateSceneCamera();
sceneCamera->SetCameraType(SG_CAMERATYPE_ORTHOGRAPHIC);
auto cameraLocations = sceneCamera->GetCameraPositions();
auto cameraLookats = sceneCamera->GetTargetPositions();
//Set the tuple count to 1
cameraLocations->SetTupleCount(1);
cameraLookats->SetTupleCount(1);
//Set the camera view
cameraLocations->SetTuple(0, location);
cameraLookats->SetTuple(0, lookat);
//Decide the size of the orthographic pixels
//This should correspond to the size of details in the scene that should be preserved
real smallSceneSize = scene->GetRadius() / 500.0f;
sceneCamera->SetOrthographicCameraPixelSize(smallSceneSize);
if (sceneCamera->ValidateCamera()) //If the camera is setup correctly
{
scene->GetRootNode()->AddChild(sceneCamera);
cameraSelectionSet->AddItem(sceneCamera->GetNodeGUID());
}
}