Simple remeshing with material baking - Scripting
In the following example we will continue expanding the script we wrote in Getting Started - Scripting to optimize the selected geometries using a RemeshingPipeline. Remeshing means that the optimized asset will be a completely new mesh with no ties to the original asset. The RemeshingPipeline allows various reduction targets, in this example we will generate an optimized mesh for a specific OnScreenSize of 300 pixels. The output will be one mesh that represents all input meshes. We will also enable material baking for the RemeshingPipeline, which means that the optimized asset will get one baked material for the entire mesh, one uv-set, and one texture per baked material channel.
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.
The script below is the starting point of this example, we've renamed the menu item to 'SimplygonRemeshingScript'.
using Simplygon;
using Simplygon.Unity.EditorPlugin;
using System.Linq;
using UnityEditor;
using UnityEngine;
public class SimpleRemeshing
{
[MenuItem("Simplygon/SimpleRemeshingScript")]
static void EntryPoint()
{
if (Selection.gameObjects.Length > 0)
{
using (ISimplygon simplygon = global::Simplygon.Loader.InitSimplygon
(out EErrorCodes simplygonErrorCode, out string simplygonErrorMessage))
{
// if Simplygon handle is valid
if (simplygonErrorCode == Simplygon.EErrorCodes.NoError)
{
// Call Simplygon here
}
// if invalid handle, output error message to the Unity console
else
{
Debug.LogError("Simplygon initializing failed!");
}
}
}
}
}
Now that we have Simplygon initialized we need to describe what kind of optimization we want to do. In this example we will set up a remeshing with a target OnScreenSize of 300 px. We will also set up material baking with two material channels, albedo and normal (see Unity Rendering Pipelines to Simplygon). The Simplygon API provides Pipelines which encapsulates processing settings and execution functionality. To avoid pure low level API calls we will utilize the RemeshingPipeline, it has all the settings we are interested in and is fairly easy to configure.
Let's create a RemeshingPipeline and fetch the RemeshingSettings. These are the API settings for the RemeshingProcessor, for more details of what each setting do, please visit the RemeshingProcessor documentation.
using Simplygon;
using Simplygon.SPL.v80.Settings;
using Simplygon.Unity.EditorPlugin;
using System.Linq;
using UnityEditor;
using UnityEngine;
public class SimpleRemeshing
{
[MenuItem("Simplygon/SimpleRemeshingScript")]
static void EntryPoint()
{
if (Selection.gameObjects.Length > 0)
{
using (ISimplygon simplygon = global::Simplygon.Loader.InitSimplygon
(out EErrorCodes simplygonErrorCode, out string simplygonErrorMessage))
{
// if Simplygon handle is valid
if (simplygonErrorCode == Simplygon.EErrorCodes.NoError)
{
// Create a remeshing pipeline
using var simplygonRemeshingPipeline = simplygon.CreateRemeshingPipeline();
// Set on screen size to 300 pixels.
using var simplygonRemeshingSettings = simplygonRemeshingPipeline.GetRemeshingSettings();
simplygonRemeshingSettings.SetOnScreenSize(300);
}
// if invalid handle, output error message to the Unity console
else
{
Debug.LogError("Simplygon initializing failed!");
}
}
}
}
}
For the material baking part we need to specify some settings for the output, that Simplygon should generate UVs as well as apply new material ids. This is achieved through the pipeline's MappingImageSettings.
To specify the texture dimensions for the output material we need to fetch the material settings for the output material in mapping image settings. Set the Width and Height to 512.
using Simplygon;
using Simplygon.Unity.EditorPlugin;
using System.Linq;
using UnityEditor;
using UnityEngine;
public class SimpleRemeshing
{
[MenuItem("Simplygon/SimpleRemeshingScript")]
static void EntryPoint()
{
if (Selection.gameObjects.Length > 0)
{
using (ISimplygon simplygon = global::Simplygon.Loader.InitSimplygon
(out EErrorCodes simplygonErrorCode, out string simplygonErrorMessage))
{
// if Simplygon handle is valid
if (simplygonErrorCode == Simplygon.EErrorCodes.NoError)
{
// Create a remeshing pipeline
using var simplygonRemeshingPipeline = simplygon.CreateRemeshingPipeline();
// Set on screen size to 300 pixels.
using var simplygonRemeshingSettings = simplygonRemeshingPipeline.GetRemeshingSettings();
simplygonRemeshingSettings.SetOnScreenSize(300);
// Prepare mapping image for material casting.
using var simplygonMappingImageSettings = simplygonRemeshingPipeline.GetMappingImageSettings();
simplygonMappingImageSettings.SetGenerateMappingImage(true);
simplygonMappingImageSettings.SetGenerateTexCoords(true);
simplygonMappingImageSettings.SetGenerateTangents(true);
simplygonMappingImageSettings.SetUseFullRetexturing(true);
simplygonMappingImageSettings.SetApplyNewMaterialIds(true);
// Set output texture size to 512x512
using var simplygonOutputMaterialSettings = simplygonMappingImageSettings.GetOutputMaterialSettings(0);
simplygonOutputMaterialSettings.SetTextureHeight(512);
simplygonOutputMaterialSettings.SetTextureWidth(512);
}
// if invalid handle, output error message to the Unity console
else
{
Debug.LogError("Simplygon initializing failed!");
}
}
}
}
}
Now we need to create two compute casters, one for the Albedo channel and one for Normals and add them to the remeshing pipeline.
using Simplygon;
using Simplygon.Unity.EditorPlugin;
using System.Linq;
using UnityEditor;
using UnityEngine;
public class SimpleRemeshing
{
[MenuItem("Simplygon/SimpleRemeshingScript")]
static void EntryPoint()
{
if (Selection.gameObjects.Length > 0)
{
using (ISimplygon simplygon = global::Simplygon.Loader.InitSimplygon
(out EErrorCodes simplygonErrorCode, out string simplygonErrorMessage))
{
// if Simplygon handle is valid
if (simplygonErrorCode == Simplygon.EErrorCodes.NoError)
{
// Create a remeshing pipeline
using var simplygonRemeshingPipeline = simplygon.CreateRemeshingPipeline();
// Set on screen size to 300 pixels.
using var simplygonRemeshingSettings = simplygonRemeshingPipeline.GetRemeshingSettings();
simplygonRemeshingSettings.SetOnScreenSize(300);
// Prepare mapping image for material casting.
using var simplygonMappingImageSettings = simplygonRemeshingPipeline.GetMappingImageSettings();
simplygonMappingImageSettings.SetGenerateMappingImage(true);
simplygonMappingImageSettings.SetGenerateTexCoords(true);
simplygonMappingImageSettings.SetGenerateTangents(true);
simplygonMappingImageSettings.SetUseFullRetexturing(true);
simplygonMappingImageSettings.SetApplyNewMaterialIds(true);
// Set output texture size to 512x512
using var simplygonOutputMaterialSettings = simplygonMappingImageSettings.GetOutputMaterialSettings(0);
simplygonOutputMaterialSettings.SetTextureHeight(512);
simplygonOutputMaterialSettings.SetTextureWidth(512);
// Add a caster for the albedo map channel.
using var albedoMapCaster = simplygon.CreateComputeCaster();
using var albedoMapCasterSettings = albedoMapCaster.GetComputeCasterSettings();
albedoMapCasterSettings.SetMaterialChannel("AlbedoMap");
simplygonRemeshingPipeline.AddMaterialCaster(albedoMapCaster, 0);
// Add a caster for normals.
using var normalsCaster = simplygon.CreateComputeCaster();
using var normalsCasterSettings = normalsCaster.GetComputeCasterSettings();
normalsCasterSettings.SetMaterialChannel("NormalMap");
simplygonRemeshingPipeline.AddMaterialCaster(normalsCaster, 0);
}
// if invalid handle, output error message to the Unity console
else
{
Debug.LogError("Simplygon initializing failed!");
}
}
}
}
}
To execute the Pipeline in Unity we'll use the SimplygonProcessing class. Simply call simplygonProcessing.Run with the Simplygon object as first argument, the selected game objects as second argument, the RunInThisProcess as third argument, and a boolean specificy if you want the newly create LOD to be instantiated in the scene.
See EPipelineRunMode for more execution options.
using Simplygon;
using Simplygon.Unity.EditorPlugin;
using System.Linq;
using UnityEditor;
using UnityEngine;
public class SimpleRemeshing
{
[MenuItem("Simplygon/SimpleRemeshingScript")]
static void EntryPoint()
{
if (Selection.gameObjects.Length > 0)
{
using (ISimplygon simplygon = global::Simplygon.Loader.InitSimplygon
(out EErrorCodes simplygonErrorCode, out string simplygonErrorMessage))
{
// if Simplygon handle is valid
if (simplygonErrorCode == Simplygon.EErrorCodes.NoError)
{
// Create a remeshing pipeline
using var simplygonRemeshingPipeline = simplygon.CreateRemeshingPipeline();
// Set on screen size to 300 pixels.
using var simplygonRemeshingSettings = simplygonRemeshingPipeline.GetRemeshingSettings();
simplygonRemeshingSettings.SetOnScreenSize(300);
// Prepare mapping image for material casting.
using var simplygonMappingImageSettings = simplygonRemeshingPipeline.GetMappingImageSettings();
simplygonMappingImageSettings.SetGenerateMappingImage(true);
simplygonMappingImageSettings.SetGenerateTexCoords(true);
simplygonMappingImageSettings.SetGenerateTangents(true);
simplygonMappingImageSettings.SetUseFullRetexturing(true);
simplygonMappingImageSettings.SetApplyNewMaterialIds(true);
// Set output texture size to 512x512
using var simplygonOutputMaterialSettings = simplygonMappingImageSettings.GetOutputMaterialSettings(0);
simplygonOutputMaterialSettings.SetTextureHeight(512);
simplygonOutputMaterialSettings.SetTextureWidth(512);
// Add a caster for the albedo map channel.
using var albedoMapCaster = simplygon.CreateComputeCaster();
using var albedoMapCasterSettings = albedoMapCaster.GetComputeCasterSettings();
albedoMapCasterSettings.SetMaterialChannel("AlbedoMap");
simplygonRemeshingPipeline.AddMaterialCaster(albedoMapCaster, 0);
// Add a caster for normals.
using var normalsCaster = simplygon.CreateComputeCaster();
using var normalsCasterSettings = normalsCaster.GetComputeCasterSettings();
normalsCasterSettings.SetMaterialChannel("NormalMap");
simplygonRemeshingPipeline.AddMaterialCaster(normalsCaster, 0);
// Run Simplygon processing.
var simplygonProcessing = new SimplygonProcessing();
simplygonProcessing.Run(simplygon, simplygonRemeshingPipeline, Selection.gameObjects.ToList(), EPipelineRunMode.RunInThisProcess, true);
}
// if invalid handle, output error message to the Unity console
else
{
Debug.LogError("Simplygon initializing failed!");
}
}
}
}
}
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,
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. Keep in mind that the optimized asset is generated to be displayed at a size of 300 pixels.
Top-down wireframe.
Top-down overdraw, yellow / red-ish is more, black is less.
If possible, try this example with various pipelines such as Reduction and Aggregation, with and without material baking.