Skip to content
On this page

Simple reduction - Scripting

In the following example we will continue expanding the script we wrote in Getting Started - Scripting to reduce the selected geometries using a ReductionPipeline with reduction ratio set to 50%.

A quick recap of what we did in 'Getting Started - Scripting' was a Unity C# script that exposes an entry point method as menu item accessible through the Unity menu. The entry point method checks if there is at least one selected item, and if so initializes Simplygon and passes on the simplygon handle as well as each GameObject to a place holder method.

The script below is the starting point of this example, we've renamed the menu item to 'SimplygonReductionScript' as well as the place holder method to Reduce. The place holder method is what we will extend to allow Simplygon to optimize selected geometries.

csharp
using Simplygon;
using Simplygon.Unity.EditorPlugin;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

public class SimpleReduction : MonoBehaviour
{
    [MenuItem("Simplygon/SimpleReductionScript")]
    static void EntryPoint()
    {
        if (Selection.objects.Length > 0)
        {
            using (ISimplygon simplygon = global::Simplygon.Loader.InitSimplygon
            (out EErrorCodes simplygonErrorCode, out string simplygonErrorMessage))
            {
                if (simplygonErrorCode == Simplygon.EErrorCodes.NoError)
                {
                    foreach (var o in Selection.objects)
                    {
                        Reduce(o as GameObject, simplygon);
                    }
                }
                else
                {
                    Debug.Log("Initializing failed!");
                }
            }
        }
    }

    public static void Reduce(GameObject gameObject, ISimplygon simplygon)
    {

    }
}

The Simplygon Unity plug-in is utilizing USD as intermediate file format between Unity and Simplygon, for the best experience we recommend using the same export and import method as the plug-in, to keep the behavior consistent. When the Simplygon Unity plug-in is installed we can access those methods.

  • Simplygon.Unity.EditorPlugin.SimplygonExporter.Export
  • Simplygon.Unity.EditorPlugin.SimplygonImporter.Import

As well as the SimplygonUtils class (optional).

  • Simplygon.Unity.EditorPlugin.SimplygonUtils

If we focus on the Reduce method we can see that we get a GameObject as well as Simplygon handle as input parameters. Next up is to export the currently selected (Unity) GameObject using SimplygonExporter.Export which will export the scene through USD to a temporary directory. This export call also returns the converted scene (Simplygon scene) which is required for most API calls.

The SimplygonExporter.Export takes the Simplygon handle, temporary export directory and list of GameObjects of which to export (including children). Let's try to add the export of the scene to the Reduce method.

The game objects need to be placed in a list, let's put them in a list. We also need to populate that export directory, this can be done manually, but we'll use SimplygonUtils.GetNewTempDirectory for this example. We should now be able to call the Export method.

csharp
public static void Reduce(GameObject gameObject, ISimplygon simplygon)
{
    List<GameObject> selectedGameObjects = new List<GameObject>();
    selectedGameObjects.Add(gameObject);

    string exportTempDirectory = SimplygonUtils.GetNewTempDirectory();

    using (spScene sgScene = SimplygonExporter.Export(simplygon, exportTempDirectory, selectedGameObjects))
    {

    }
}

Now that we have the Simplygon scene we need to describe what kind of optimization we want to do. For this example we will do a simple reduction of 50% of the original triangle count. Simplygon API provides Pipelines which encapsulates processing settings and execution functionality. To avoid pure low level API calls we will utilize the ReductionPipeline, it has all the reduction settings we are interested in and is fairly easy to configure.

Let's create a ReductionPipeline and fetch the ReductionSettings. These are the API settings for the ReductionProcessor, for more details of what each setting do, please visit the ReductionProcessor documentation.

If we look at what settings are available in ReductionSettings we can see that there indeed is a ReductionTargetTriangleRatio setting, set it to 0.5f for a 50% reduction. There is also a SetReductionTarget method which purpose is to set the condition of when to stop the optimization, this method is combining multiple API calls into one. See EStopCondition and SetReductionTargets for more details. For now we'll set the stop condition to 'All' and enable 'UseTriangleRatio' (first flag), the remaining flags should be set to false.

