This example shows how to use Geomorph. The processed scene will contain reduced geometry mesh nodes, each of which in turn holds an additional child mesh node containing a geomorph geometry that shows how original vertices have moved during reduction. Blend geometries between original and geomorph geometries are then generated in steps of 1/10 and stored to file, to show how the morph-data can be used.
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("ReductionWithGeomorph") + 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 ArrayBlend(Simplygon::spRealArray targetArray, Simplygon::spRealArray origArray, Simplygon::spRealArray morphArray, float blend)
{
auto itemCount = targetArray->GetItemCount();
// Morph the geometry
for (auto i = 0U; i < itemCount; ++i)
{
float origValue = origArray->GetItem((int)i);
float morphValue = morphArray->GetItem((int)i);
float blendResult = morphValue * blend + origValue * (1.0f-blend);
targetArray->SetItem((int)i, blendResult);
}
}
void MorphGeometry(Simplygon::spSceneMesh originalSceneMesh, Simplygon::spSceneMesh morphSceneMesh, Simplygon::spSceneMesh blendSceneMesh, float blend)
{
Simplygon::spGeometryData originalGeometry = originalSceneMesh->GetGeometry();
Simplygon::spGeometryData morphGeometry = morphSceneMesh->GetGeometry();
Simplygon::spGeometryData blendGeometry = blendSceneMesh->GetGeometry();
Simplygon::spRealArray origArray = originalGeometry->GetCoords();
Simplygon::spRealArray morphArray = morphGeometry->GetCoords();
Simplygon::spRealArray targetArray = blendGeometry->GetCoords();
Simplygon::spRidArray origVertexIdsArray = originalGeometry->GetVertexIds();
Simplygon::spRidArray morphVertexIdsArray = morphGeometry->GetVertexIds();
Simplygon::spRidArray targetVertexIdsArray = blendGeometry->GetVertexIds();
auto itemCount = targetVertexIdsArray->GetItemCount();
// For each corner:
for (auto c = 0U; c < itemCount; ++c)
{
auto origVertexId = origVertexIdsArray->GetItem((int)c);
auto morphVertexId = morphVertexIdsArray->GetItem((int)c);
auto targetVertexId = targetVertexIdsArray->GetItem((int)c);
// For each x,z,y value in each coordinate:
for (auto d = 0U; d < 3; ++d)
{
int dInt = (int)d;
float origValue = origArray->GetItem(origVertexId * 3 + dInt);
float morphValue = morphArray->GetItem(morphVertexId * 3 + dInt);
float blendResult = morphValue * blend + origValue * (1.0f-blend);
targetArray->SetItem(targetVertexId * 3 + dInt, blendResult);
}
}
// Morph the UVs
targetArray = blendGeometry->GetTexCoords(0);
origArray = originalGeometry->GetTexCoords(0);
morphArray = morphGeometry->GetTexCoords(0);
ArrayBlend(targetArray, origArray, morphArray, blend);
// Morph the Normals
targetArray = blendGeometry->GetNormals();
origArray = originalGeometry->GetNormals();
morphArray = morphGeometry->GetNormals();
ArrayBlend(targetArray, origArray, morphArray, blend);
}
void RunReductionWithGeomorph(Simplygon::ISimplygon* sg)
{
// Load scene to process.
printf("%s\n", "Load scene to process.");
Simplygon::spScene sgScene = LoadScene(sg, "../../../Assets/SimplygonMan/SimplygonMan.obj");
// Create the reduction processor.
Simplygon::spReductionProcessor sgReductionProcessor = sg->CreateReductionProcessor();
// Select Mesh Nodes
int selectionSetId = sgScene->SelectNodes("ISceneMesh");
// Keep a copy of the original scene to be able to blend between this and the morph geometry.
Simplygon::spScene originalScene = sgScene->NewCopy();
Simplygon::spScene blendScene = sgScene->NewCopy();
sgReductionProcessor->SetScene( sgScene );
Simplygon::spReductionSettings sgReductionSettings = sgReductionProcessor->GetReductionSettings();
// Set reduction target to triangle ratio with a ratio of 50%. Enable geomorph creation and
// disable symmetry quad retriangulator.
sgReductionSettings->SetReductionTargets( Simplygon::EStopCondition::All, true, false, false, false );
sgReductionSettings->SetReductionTargetTriangleRatio( 0.5f );
sgReductionSettings->SetCreateGeomorphGeometry( true );
sgReductionSettings->SetUseSymmetryQuadRetriangulator( false );
Simplygon::spRepairSettings sgRepairSettings = sgReductionProcessor->GetRepairSettings();
// Disable TJunctionRemover.
sgRepairSettings->SetWeldDist( 0.0f );
sgRepairSettings->SetUseTJunctionRemover( false );
Simplygon::spNormalCalculationSettings sgNormalCalculationSettings = sgReductionProcessor->GetNormalCalculationSettings();
// Replace normals.
sgNormalCalculationSettings->SetReplaceNormals( true );
sgNormalCalculationSettings->SetHardEdgeAngle( 60.0f );
// Start the reduction process.
printf("%s\n", "Start the reduction process.");
sgReductionProcessor->RunProcessing();
auto set = sgScene->GetSelectionSetTable()->GetSelectionSet(selectionSetId);
// Selection set to collect geomorph nodes in scene.
Simplygon::spSelectionSet geomorphSelectionSet = sg->CreateSelectionSet();
// Morph the geometry
for (auto blendValue = 0; blendValue < 11; ++blendValue)
{
auto geometryCount = set->GetItemCount();
// The LOD scene contains morphed geometries, placed as children of their respective reduced
// geometry mesh node.
// The name of a morph mesh node is the same as its parent, with "_geomorph" added.
for (auto geomIndex = 0U; geomIndex < geometryCount; ++geomIndex)
{
auto guid = set->GetItem(geomIndex);
Simplygon::spSceneNode lodSceneNode = sgScene->GetNodeByGUID(guid);
Simplygon::spSceneNode originalSceneNode = originalScene->GetNodeByGUID(guid);
Simplygon::spSceneNode blendSceneNode = blendScene->GetNodeByGUID(guid);
Simplygon::spSceneMesh lodSceneMesh = Simplygon::spSceneMesh::SafeCast(lodSceneNode);
Simplygon::spSceneMesh originalSceneMesh = Simplygon::spSceneMesh::SafeCast(originalSceneNode);
Simplygon::spSceneMesh blendSceneMesh = Simplygon::spSceneMesh::SafeCast(blendSceneNode);
auto childCount = lodSceneNode->GetChildCount();
for (auto childIndex = 0U; childIndex < childCount; ++childIndex)
{
Simplygon::spSceneNode childNode = lodSceneNode->GetChild((int)childIndex);
auto childName = childNode->GetName();
std::string childNodeString = std::string(childName.c_str());
auto name = lodSceneNode->GetName();
std::string geomorphName = std::string("_geomorph");
if (childNodeString.find(geomorphName) != std::string::npos )
{
Simplygon::spSceneMesh morphSceneMesh = Simplygon::spSceneMesh::SafeCast(childNode);
auto morphGUID = morphSceneMesh->GetNodeGUID();
geomorphSelectionSet->AddItem(morphGUID);
MorphGeometry(originalSceneMesh, morphSceneMesh, blendSceneMesh, blendValue*0.1f);
}
}
}
std::string outputName = std::string("Geomorph_BlendedGeometry") + std::string(std::to_string(blendValue)) + std::string(".obj");
// Save processed scene.
printf("%s\n", "Save processed scene.");
SaveScene(sg, blendScene, outputName.c_str());
}
}
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);
}
RunReductionWithGeomorph(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\\", "ReductionWithGeomorph", "_", 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 ArrayBlend(Simplygon.spRealArray targetArray, Simplygon.spRealArray origArray, Simplygon.spRealArray morphArray, float blend)
{
var itemCount = targetArray.GetItemCount();
// Morph the geometry
for (uint i = 0; i < itemCount; ++i)
{
float origValue = origArray.GetItem((int)i);
float morphValue = morphArray.GetItem((int)i);
float blendResult = morphValue * blend + origValue * (1.0f-blend);
targetArray.SetItem((int)i, blendResult);
}
}
static void MorphGeometry(Simplygon.spSceneMesh originalSceneMesh, Simplygon.spSceneMesh morphSceneMesh, Simplygon.spSceneMesh blendSceneMesh, float blend)
{
Simplygon.spGeometryData originalGeometry = originalSceneMesh.GetGeometry();
Simplygon.spGeometryData morphGeometry = morphSceneMesh.GetGeometry();
Simplygon.spGeometryData blendGeometry = blendSceneMesh.GetGeometry();
Simplygon.spRealArray origArray = originalGeometry.GetCoords();
Simplygon.spRealArray morphArray = morphGeometry.GetCoords();
Simplygon.spRealArray targetArray = blendGeometry.GetCoords();
Simplygon.spRidArray origVertexIdsArray = originalGeometry.GetVertexIds();
Simplygon.spRidArray morphVertexIdsArray = morphGeometry.GetVertexIds();
Simplygon.spRidArray targetVertexIdsArray = blendGeometry.GetVertexIds();
var itemCount = targetVertexIdsArray.GetItemCount();
// For each corner:
for (uint c = 0; c < itemCount; ++c)
{
var origVertexId = origVertexIdsArray.GetItem((int)c);
var morphVertexId = morphVertexIdsArray.GetItem((int)c);
var targetVertexId = targetVertexIdsArray.GetItem((int)c);
// For each x,z,y value in each coordinate:
for (uint d = 0; d < 3; ++d)
{
int dInt = (int)d;
float origValue = origArray.GetItem(origVertexId * 3 + dInt);
float morphValue = morphArray.GetItem(morphVertexId * 3 + dInt);
float blendResult = morphValue * blend + origValue * (1.0f-blend);
targetArray.SetItem(targetVertexId * 3 + dInt, blendResult);
}
}
// Morph the UVs
targetArray = blendGeometry.GetTexCoords(0);
origArray = originalGeometry.GetTexCoords(0);
morphArray = morphGeometry.GetTexCoords(0);
ArrayBlend(targetArray, origArray, morphArray, blend);
// Morph the Normals
targetArray = blendGeometry.GetNormals();
origArray = originalGeometry.GetNormals();
morphArray = morphGeometry.GetNormals();
ArrayBlend(targetArray, origArray, morphArray, blend);
}
static void RunReductionWithGeomorph(Simplygon.ISimplygon sg)
{
// Load scene to process.
Console.WriteLine("Load scene to process.");
Simplygon.spScene sgScene = LoadScene(sg, "../../../Assets/SimplygonMan/SimplygonMan.obj");
// Create the reduction processor.
using Simplygon.spReductionProcessor sgReductionProcessor = sg.CreateReductionProcessor();
// Select Mesh Nodes
int selectionSetId = sgScene.SelectNodes("ISceneMesh");
// Keep a copy of the original scene to be able to blend between this and the morph geometry.
Simplygon.spScene originalScene = sgScene.NewCopy();
Simplygon.spScene blendScene = sgScene.NewCopy();
sgReductionProcessor.SetScene( sgScene );
using Simplygon.spReductionSettings sgReductionSettings = sgReductionProcessor.GetReductionSettings();
// Set reduction target to triangle ratio with a ratio of 50%. Enable geomorph creation and
// disable symmetry quad retriangulator.
sgReductionSettings.SetReductionTargets( Simplygon.EStopCondition.All, true, false, false, false );
sgReductionSettings.SetReductionTargetTriangleRatio( 0.5f );
sgReductionSettings.SetCreateGeomorphGeometry( true );
sgReductionSettings.SetUseSymmetryQuadRetriangulator( false );
using Simplygon.spRepairSettings sgRepairSettings = sgReductionProcessor.GetRepairSettings();
// Disable TJunctionRemover.
sgRepairSettings.SetWeldDist( 0.0f );
sgRepairSettings.SetUseTJunctionRemover( false );
using Simplygon.spNormalCalculationSettings sgNormalCalculationSettings = sgReductionProcessor.GetNormalCalculationSettings();
// Replace normals.
sgNormalCalculationSettings.SetReplaceNormals( true );
sgNormalCalculationSettings.SetHardEdgeAngle( 60.0f );
// Start the reduction process.
Console.WriteLine("Start the reduction process.");
sgReductionProcessor.RunProcessing();
var set = sgScene.GetSelectionSetTable().GetSelectionSet(selectionSetId);
// Selection set to collect geomorph nodes in scene.
using Simplygon.spSelectionSet geomorphSelectionSet = sg.CreateSelectionSet();
// Morph the geometry
for (uint blendValue = 0; blendValue < 11; ++blendValue)
{
var geometryCount = set.GetItemCount();
// The LOD scene contains morphed geometries, placed as children of their respective reduced
// geometry mesh node.
// The name of a morph mesh node is the same as its parent, with "_geomorph" added.
for (uint geomIndex = 0; geomIndex < geometryCount; ++geomIndex)
{
var guid = set.GetItem(geomIndex);
Simplygon.spSceneNode lodSceneNode = sgScene.GetNodeByGUID(guid);
Simplygon.spSceneNode originalSceneNode = originalScene.GetNodeByGUID(guid);
Simplygon.spSceneNode blendSceneNode = blendScene.GetNodeByGUID(guid);
Simplygon.spSceneMesh lodSceneMesh = Simplygon.spSceneMesh.SafeCast(lodSceneNode);
Simplygon.spSceneMesh originalSceneMesh = Simplygon.spSceneMesh.SafeCast(originalSceneNode);
Simplygon.spSceneMesh blendSceneMesh = Simplygon.spSceneMesh.SafeCast(blendSceneNode);
var childCount = lodSceneNode.GetChildCount();
for (uint childIndex = 0; childIndex < childCount; ++childIndex)
{
Simplygon.spSceneNode childNode = lodSceneNode.GetChild((int)childIndex);
var childName = childNode.GetName();
string childNodeString = string.Join("", new string[] { childName });
var name = lodSceneNode.GetName();
string geomorphName = string.Join("", new string[] { "_geomorph" });
if (childNodeString.Contains(geomorphName) )
{
Simplygon.spSceneMesh morphSceneMesh = Simplygon.spSceneMesh.SafeCast(childNode);
var morphGUID = morphSceneMesh.GetNodeGUID();
geomorphSelectionSet.AddItem(morphGUID);
MorphGeometry(originalSceneMesh, morphSceneMesh, blendSceneMesh, blendValue*0.1f);
}
}
}
string outputName = string.Join("", new string[] { "Geomorph_BlendedGeometry", blendValue.ToString(), ".obj" });
// Save processed scene.
Console.WriteLine("Save processed scene.");
SaveScene(sg, blendScene, outputName);
}
}
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;
}
RunReductionWithGeomorph(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\\', 'ReductionWithGeomorph', '_', path])
sgSceneExporter.SetExportFilePath(outputScenePath)
sgSceneExporter.SetScene(sgScene)
# Run scene exporter.
exportResult = sgSceneExporter.Run()
if Simplygon.Failed(exportResult):
raise Exception('Failed to save scene.')
def ArrayBlend(targetArray: Simplygon.spRealArray, origArray: Simplygon.spRealArray, morphArray: Simplygon.spRealArray, blend):
itemCount = targetArray.GetItemCount()
# Morph the geometry
for i in range(itemCount):
origValue = origArray.GetItem(i)
morphValue = morphArray.GetItem(i)
blendResult = morphValue * blend + origValue * (1.0-blend)
targetArray.SetItem(i, blendResult)
def MorphGeometry(originalSceneMesh: Simplygon.spSceneMesh, morphSceneMesh: Simplygon.spSceneMesh, blendSceneMesh: Simplygon.spSceneMesh, blend):
originalGeometry = originalSceneMesh.GetGeometry()
morphGeometry = morphSceneMesh.GetGeometry()
blendGeometry = blendSceneMesh.GetGeometry()
origArray = originalGeometry.GetCoords()
morphArray = morphGeometry.GetCoords()
targetArray = blendGeometry.GetCoords()
origVertexIdsArray = originalGeometry.GetVertexIds()
morphVertexIdsArray = morphGeometry.GetVertexIds()
targetVertexIdsArray = blendGeometry.GetVertexIds()
itemCount = targetVertexIdsArray.GetItemCount()
# For each corner:
for c in range(itemCount):
origVertexId = origVertexIdsArray.GetItem(c)
morphVertexId = morphVertexIdsArray.GetItem(c)
targetVertexId = targetVertexIdsArray.GetItem(c)
# For each x,z,y value in each coordinate:
for d in range(3):
dInt = d
origValue = origArray.GetItem(origVertexId * 3 + dInt)
morphValue = morphArray.GetItem(morphVertexId * 3 + dInt)
blendResult = morphValue * blend + origValue * (1.0-blend)
targetArray.SetItem(targetVertexId * 3 + dInt, blendResult)
# Morph the UVs
targetArray = blendGeometry.GetTexCoords(0)
origArray = originalGeometry.GetTexCoords(0)
morphArray = morphGeometry.GetTexCoords(0)
ArrayBlend(targetArray, origArray, morphArray, blend)
# Morph the Normals
targetArray = blendGeometry.GetNormals()
origArray = originalGeometry.GetNormals()
morphArray = morphGeometry.GetNormals()
ArrayBlend(targetArray, origArray, morphArray, blend)
def RunReductionWithGeomorph(sg: Simplygon.ISimplygon):
# Load scene to process.
print("Load scene to process.")
sgScene = LoadScene(sg, '../../../Assets/SimplygonMan/SimplygonMan.obj')
# Create the reduction processor.
sgReductionProcessor = sg.CreateReductionProcessor()
# Select Mesh Nodes
selectionSetId = sgScene.SelectNodes('ISceneMesh')
# Keep a copy of the original scene to be able to blend between this and the morph geometry.
originalScene = sgScene.NewCopy()
blendScene = sgScene.NewCopy()
sgReductionProcessor.SetScene( sgScene )
sgReductionSettings = sgReductionProcessor.GetReductionSettings()
# Set reduction target to triangle ratio with a ratio of 50%. Enable geomorph creation and
# disable symmetry quad retriangulator.
sgReductionSettings.SetReductionTargets( Simplygon.EStopCondition_All, True, False, False, False )
sgReductionSettings.SetReductionTargetTriangleRatio( 0.5 )
sgReductionSettings.SetCreateGeomorphGeometry( True )
sgReductionSettings.SetUseSymmetryQuadRetriangulator( False )
sgRepairSettings = sgReductionProcessor.GetRepairSettings()
# Disable TJunctionRemover.
sgRepairSettings.SetWeldDist( 0.0 )
sgRepairSettings.SetUseTJunctionRemover( False )
sgNormalCalculationSettings = sgReductionProcessor.GetNormalCalculationSettings()
# Replace normals.
sgNormalCalculationSettings.SetReplaceNormals( True )
sgNormalCalculationSettings.SetHardEdgeAngle( 60.0 )
# Start the reduction process.
print("Start the reduction process.")
sgReductionProcessor.RunProcessing()
set = sgScene.GetSelectionSetTable().GetSelectionSet(selectionSetId)
# Selection set to collect geomorph nodes in scene.
geomorphSelectionSet = sg.CreateSelectionSet()
# Morph the geometry
for blendValue in range(11):
geometryCount = set.GetItemCount()
# The LOD scene contains morphed geometries, placed as children of their respective reduced
# geometry mesh node.
# The name of a morph mesh node is the same as its parent, with "_geomorph" added.
for geomIndex in range(geometryCount):
guid = set.GetItem(geomIndex)
lodSceneNode = sgScene.GetNodeByGUID(guid)
originalSceneNode = originalScene.GetNodeByGUID(guid)
blendSceneNode = blendScene.GetNodeByGUID(guid)
lodSceneMesh = Simplygon.spSceneMesh.SafeCast(lodSceneNode)
originalSceneMesh = Simplygon.spSceneMesh.SafeCast(originalSceneNode)
blendSceneMesh = Simplygon.spSceneMesh.SafeCast(blendSceneNode)
childCount = lodSceneNode.GetChildCount()
for childIndex in range(childCount):
childNode = lodSceneNode.GetChild(childIndex)
childName = childNode.GetName()
childNodeString = ''.join([childName])
name = lodSceneNode.GetName()
geomorphName = ''.join(['_geomorph'])
if geomorphName in childNodeString:
morphSceneMesh = Simplygon.spSceneMesh.SafeCast(childNode)
morphGUID = morphSceneMesh.GetNodeGUID()
geomorphSelectionSet.AddItem(morphGUID)
MorphGeometry(originalSceneMesh, morphSceneMesh, blendSceneMesh, blendValue*0.1)
outputName = ''.join(['Geomorph_BlendedGeometry', str(blendValue), '.obj'])
# Save processed scene.
print("Save processed scene.")
SaveScene(sg, blendScene, outputName)
if __name__ == '__main__':
sg = simplygon_loader.init_simplygon()
if sg is None:
exit(Simplygon.GetLastInitializationError())
RunReductionWithGeomorph(sg)
sg = None
gc.collect()