Skip to content

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()