csharp
public static void Reduce(GameObject gameObject, ISimplygon simplygon)
{
    List<GameObject> selectedGameObjects = new List<GameObject>();
    selectedGameObjects.Add(gameObject);

    string exportTempDirectory = SimplygonUtils.GetNewTempDirectory();

    using (spScene sgScene = SimplygonExporter.Export(simplygon, exportTempDirectory, selectedGameObjects))
    {
        using (spReductionPipeline reductionPipeline = simplygon.CreateReductionPipeline())
        using (spReductionSettings reductionSettings = reductionPipeline.GetReductionSettings())
        {
            reductionSettings.SetReductionTargets(EStopCondition.All, true, false, false, false);
            reductionSettings.SetReductionTargetTriangleRatio(0.5f);
        }
    }
}

As the Pipeline is responsible for execution we can use it to optimize the scene for us, simply call reductionPipeline.RunScene with the scene as first argument, and the RunInThisProcess as second argument. See EPipelineRunMode for more execution options.

csharp
public static void Reduce(GameObject gameObject, ISimplygon simplygon)
{
    List<GameObject> selectedGameObjects = new List<GameObject>();
    selectedGameObjects.Add(gameObject);

    string exportTempDirectory = SimplygonUtils.GetNewTempDirectory();

    using (spScene sgScene = SimplygonExporter.Export(simplygon, exportTempDirectory, selectedGameObjects))
    {
        using (spReductionPipeline reductionPipeline = simplygon.CreateReductionPipeline())
        using (spReductionSettings reductionSettings = reductionPipeline.GetReductionSettings())
        {
            reductionSettings.SetReductionTargets(EStopCondition.All, true, false, false, false);
            reductionSettings.SetReductionTargetTriangleRatio(0.5f);

            reductionPipeline.RunScene(sgScene, EPipelineRunMode.RunInThisProcess);

        }
    }
}

When the optimization has completed the pipeline will hold the optimized scene(s). To directly import the results stored in the pipeline into Unity, lets utilize SimplygonImporter.Import. The arguments for Import are the Simplygon handle, the pipeline with the optimized scene(s), initial LOD index, (Unity) asset folder path and the name of imported object once inside Unity.

For the import path we will simply use 'AssetDatabase' as it will handle name clashes and indexing. We'll place the result in the project's 'Asset/SimpleReductions' folder, as well as use the original selected node name (SimplygonImporter.Import will append '_LODX' prefix).

csharp
public static void Reduce(GameObject gameObject, ISimplygon simplygon)
{
    List<GameObject> selectedGameObjects = new List<GameObject>();
    selectedGameObjects.Add(gameObject);

    string exportTempDirectory = SimplygonUtils.GetNewTempDirectory();

    using (spScene sgScene = SimplygonExporter.Export(simplygon, exportTempDirectory, selectedGameObjects))
    {
        using (spReductionPipeline reductionPipeline = simplygon.CreateReductionPipeline())
        using (spReductionSettings reductionSettings = reductionPipeline.GetReductionSettings())
        {
            reductionSettings.SetReductionTargets(EStopCondition.All, true, false, false, false);
            reductionSettings.SetReductionTargetTriangleRatio(0.5f);

            reductionPipeline.RunScene(sgScene, EPipelineRunMode.RunInThisProcess);

            string baseFolder = "Assets/SimpleReductions";
            if (!AssetDatabase.IsValidFolder(baseFolder))
            {
                AssetDatabase.CreateFolder("Assets", "SimpleReductions");
            }

            string assetFolderGuid = AssetDatabase.CreateFolder(baseFolder, gameObject.name);
            string assetFolderPath = AssetDatabase.GUIDToAssetPath(assetFolderGuid);

            int startingLodIndex = 0;
            SimplygonImporter.Import(simplygon, reductionPipeline, ref startingLodIndex, 
                assetFolderPath,gameObject.name);
        }
    }
}

That is it, let's go back to Unity, import an asset, select the object(s) and run the script. In this example we will use this city block, a complex asset with many meshes, materials and textures,

Original asset

Once the optimization has completed the result will get imported back to the scene. See the result in the image below, the original asset to the left, the optimized asset to the right.

Optimized asset

If possible, try this example with various pipelines such as Aggregation and Remeshing, with and without material baking.