///////////////////////////////////////////////////////////////////////////
//
// System: Simplygon
// File: AggregateRepeatingUVsExample.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#
//
// This example shows how to get improved texture quality from meshes that
// contain lots of tiling textures when aggregating the scene materials.
//
// By subdividing (cutting) the mesh along the UV-tiling seams we generate
// a more complex geometry with more triangles that will only have UVs in
// the uv = [ (0, 0) , (tileSize, tileSize) ] interval.
//
// The tileSize is a user selected positive integer that determines the
// cutting frequency. Smaller tileSize values (minimum 1) increases
// the geometry complexity but improves the texture quality.
//
// So the trade-off is that with better texture quality you get more geometry complexity.
//
// Example:
// The scene contains a plane (two triangles) with a tiling dirt texture that
// gets repeated 10 times in both U and V directions. Normally if the
// scene is aggregated, the geometry will be exactly the same but the
// output texture will contain all the (10x10=100) dirt patches put into the
// single texture. The output texture cannot be tiling because
// there might be more objects in the scene whose textures will also
// be aggregated into the output.
//
// By enabling the subdivision based on UV tiles - the dirt mesh is cut
// for every "positive multiplier" of times the texture is repeated.
// The output geometry contains more triangle in the plane, but the
// dirt texture can be much larger. If the tileSize is 1 - the output
// texture will only contain one instance of the dirt texture with no
// repeats.
//
///////////////////////////////////////////////////////////////////////////
#include "../Common/Example.h"
void RunExample(const std::string& readFrom, const std::string& writeTo, int tileSize);
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();
for (int tileSize = 1; tileSize < 10; tileSize += 2)
{
std::string outputFileName = "Aggregation_UVSubdivision_TileSize_" + std::to_string(tileSize);
RunExample(assetPath + "UVSubdivision/TilingTextures.obj", outputFileName, tileSize);
}
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, int tileSize)
{
// Load from file
spWavefrontImporter objReader = sg->CreateWavefrontImporter();
objReader->SetImportFilePath( readFrom.c_str() );
if( !objReader->RunImport() )
throw std::exception("Failed to load input file!");
// Get the scene
spScene scene = objReader->GetScene();
spScene aggregatedScene = sg->CreateScene();
aggregatedScene->DeepCopy( scene );
// Create the spAggregationProcessor
spAggregationProcessor aggregationProcessor = sg->CreateAggregationProcessor();
// Set the input scene
aggregationProcessor->SetScene(aggregatedScene);
spAggregationSettings aggregatorSettings = aggregationProcessor->GetAggregationSettings();
//Enable the subdivision
aggregatorSettings->SetSubdivideGeometryBasedOnUVTiles(true);
aggregatorSettings->SetSubdivisionTileSize(tileSize);
// 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);
// Get the mapping image settings, a mapping image is needed to
// cast the new textures.
spMappingImageSettings mappingImageSettings = aggregationProcessor->GetMappingImageSettings();
mappingImageSettings->SetGenerateMappingImage(true);
mappingImageSettings->SetGutterSpace(20);
mappingImageSettings->SetWidth(512);
mappingImageSettings->SetHeight(512);
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);
// Run the process
aggregationProcessor->RunProcessing();
//Clear the copied materials from the lod, and later populate it with newly generated materials
spTextureTable lodTexTable = aggregatedScene->GetTextureTable();
lodTexTable->Clear();
spMaterialTable lodMatTable = aggregatedScene->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() );
std::string diffuse;
diffuse = writeTo + ".png";
diffuseCaster->SetOutputFilePath( diffuse.c_str() );
diffuseCaster->SetMappingImage( aggregationProcessor->GetMappingImage() );
diffuseCaster->RunProcessing();
//Setup new texture in LOD scene
AddSimplygonTexture( lodMaterial, lodTexTable, SG_MATERIAL_CHANNEL_DIFFUSE, diffuse.c_str());
if (true)
{
// Store to file
spWavefrontExporter objexp = sg->CreateWavefrontExporter();
std::string output_geometry_filename = GetExecutablePath() + writeTo + ".obj";
objexp->SetExportFilePath(output_geometry_filename.c_str());
objexp->SetScene(aggregatedScene);
objexp->RunExport();
}
//Create a new Scene based on the aggregated scene.
spScene reducedScene = sg->CreateScene();
reducedScene->DeepCopy(aggregatedScene);
//Many triangles might have been created from the subdivisions
//We run a reduction on the results.
spReductionProcessor red = sg->CreateReductionProcessor();
// Set the input scene
red->SetScene(reducedScene);
spReductionSettings reductionSettings = red->GetReductionSettings();
reductionSettings->SetOnScreenSize(500);
// Run the process
red->RunProcessing();
if(true)
{
// Store to file
spWavefrontExporter objexp = sg->CreateWavefrontExporter();
std::string output_geometry_filename = GetExecutablePath() + writeTo + "_Reduced" + ".obj";
objexp->SetExportFilePath(output_geometry_filename.c_str());
objexp->SetScene(reducedScene);
objexp->RunExport();
}
}