This example shows how to use the Reduction processor with modular seams.
cpp
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include <string>
#include <stdlib.h>
#include <filesystem>
#include <future>
#include "SimplygonLoader.h"
Simplygon::spScene LoadScene(Simplygon::ISimplygon* sg, const char* path)
{
// Create scene importer
Simplygon::spSceneImporter sgSceneImporter = sg->CreateSceneImporter();
sgSceneImporter->SetImportFilePath(path);
// Run scene importer.
auto importResult = sgSceneImporter->Run();
if (Simplygon::Failed(importResult))
{
throw std::exception("Failed to load scene.");
}
Simplygon::spScene sgScene = sgSceneImporter->GetScene();
return sgScene;
}
void SaveScene(Simplygon::ISimplygon* sg, Simplygon::spScene sgScene, const char* path)
{
// Create scene exporter.
Simplygon::spSceneExporter sgSceneExporter = sg->CreateSceneExporter();
std::string outputScenePath = std::string("output\\") + std::string("ReductionWithModularSeams") + std::string("_") + std::string(path);
sgSceneExporter->SetExportFilePath(outputScenePath.c_str());
sgSceneExporter->SetScene(sgScene);
// Run scene exporter.
auto exportResult = sgSceneExporter->Run();
if (Simplygon::Failed(exportResult))
{
throw std::exception("Failed to save scene.");
}
}
void CheckLog(Simplygon::ISimplygon* sg)
{
// Check if any errors occurred.
bool hasErrors = sg->ErrorOccurred();
if (hasErrors)
{
Simplygon::spStringArray errors = sg->CreateStringArray();
sg->GetErrorMessages(errors);
auto errorCount = errors->GetItemCount();
if (errorCount > 0)
{
printf("%s\n", "CheckLog: Errors:");
for (auto errorIndex = 0U; errorIndex < errorCount; ++errorIndex)
{
Simplygon::spString errorString = errors->GetItem((int)errorIndex);
printf("%s\n", errorString.c_str());
}
sg->ClearErrorMessages();
}
}
else
{
printf("%s\n", "CheckLog: No errors.");
}
// Check if any warnings occurred.
bool hasWarnings = sg->WarningOccurred();
if (hasWarnings)
{
Simplygon::spStringArray warnings = sg->CreateStringArray();
sg->GetWarningMessages(warnings);
auto warningCount = warnings->GetItemCount();
if (warningCount > 0)
{
printf("%s\n", "CheckLog: Warnings:");
for (auto warningIndex = 0U; warningIndex < warningCount; ++warningIndex)
{
Simplygon::spString warningString = warnings->GetItem((int)warningIndex);
printf("%s\n", warningString.c_str());
}
sg->ClearWarningMessages();
}
}
else
{
printf("%s\n", "CheckLog: No warnings.");
}
// Error out if Simplygon has errors.
if (hasErrors)
{
throw std::exception("Processing failed with an error");
}
}
Simplygon::spGeometryDataCollection ExtractGeometriesInScene(Simplygon::ISimplygon* sg, Simplygon::spScene sgModularAssetsScene)
{
// Extract all geometries in the scene into individual geometries
Simplygon::spGeometryDataCollection sgGeometryDataCollection = sg->CreateGeometryDataCollection();
int id = sgModularAssetsScene->SelectNodes("ISceneMesh");
auto set = sgModularAssetsScene->GetSelectionSetTable()->GetSelectionSet(id);
auto geometryCount = set->GetItemCount();
for (auto geomIndex = 0U; geomIndex < geometryCount; ++geomIndex)
{
auto guid = set->GetItem(geomIndex);
Simplygon::spSceneNode sgSceneNode = sgModularAssetsScene->GetNodeByGUID(guid);
Simplygon::spSceneMesh sgSceneMesh = Simplygon::spSceneMesh::SafeCast(sgSceneNode);
Simplygon::spGeometryData geom = sgSceneMesh->GetGeometry();
sgGeometryDataCollection->AddGeometryData(geom);
}
return sgGeometryDataCollection;
}
void DebugModularSeams(Simplygon::ISimplygon* sg, bool outputDebugInfo, Simplygon::spModularSeams sgModularSeams)
{
if (outputDebugInfo)
{
// Optional but helpful to be able to see what the analyzer found.
// Each unique modular seam can be extracted as a geometry. If the analyzer ran with
// IsTranslationIndependent=false then the seam geometry should be exactly located at the same
// place as the modular seams in the original scene.
// Each modular seam also has a string array with all the names of the geometries that have that
// specific modular seam.
auto seamCount = sgModularSeams->GetModularSeamCount();
for (auto seamIndex = 0U; seamIndex < seamCount; ++seamIndex)
{
Simplygon::spGeometryData debugGeom = sgModularSeams->NewDebugModularSeamGeometry((int)seamIndex);
Simplygon::spStringArray geometryNames = sgModularSeams->NewModularSeamGeometryStringArray((int)seamIndex);
Simplygon::spScene debugScene = sg->CreateScene();
debugScene->GetRootNode()->CreateChildMesh(debugGeom);
std::string fileName = std::string("output\\") + std::string("ReductionWithModularSeams_seam_") + std::string(std::to_string(seamIndex)) + std::string(".obj");
Simplygon::spSceneExporter sgSceneExporter = sg->CreateSceneExporter();
sgSceneExporter->SetExportFilePath( fileName.c_str() );
sgSceneExporter->SetScene( debugScene );
sgSceneExporter->Run();
auto vertexCount = debugGeom->GetVertexCount();
auto geometryNamesCount = geometryNames->GetItemCount();
std::string outputText = std::string("Seam ") + std::string(std::to_string(seamIndex)) + std::string(" consists of ") + std::string(std::to_string(vertexCount)) + std::string(" vertices and is shared among ") + std::string(std::to_string(geometryNamesCount)) + std::string(" geometries:");
printf("%s\n", outputText.c_str());
for (auto geomIndex = 0U; geomIndex < geometryNamesCount; ++geomIndex)
{
Simplygon::spString geometryName = geometryNames->GetItem((int)geomIndex);
std::string geometryNameOutput = std::string(" geom ") + std::string(std::to_string(geomIndex)) + std::string(": ") + std::string(geometryName.c_str());
printf("%s\n", geometryNameOutput.c_str());
}
}
}
}
void ModifyReductionSettings(Simplygon::spReductionSettings sgReductionSettings, float triangleRatio, float maxDeviation)
{
sgReductionSettings->SetKeepSymmetry( true );
sgReductionSettings->SetUseAutomaticSymmetryDetection( true );
sgReductionSettings->SetUseHighQualityNormalCalculation( true );
sgReductionSettings->SetReductionHeuristics( Simplygon::EReductionHeuristics::Consistent );
// The importances can be changed here to allow the features to be weighed differently both during
// regular reduction and during the analyzing of modular seam
sgReductionSettings->SetEdgeSetImportance( 1.0f );
sgReductionSettings->SetGeometryImportance( 1.0f );
sgReductionSettings->SetGroupImportance( 1.0f );
sgReductionSettings->SetMaterialImportance( 1.0f );
sgReductionSettings->SetShadingImportance( 1.0f );
sgReductionSettings->SetSkinningImportance( 1.0f );
sgReductionSettings->SetTextureImportance( 1.0f );
sgReductionSettings->SetVertexColorImportance( 1.0f );
// The reduction targets below are only used for the regular reduction, not the modular seam
// analyzer
sgReductionSettings->SetReductionTargetTriangleRatio( triangleRatio );
sgReductionSettings->SetReductionTargetMaxDeviation( maxDeviation );
sgReductionSettings->SetReductionTargets(Simplygon::EStopCondition::All, true, false, true, false);
}
void GenerateModularSeams(Simplygon::ISimplygon* sg, Simplygon::spScene sgModularAssetsScene)
{
Simplygon::spGeometryDataCollection sgGeometryDataCollection = ExtractGeometriesInScene(sg, sgModularAssetsScene);
// Figure out a small value in relation to the scene that will be the tolerance for the modular seams
// if a coordinate is moved a distance smaller than the tolerance, then it is regarded as the same
// coordinate so two vertices are the at the same place if the distance between them is smaller than
// radius * smallValue
sgModularAssetsScene->CalculateExtents();
float smallValue = 0.0001f;
float sceneRadius = sgModularAssetsScene->GetRadius();
float tolerance = sceneRadius * smallValue;
Simplygon::spReductionSettings sgReductionSettings = sg->CreateReductionSettings();
// The triangleRatio and maxDeviation are not important here and will not be used, only the
// relative importances and settings
ModifyReductionSettings(sgReductionSettings, 0.0f, 0.0f);
// Create the modular seam analyzer.
Simplygon::spModularSeamAnalyzer sgModularSeamAnalyzer = sg->CreateModularSeamAnalyzer();
sgModularSeamAnalyzer->SetTolerance( tolerance );
sgModularSeamAnalyzer->SetIsTranslationIndependent( false );
auto modularGeometryCount = sgGeometryDataCollection->GetItemCount();
// Add the geometries to the analyzer
for (auto modularGeometryId = 0U; modularGeometryId < modularGeometryCount; ++modularGeometryId)
{
auto modularGeometryObject = sgGeometryDataCollection->GetItemAsObject(modularGeometryId);
Simplygon::spGeometryData modularGeometry = Simplygon::spGeometryData::SafeCast(modularGeometryObject);
sgModularSeamAnalyzer->AddGeometry(modularGeometry);
}
// The analyzer needs to know the different reduction settings importances and such because it
// runs the reduction as far as possible for all the seams and stores the order and max deviations
// for future reductions of assets with the same seams
sgModularSeamAnalyzer->Analyze(sgReductionSettings);
// Fetch the modular seams. These can be stored to file and used later
Simplygon::spModularSeams sgModularSeams = sgModularSeamAnalyzer->GetModularSeams();
std::string modularSeamsPath = std::string("output\\") + std::string("ModularAssets.modseam");
sgModularSeams->SaveToFile(modularSeamsPath.c_str());
}
Simplygon::spModularSeams LoadModularSeams(Simplygon::ISimplygon* sg)
{
// Load pre-generated modular seams
Simplygon::spModularSeams sgModularSeams = sg->CreateModularSeams();
std::string modularSeamsPath = std::string("output\\") + std::string("ModularAssets.modseam");
sgModularSeams->LoadFromFile(modularSeamsPath.c_str());
return sgModularSeams;
}
void RunReduction(Simplygon::ISimplygon* sg, Simplygon::spScene sgModularAssetsScene, Simplygon::spModularSeams sgModularSeams, float triangleRatio, float maxDeviation, float modularSeamReductionRatio, float modularSeamMaxDeviation)
{
Simplygon::spGeometryDataCollection sgGeometryDataCollection = ExtractGeometriesInScene(sg, sgModularAssetsScene);
auto modularGeometryCount = sgGeometryDataCollection->GetItemCount();
// Add the geometries to the analyzer
for (auto modularGeometryId = 0U; modularGeometryId < modularGeometryCount; ++modularGeometryId)
{
auto modularGeometryObject = sgGeometryDataCollection->GetItemAsObject(modularGeometryId);
Simplygon::spGeometryData modularGeometry = Simplygon::spGeometryData::SafeCast(modularGeometryObject);
// Run reduction on each geometry individually,
// feed the modular seams into the reducer with the ModularSeamSettings
// so the modular seams are reduced identically and are untouched by the rest of the
// geometry reduction
Simplygon::spScene sgSingleAssetScene = sgModularAssetsScene->NewCopy();
// Remove all the geometries but keep any textures, materials etc.
sgSingleAssetScene->RemoveSceneNodes();
// Add just a copy of the current geometry to the scene
Simplygon::spGeometryData modularGeometryCopy = modularGeometry->NewCopy(true);
Simplygon::spSceneNode sgRootNode = sgSingleAssetScene->GetRootNode();
Simplygon::spSceneMesh sgSceneMesh = sgRootNode->CreateChildMesh(modularGeometryCopy);
Simplygon::spReductionProcessor sgReductionProcessor = sg->CreateReductionProcessor();
sgReductionProcessor->SetScene( sgSingleAssetScene );
Simplygon::spReductionSettings sgReductionSettings = sgReductionProcessor->GetReductionSettings();
Simplygon::spModularSeamSettings sgModularSeamSettings = sgReductionProcessor->GetModularSeamSettings();
// Set the same reduction (importance) settings as the modular seam analyzer for consistent
// quality
ModifyReductionSettings(sgReductionSettings, triangleRatio, maxDeviation);
sgModularSeamSettings->SetReductionRatio( modularSeamReductionRatio );
sgModularSeamSettings->SetMaxDeviation( modularSeamMaxDeviation );
sgModularSeamSettings->SetStopCondition( Simplygon::EStopCondition::All );
sgModularSeamSettings->SetModularSeams( sgModularSeams );
sgReductionProcessor->RunProcessing();
Simplygon::spString geomName = modularGeometry->GetName();
std::string outputName = std::string(geomName) + std::string(".obj");
SaveScene(sg, sgSingleAssetScene, outputName.c_str());
}
}
void RunReductionWithModularSeams(Simplygon::ISimplygon* sg)
{
// Set reduction targets. Stop condition is set to 'All'
float triangleRatio = 0.5f;
float maxDeviation = 0.0f;
float modularSeamReductionRatio = 0.75f;
float modularSeamMaxDeviation = 0.0f;
// Load a scene that has a few modular assets in it as different scene meshes.
Simplygon::spScene sgModularAssetsScene = LoadScene(sg, "../../../Assets/ModularAssets/ModularAssets.obj");
bool generateNewSeams = true;
if (generateNewSeams)
{
GenerateModularSeams(sg, sgModularAssetsScene);
}
Simplygon::spModularSeams sgModularSeams = LoadModularSeams(sg);
DebugModularSeams(sg, true, sgModularSeams);
// Run the reduction. The seams are reduced identically and the rest of the geometries are reduced
// like normal
RunReduction(sg, sgModularAssetsScene, sgModularSeams, triangleRatio, maxDeviation, modularSeamReductionRatio, modularSeamMaxDeviation);
// Check log for any warnings or errors.
printf("%s\n", "Check log for any warnings or errors.");
CheckLog(sg);
}
int main()
{
Simplygon::ISimplygon* sg = NULL;
Simplygon::EErrorCodes initval = Simplygon::Initialize( &sg );
if( initval != Simplygon::EErrorCodes::NoError )
{
printf( "Failed to initialize Simplygon: ErrorCode(%d)", (int)initval );
return int(initval);
}
RunReductionWithModularSeams(sg);
Simplygon::Deinitialize(sg);
return 0;
}
csharp
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.IO;
using System.Threading.Tasks;
public class Program
{
static Simplygon.spScene LoadScene(Simplygon.ISimplygon sg, string path)
{
// Create scene importer
using Simplygon.spSceneImporter sgSceneImporter = sg.CreateSceneImporter();
sgSceneImporter.SetImportFilePath(path);
// Run scene importer.
var importResult = sgSceneImporter.Run();
if (Simplygon.Simplygon.Failed(importResult))
{
throw new System.Exception("Failed to load scene.");
}
Simplygon.spScene sgScene = sgSceneImporter.GetScene();
return sgScene;
}
static void SaveScene(Simplygon.ISimplygon sg, Simplygon.spScene sgScene, string path)
{
// Create scene exporter.
using Simplygon.spSceneExporter sgSceneExporter = sg.CreateSceneExporter();
string outputScenePath = string.Join("", new string[] { "output\\", "ReductionWithModularSeams", "_", path });
sgSceneExporter.SetExportFilePath(outputScenePath);
sgSceneExporter.SetScene(sgScene);
// Run scene exporter.
var exportResult = sgSceneExporter.Run();
if (Simplygon.Simplygon.Failed(exportResult))
{
throw new System.Exception("Failed to save scene.");
}
}
static void CheckLog(Simplygon.ISimplygon sg)
{
// Check if any errors occurred.
bool hasErrors = sg.ErrorOccurred();
if (hasErrors)
{
Simplygon.spStringArray errors = sg.CreateStringArray();
sg.GetErrorMessages(errors);
var errorCount = errors.GetItemCount();
if (errorCount > 0)
{
Console.WriteLine("CheckLog: Errors:");
for (uint errorIndex = 0; errorIndex < errorCount; ++errorIndex)
{
string errorString = errors.GetItem((int)errorIndex);
Console.WriteLine(errorString);
}
sg.ClearErrorMessages();
}
}
else
{
Console.WriteLine("CheckLog: No errors.");
}
// Check if any warnings occurred.
bool hasWarnings = sg.WarningOccurred();
if (hasWarnings)
{
Simplygon.spStringArray warnings = sg.CreateStringArray();
sg.GetWarningMessages(warnings);
var warningCount = warnings.GetItemCount();
if (warningCount > 0)
{
Console.WriteLine("CheckLog: Warnings:");
for (uint warningIndex = 0; warningIndex < warningCount; ++warningIndex)
{
string warningString = warnings.GetItem((int)warningIndex);
Console.WriteLine(warningString);
}
sg.ClearWarningMessages();
}
}
else
{
Console.WriteLine("CheckLog: No warnings.");
}
// Error out if Simplygon has errors.
if (hasErrors)
{
throw new System.Exception("Processing failed with an error");
}
}
static Simplygon.spGeometryDataCollection ExtractGeometriesInScene(Simplygon.ISimplygon sg, Simplygon.spScene sgModularAssetsScene)
{
// Extract all geometries in the scene into individual geometries
Simplygon.spGeometryDataCollection sgGeometryDataCollection = sg.CreateGeometryDataCollection();
int id = sgModularAssetsScene.SelectNodes("ISceneMesh");
var set = sgModularAssetsScene.GetSelectionSetTable().GetSelectionSet(id);
var geometryCount = set.GetItemCount();
for (uint geomIndex = 0; geomIndex < geometryCount; ++geomIndex)
{
var guid = set.GetItem(geomIndex);
Simplygon.spSceneNode sgSceneNode = sgModularAssetsScene.GetNodeByGUID(guid);
Simplygon.spSceneMesh sgSceneMesh = Simplygon.spSceneMesh.SafeCast(sgSceneNode);
Simplygon.spGeometryData geom = sgSceneMesh.GetGeometry();
sgGeometryDataCollection.AddGeometryData(geom);
}
return sgGeometryDataCollection;
}
static void DebugModularSeams(Simplygon.ISimplygon sg, bool outputDebugInfo, Simplygon.spModularSeams sgModularSeams)
{
if (outputDebugInfo)
{
// Optional but helpful to be able to see what the analyzer found.
// Each unique modular seam can be extracted as a geometry. If the analyzer ran with
// IsTranslationIndependent=false then the seam geometry should be exactly located at the same
// place as the modular seams in the original scene.
// Each modular seam also has a string array with all the names of the geometries that have that
// specific modular seam.
var seamCount = sgModularSeams.GetModularSeamCount();
for (uint seamIndex = 0; seamIndex < seamCount; ++seamIndex)
{
Simplygon.spGeometryData debugGeom = sgModularSeams.NewDebugModularSeamGeometry((int)seamIndex);
Simplygon.spStringArray geometryNames = sgModularSeams.NewModularSeamGeometryStringArray((int)seamIndex);
Simplygon.spScene debugScene = sg.CreateScene();
debugScene.GetRootNode().CreateChildMesh(debugGeom);
string fileName = string.Join("", new string[] { "output\\", "ReductionWithModularSeams_seam_", seamIndex.ToString(), ".obj" });
using Simplygon.spSceneExporter sgSceneExporter = sg.CreateSceneExporter();
sgSceneExporter.SetExportFilePath( fileName );
sgSceneExporter.SetScene( debugScene );
sgSceneExporter.Run();
var vertexCount = debugGeom.GetVertexCount();
var geometryNamesCount = geometryNames.GetItemCount();
string outputText = string.Join("", new string[] { "Seam ", seamIndex.ToString(), " consists of ", vertexCount.ToString(), " vertices and is shared among ", geometryNamesCount.ToString(), " geometries:" });
Console.WriteLine(outputText);
for (uint geomIndex = 0; geomIndex < geometryNamesCount; ++geomIndex)
{
string geometryName = geometryNames.GetItem((int)geomIndex);
string geometryNameOutput = string.Join("", new string[] { " geom ", geomIndex.ToString(), ": ", geometryName });
Console.WriteLine(geometryNameOutput);
}
}
}
}
static void ModifyReductionSettings(Simplygon.spReductionSettings sgReductionSettings, float triangleRatio, float maxDeviation)
{
sgReductionSettings.SetKeepSymmetry( true );
sgReductionSettings.SetUseAutomaticSymmetryDetection( true );
sgReductionSettings.SetUseHighQualityNormalCalculation( true );
sgReductionSettings.SetReductionHeuristics( Simplygon.EReductionHeuristics.Consistent );
// The importances can be changed here to allow the features to be weighed differently both during
// regular reduction and during the analyzing of modular seam
sgReductionSettings.SetEdgeSetImportance( 1.0f );
sgReductionSettings.SetGeometryImportance( 1.0f );
sgReductionSettings.SetGroupImportance( 1.0f );
sgReductionSettings.SetMaterialImportance( 1.0f );
sgReductionSettings.SetShadingImportance( 1.0f );
sgReductionSettings.SetSkinningImportance( 1.0f );
sgReductionSettings.SetTextureImportance( 1.0f );
sgReductionSettings.SetVertexColorImportance( 1.0f );
// The reduction targets below are only used for the regular reduction, not the modular seam
// analyzer
sgReductionSettings.SetReductionTargetTriangleRatio( triangleRatio );
sgReductionSettings.SetReductionTargetMaxDeviation( maxDeviation );
sgReductionSettings.SetReductionTargets(Simplygon.EStopCondition.All, true, false, true, false);
}
static void GenerateModularSeams(Simplygon.ISimplygon sg, Simplygon.spScene sgModularAssetsScene)
{
Simplygon.spGeometryDataCollection sgGeometryDataCollection = ExtractGeometriesInScene(sg, sgModularAssetsScene);
// Figure out a small value in relation to the scene that will be the tolerance for the modular seams
// if a coordinate is moved a distance smaller than the tolerance, then it is regarded as the same
// coordinate so two vertices are the at the same place if the distance between them is smaller than
// radius * smallValue
sgModularAssetsScene.CalculateExtents();
float smallValue = 0.0001f;
float sceneRadius = sgModularAssetsScene.GetRadius();
float tolerance = sceneRadius * smallValue;
using Simplygon.spReductionSettings sgReductionSettings = sg.CreateReductionSettings();
// The triangleRatio and maxDeviation are not important here and will not be used, only the
// relative importances and settings
ModifyReductionSettings(sgReductionSettings, 0.0f, 0.0f);
// Create the modular seam analyzer.
using Simplygon.spModularSeamAnalyzer sgModularSeamAnalyzer = sg.CreateModularSeamAnalyzer();
sgModularSeamAnalyzer.SetTolerance( tolerance );
sgModularSeamAnalyzer.SetIsTranslationIndependent( false );
var modularGeometryCount = sgGeometryDataCollection.GetItemCount();
// Add the geometries to the analyzer
for (uint modularGeometryId = 0; modularGeometryId < modularGeometryCount; ++modularGeometryId)
{
var modularGeometryObject = sgGeometryDataCollection.GetItemAsObject(modularGeometryId);
Simplygon.spGeometryData modularGeometry = Simplygon.spGeometryData.SafeCast(modularGeometryObject);
sgModularSeamAnalyzer.AddGeometry(modularGeometry);
}
// The analyzer needs to know the different reduction settings importances and such because it
// runs the reduction as far as possible for all the seams and stores the order and max deviations
// for future reductions of assets with the same seams
sgModularSeamAnalyzer.Analyze(sgReductionSettings);
// Fetch the modular seams. These can be stored to file and used later
using Simplygon.spModularSeams sgModularSeams = sgModularSeamAnalyzer.GetModularSeams();
string modularSeamsPath = string.Join("", new string[] { "output\\", "ModularAssets.modseam" });
sgModularSeams.SaveToFile(modularSeamsPath);
}
static Simplygon.spModularSeams LoadModularSeams(Simplygon.ISimplygon sg)
{
// Load pre-generated modular seams
Simplygon.spModularSeams sgModularSeams = sg.CreateModularSeams();
string modularSeamsPath = string.Join("", new string[] { "output\\", "ModularAssets.modseam" });
sgModularSeams.LoadFromFile(modularSeamsPath);
return sgModularSeams;
}
static void RunReduction(Simplygon.ISimplygon sg, Simplygon.spScene sgModularAssetsScene, Simplygon.spModularSeams sgModularSeams, float triangleRatio, float maxDeviation, float modularSeamReductionRatio, float modularSeamMaxDeviation)
{
Simplygon.spGeometryDataCollection sgGeometryDataCollection = ExtractGeometriesInScene(sg, sgModularAssetsScene);
var modularGeometryCount = sgGeometryDataCollection.GetItemCount();
// Add the geometries to the analyzer
for (uint modularGeometryId = 0; modularGeometryId < modularGeometryCount; ++modularGeometryId)
{
var modularGeometryObject = sgGeometryDataCollection.GetItemAsObject(modularGeometryId);
Simplygon.spGeometryData modularGeometry = Simplygon.spGeometryData.SafeCast(modularGeometryObject);
// Run reduction on each geometry individually,
// feed the modular seams into the reducer with the ModularSeamSettings
// so the modular seams are reduced identically and are untouched by the rest of the
// geometry reduction
Simplygon.spScene sgSingleAssetScene = sgModularAssetsScene.NewCopy();
// Remove all the geometries but keep any textures, materials etc.
sgSingleAssetScene.RemoveSceneNodes();
// Add just a copy of the current geometry to the scene
Simplygon.spGeometryData modularGeometryCopy = modularGeometry.NewCopy(true);
Simplygon.spSceneNode sgRootNode = sgSingleAssetScene.GetRootNode();
Simplygon.spSceneMesh sgSceneMesh = sgRootNode.CreateChildMesh(modularGeometryCopy);
using Simplygon.spReductionProcessor sgReductionProcessor = sg.CreateReductionProcessor();
sgReductionProcessor.SetScene( sgSingleAssetScene );
using Simplygon.spReductionSettings sgReductionSettings = sgReductionProcessor.GetReductionSettings();
using Simplygon.spModularSeamSettings sgModularSeamSettings = sgReductionProcessor.GetModularSeamSettings();
// Set the same reduction (importance) settings as the modular seam analyzer for consistent
// quality
ModifyReductionSettings(sgReductionSettings, triangleRatio, maxDeviation);
sgModularSeamSettings.SetReductionRatio( modularSeamReductionRatio );
sgModularSeamSettings.SetMaxDeviation( modularSeamMaxDeviation );
sgModularSeamSettings.SetStopCondition( Simplygon.EStopCondition.All );
sgModularSeamSettings.SetModularSeams( sgModularSeams );
sgReductionProcessor.RunProcessing();
string geomName = modularGeometry.GetName();
string outputName = string.Join("", new string[] { geomName, ".obj" });
SaveScene(sg, sgSingleAssetScene, outputName);
}
}
static void RunReductionWithModularSeams(Simplygon.ISimplygon sg)
{
// Set reduction targets. Stop condition is set to 'All'
float triangleRatio = 0.5f;
float maxDeviation = 0.0f;
float modularSeamReductionRatio = 0.75f;
float modularSeamMaxDeviation = 0.0f;
// Load a scene that has a few modular assets in it as different scene meshes.
Simplygon.spScene sgModularAssetsScene = LoadScene(sg, "../../../Assets/ModularAssets/ModularAssets.obj");
bool generateNewSeams = true;
if (generateNewSeams)
{
GenerateModularSeams(sg, sgModularAssetsScene);
}
Simplygon.spModularSeams sgModularSeams = LoadModularSeams(sg);
DebugModularSeams(sg, true, sgModularSeams);
// Run the reduction. The seams are reduced identically and the rest of the geometries are reduced
// like normal
RunReduction(sg, sgModularAssetsScene, sgModularSeams, triangleRatio, maxDeviation, modularSeamReductionRatio, modularSeamMaxDeviation);
// Check log for any warnings or errors.
Console.WriteLine("Check log for any warnings or errors.");
CheckLog(sg);
}
static int Main(string[] args)
{
using var sg = Simplygon.Loader.InitSimplygon(out var errorCode, out var errorMessage);
if (errorCode != Simplygon.EErrorCodes.NoError)
{
Console.WriteLine( $"Failed to initialize Simplygon: ErrorCode({(int)errorCode}) {errorMessage}" );
return (int)errorCode;
}
RunReductionWithModularSeams(sg);
return 0;
}
}
python
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
import math
import os
import sys
import glob
import gc
import threading
from pathlib import Path
from simplygon10 import simplygon_loader
from simplygon10 import Simplygon
def LoadScene(sg: Simplygon.ISimplygon, path: str):
# Create scene importer
sgSceneImporter = sg.CreateSceneImporter()
sgSceneImporter.SetImportFilePath(path)
# Run scene importer.
importResult = sgSceneImporter.Run()
if Simplygon.Failed(importResult):
raise Exception('Failed to load scene.')
sgScene = sgSceneImporter.GetScene()
return sgScene
def SaveScene(sg: Simplygon.ISimplygon, sgScene: Simplygon.spScene, path: str):
# Create scene exporter.
sgSceneExporter = sg.CreateSceneExporter()
outputScenePath = ''.join(['output\\', 'ReductionWithModularSeams', '_', path])
sgSceneExporter.SetExportFilePath(outputScenePath)
sgSceneExporter.SetScene(sgScene)
# Run scene exporter.
exportResult = sgSceneExporter.Run()
if Simplygon.Failed(exportResult):
raise Exception('Failed to save scene.')
def CheckLog(sg: Simplygon.ISimplygon):
# Check if any errors occurred.
hasErrors = sg.ErrorOccurred()
if hasErrors:
errors = sg.CreateStringArray()
sg.GetErrorMessages(errors)
errorCount = errors.GetItemCount()
if errorCount > 0:
print('CheckLog: Errors:')
for errorIndex in range(errorCount):
errorString = errors.GetItem(errorIndex)
print(errorString)
sg.ClearErrorMessages()
else:
print('CheckLog: No errors.')
# Check if any warnings occurred.
hasWarnings = sg.WarningOccurred()
if hasWarnings:
warnings = sg.CreateStringArray()
sg.GetWarningMessages(warnings)
warningCount = warnings.GetItemCount()
if warningCount > 0:
print('CheckLog: Warnings:')
for warningIndex in range(warningCount):
warningString = warnings.GetItem(warningIndex)
print(warningString)
sg.ClearWarningMessages()
else:
print('CheckLog: No warnings.')
# Error out if Simplygon has errors.
if hasErrors:
raise Exception('Processing failed with an error')
def ExtractGeometriesInScene(sg: Simplygon.ISimplygon, sgModularAssetsScene: Simplygon.spScene):
# Extract all geometries in the scene into individual geometries
sgGeometryDataCollection = sg.CreateGeometryDataCollection()
id = sgModularAssetsScene.SelectNodes("ISceneMesh")
set = sgModularAssetsScene.GetSelectionSetTable().GetSelectionSet(id)
geometryCount = set.GetItemCount()
for geomIndex in range(geometryCount):
guid = set.GetItem(geomIndex)
sgSceneNode = sgModularAssetsScene.GetNodeByGUID(guid)
sgSceneMesh = Simplygon.spSceneMesh.SafeCast(sgSceneNode)
geom = sgSceneMesh.GetGeometry()
sgGeometryDataCollection.AddGeometryData(geom)
return sgGeometryDataCollection
def DebugModularSeams(sg: Simplygon.ISimplygon, outputDebugInfo, sgModularSeams: Simplygon.spModularSeams):
if outputDebugInfo:
# Optional but helpful to be able to see what the analyzer found.
# Each unique modular seam can be extracted as a geometry. If the analyzer ran with
# IsTranslationIndependent=false then the seam geometry should be exactly located at the same
# place as the modular seams in the original scene.
# Each modular seam also has a string array with all the names of the geometries that have that
# specific modular seam.
seamCount = sgModularSeams.GetModularSeamCount()
for seamIndex in range(seamCount):
debugGeom = sgModularSeams.NewDebugModularSeamGeometry(seamIndex)
geometryNames = sgModularSeams.NewModularSeamGeometryStringArray(seamIndex)
debugScene = sg.CreateScene()
debugScene.GetRootNode().CreateChildMesh(debugGeom)
fileName = ''.join(['output\\', 'ReductionWithModularSeams_seam_', str(seamIndex), '.obj'])
sgSceneExporter = sg.CreateSceneExporter()
sgSceneExporter.SetExportFilePath( fileName )
sgSceneExporter.SetScene( debugScene )
sgSceneExporter.Run()
vertexCount = debugGeom.GetVertexCount()
geometryNamesCount = geometryNames.GetItemCount()
outputText = ''.join(['Seam ', str(seamIndex), ' consists of ', str(vertexCount), ' vertices and is shared among ', str(geometryNamesCount), ' geometries:'])
print(outputText)
for geomIndex in range(geometryNamesCount):
geometryName = geometryNames.GetItem(geomIndex)
geometryNameOutput = ''.join([' geom ', str(geomIndex), ': ', geometryName])
print(geometryNameOutput)
def ModifyReductionSettings(sgReductionSettings: Simplygon.spReductionSettings, triangleRatio, maxDeviation):
sgReductionSettings.SetKeepSymmetry( True )
sgReductionSettings.SetUseAutomaticSymmetryDetection( True )
sgReductionSettings.SetUseHighQualityNormalCalculation( True )
sgReductionSettings.SetReductionHeuristics( Simplygon.EReductionHeuristics_Consistent )
# The importances can be changed here to allow the features to be weighed differently both during
# regular reduction and during the analyzing of modular seam
sgReductionSettings.SetEdgeSetImportance( 1.0 )
sgReductionSettings.SetGeometryImportance( 1.0 )
sgReductionSettings.SetGroupImportance( 1.0 )
sgReductionSettings.SetMaterialImportance( 1.0 )
sgReductionSettings.SetShadingImportance( 1.0 )
sgReductionSettings.SetSkinningImportance( 1.0 )
sgReductionSettings.SetTextureImportance( 1.0 )
sgReductionSettings.SetVertexColorImportance( 1.0 )
# The reduction targets below are only used for the regular reduction, not the modular seam
# analyzer
sgReductionSettings.SetReductionTargetTriangleRatio( triangleRatio )
sgReductionSettings.SetReductionTargetMaxDeviation( maxDeviation )
sgReductionSettings.SetReductionTargets(Simplygon.EStopCondition_All, True, False, True, False)
def GenerateModularSeams(sg: Simplygon.ISimplygon, sgModularAssetsScene: Simplygon.spScene):
sgGeometryDataCollection = ExtractGeometriesInScene(sg, sgModularAssetsScene)
# Figure out a small value in relation to the scene that will be the tolerance for the modular seams
# if a coordinate is moved a distance smaller than the tolerance, then it is regarded as the same
# coordinate so two vertices are the at the same place if the distance between them is smaller than
# radius * smallValue
sgModularAssetsScene.CalculateExtents()
smallValue = 0.0001
sceneRadius = sgModularAssetsScene.GetRadius()
tolerance = sceneRadius * smallValue
sgReductionSettings = sg.CreateReductionSettings()
# The triangleRatio and maxDeviation are not important here and will not be used, only the
# relative importances and settings
ModifyReductionSettings(sgReductionSettings, 0.0, 0.0)
# Create the modular seam analyzer.
sgModularSeamAnalyzer = sg.CreateModularSeamAnalyzer()
sgModularSeamAnalyzer.SetTolerance( tolerance )
sgModularSeamAnalyzer.SetIsTranslationIndependent( False )
modularGeometryCount = sgGeometryDataCollection.GetItemCount()
# Add the geometries to the analyzer
for modularGeometryId in range(modularGeometryCount):
modularGeometryObject = sgGeometryDataCollection.GetItemAsObject(modularGeometryId)
modularGeometry = Simplygon.spGeometryData.SafeCast(modularGeometryObject)
sgModularSeamAnalyzer.AddGeometry(modularGeometry)
# The analyzer needs to know the different reduction settings importances and such because it
# runs the reduction as far as possible for all the seams and stores the order and max deviations
# for future reductions of assets with the same seams
sgModularSeamAnalyzer.Analyze(sgReductionSettings)
# Fetch the modular seams. These can be stored to file and used later
sgModularSeams = sgModularSeamAnalyzer.GetModularSeams()
modularSeamsPath = ''.join(['output\\', 'ModularAssets.modseam'])
sgModularSeams.SaveToFile(modularSeamsPath)
def LoadModularSeams(sg: Simplygon.ISimplygon):
# Load pre-generated modular seams
sgModularSeams = sg.CreateModularSeams()
modularSeamsPath = ''.join(['output\\', 'ModularAssets.modseam'])
sgModularSeams.LoadFromFile(modularSeamsPath)
return sgModularSeams
def RunReduction(sg: Simplygon.ISimplygon, sgModularAssetsScene: Simplygon.spScene, sgModularSeams: Simplygon.spModularSeams, triangleRatio, maxDeviation, modularSeamReductionRatio, modularSeamMaxDeviation):
sgGeometryDataCollection = ExtractGeometriesInScene(sg, sgModularAssetsScene)
modularGeometryCount = sgGeometryDataCollection.GetItemCount()
# Add the geometries to the analyzer
for modularGeometryId in range(modularGeometryCount):
modularGeometryObject = sgGeometryDataCollection.GetItemAsObject(modularGeometryId)
modularGeometry = Simplygon.spGeometryData.SafeCast(modularGeometryObject)
# Run reduction on each geometry individually,
# feed the modular seams into the reducer with the ModularSeamSettings
# so the modular seams are reduced identically and are untouched by the rest of the
# geometry reduction
sgSingleAssetScene = sgModularAssetsScene.NewCopy()
# Remove all the geometries but keep any textures, materials etc.
sgSingleAssetScene.RemoveSceneNodes()
# Add just a copy of the current geometry to the scene
modularGeometryCopy = modularGeometry.NewCopy(True)
sgRootNode = sgSingleAssetScene.GetRootNode()
sgSceneMesh = sgRootNode.CreateChildMesh(modularGeometryCopy)
sgReductionProcessor = sg.CreateReductionProcessor()
sgReductionProcessor.SetScene( sgSingleAssetScene )
sgReductionSettings = sgReductionProcessor.GetReductionSettings()
sgModularSeamSettings = sgReductionProcessor.GetModularSeamSettings()
# Set the same reduction (importance) settings as the modular seam analyzer for consistent
# quality
ModifyReductionSettings(sgReductionSettings, triangleRatio, maxDeviation)
sgModularSeamSettings.SetReductionRatio( modularSeamReductionRatio )
sgModularSeamSettings.SetMaxDeviation( modularSeamMaxDeviation )
sgModularSeamSettings.SetStopCondition( Simplygon.EStopCondition_All )
sgModularSeamSettings.SetModularSeams( sgModularSeams )
sgReductionProcessor.RunProcessing()
geomName = modularGeometry.GetName()
outputName = ''.join([geomName, '.obj'])
SaveScene(sg, sgSingleAssetScene, outputName)
def RunReductionWithModularSeams(sg: Simplygon.ISimplygon):
# Set reduction targets. Stop condition is set to 'All'
triangleRatio = 0.5
maxDeviation = 0.0
modularSeamReductionRatio = 0.75
modularSeamMaxDeviation = 0.0
# Load a scene that has a few modular assets in it as different scene meshes.
sgModularAssetsScene = LoadScene(sg, '../../../Assets/ModularAssets/ModularAssets.obj')
generateNewSeams = True
if generateNewSeams:
GenerateModularSeams(sg, sgModularAssetsScene)
sgModularSeams = LoadModularSeams(sg)
DebugModularSeams(sg, True, sgModularSeams)
# Run the reduction. The seams are reduced identically and the rest of the geometries are reduced
# like normal
RunReduction(sg, sgModularAssetsScene, sgModularSeams, triangleRatio, maxDeviation, modularSeamReductionRatio, modularSeamMaxDeviation)
# Check log for any warnings or errors.
print("Check log for any warnings or errors.")
CheckLog(sg)
if __name__ == '__main__':
sg = simplygon_loader.init_simplygon()
if sg is None:
exit(Simplygon.GetLastInitializationError())
RunReductionWithModularSeams(sg)
sg = None
gc.collect()