Creating a physics mesh with Simplygon

Disclaimer: The code in this post is written using version 9.0.9500 of Simplygon. Should you encounter this post running a different version, some of the API calls might differ. However, the core concepts should still remain valid.

Introduction

When creating physics meshes, it's typically a good idea to optimize the original heavily, to make calculations cheaper. You would also want to keep material information in the optimized object, in order to spawn appropriate effects, play correct sounds etc. Combining the vertex color caster and remeshing is a great starting point to achieve super-simple geometry that checks the boxes for good physics meshes.

Example asset

For our example we wil generate a physics mesh for this asset.

Gold mine

It consists of a large number of objects and has some rock, metal and wood materials that we like to keep separated in the result.

Starting the script

We will be working in Simplygon's Maya integration to generate the physics mesh. Our goal is to get rid of all materials and rely on vertex colors. Although texture are more precise, using vertex colors means that we save data and don't need to do texture look ups. We will call our script from Maya with this command.

execfile(<path to our script file>)

Here's the skeleton for our script:

from simplygon import simplygon_loader
from simplygon import Simplygon
import maya.cmds as cmds
import os

tmp_file = "c:/tmp/_export.sb"

# Export the current selected objects into a Simplygon scene
def export_selection(sg):
    cmds.Simplygon(exp = tmp_file)
    scene = sg.CreateScene()
    scene.LoadFromFile(tmp_file)
    return scene

# Import the Simplygon scene into Maya
def import_results(scene):
    scene.SaveToFile(tmp_file)
    cmds.Simplygon(imp=tmp_file)
    os.remove(tmp_file)

# Starting point for the processing
def process_selection(sg):
    scene = export_selection(sg)
    optimized_scene = create_physics_proxy(sg, scene)
    import_results(optimized_scene)

def main():
    sg = simplygon_loader.init_simplygon()
    print(sg.GetVersion())
    process_selection(sg)
    del sg

if __name__== "__main__":
    main()

We will be utilizing the Simplygon command in Maya to export the selection into a scene, and also when taking the results back into Maya.

Converting materials to vertex colors

For our example we will be using a very simplistic heuristic. We'll create a simple lookup table that uses the material name to decide what property it has. This can easily be replaced with something that looks into the material properties in Maya. Here is our lookup table:

material_lookup = [{"name_contains":"metal", "color":[1.0,0.0,0.0,1.0]}, 
                   {"name_contains":"rock", "color":[0.0,1.0,0.0,1.0]}, 
                   {"name_contains":"wood", "color":[0.0,0.0,1.0,1.0]}]

All metallic parts will be colored red, rock is green and finally wood will be set to blue.

We will add a channel to all materials in the scene where we set a color node that we can use when casting the materials later. This channel is important for the whole script, so we'll define that at the top.

channel_name = "physics_attribute"

Here is the function that does the job:

def create_physics_attribute_shading_nodes(sg, material_table):
    for mat_id in range(0, material_table.GetMaterialsCount()):
        material = material_table.GetMaterial(mat_id)
        material_name = material.GetName()
        for mat_to_color in material_lookup:
            if mat_to_color["name_contains"] in material_name:
                # We've found the color to map this material to
                color = mat_to_color["color"]
                # Add a new channel to the material
                material.AddMaterialChannel(channel_name)
                # Hook a color shading node into the new channel 
                # and set the color
                shading_node = sg.CreateShadingColorNode()
                shading_node.SetColor(color[0],color[1],color[2],color[3])
                material.SetShadingNetwork(channel_name, shading_node)

We use the material table in the Simplygon scene to loop through all materials. For each one we add a channel that we connect a color shading node to.

Creating a vertex color caster

The casting of the material attributes to the new mesh will be handled by a vertex color caster. Let's define a function to set that up:

# Create a vertex color caster that casts the physics attribute to the mesh
def create_physics_attribute_caster(sg, scene):
    vc_caster = sg.CreateVertexColorCaster()
    vc_caster.SetScene(scene)
    vc_caster_settings = vc_caster.GetVertexColorCasterSettings()
    vc_caster_settings.SetMaterialChannel(channel_name)
    vc_caster_settings.SetOutputColorName(channel_name)
    return vc_caster

By setting the material channel and output color to channel_name we instruct the caster to transfer the material property we defined above into a vertex color channel. They don't have to be called the same though.

Tying it all together

The bulk of the work is done. Now we just need to create a remeshing pipeline and run it. Here's the function to create that:

# Create a remeshing pipeline
def create_remeshing_pipeline(sg):
    remeshing_pipeline = sg.CreateRemeshingPipeline()
    remesher_settings = remeshing_pipeline.GetRemeshingSettings()
    remesher_settings.SetOnScreenSize(300)
    mapping_image_settings = remeshing_pipeline.GetMappingImageSettings()
    mapping_image_settings.SetGenerateMappingImage(True)
    output_material_settings = mapping_image_settings.GetOutputMaterialSettings(0)
    output_material_settings.SetTextureHeight(1024)
    output_material_settings.SetTextureWidth(1024)
    return remeshing_pipeline

With that in place we can finally set up the function that orchestrates the process. Here it is:

# Function that sets up the hollow shell processing.
def create_physics_proxy(sg, scene): 
    # Map physics attributes to colors
    create_physics_attribute_shading_nodes(sg, scene.GetMaterialTable())
    # Create a remeshing pipline
    remeshing_pipeline = create_remeshing_pipeline(sg)
    # Create a vertex color caster to map the attributes
    physics_attribute_caster = create_physics_attribute_caster(sg, scene)
    # Add it to the pipeline
    remeshing_pipeline.AddMaterialCaster(physics_attribute_caster, 0)
    # Run the process
    remeshing_pipeline.RunScene(scene, Simplygon.EPipelineRunMode_RunInThisProcess)        
    return scene

The results

Now we can run the script in Maya using the execfile command mentioned earlier in this post. Here is the output from the process:

Physics mesh

And that's how you can create a super cheap physics mesh with Simplygon.

The script

Here is the complete script.

# Copyright (c) Microsoft Corporation. 
# Licensed under the MIT license. 
 
from simplygon import simplygon_loader
from simplygon import Simplygon
import maya.cmds as cmds
import os

channel_name = "physics_color"
material_lookup = [{"name_contains":"metal", "color":[1.0,0.0,0.0,1.0]}, 
                   {"name_contains":"rock", "color":[0.0,1.0,0.0,1.0]}, 
                   {"name_contains":"wood", "color":[0.0,0.0,1.0,1.0]}]
tmp_file = "c:/tmp/_export.sb"

# Adds a shading node to all materials in the scene with the color specified in the lookup table. 
def create_physics_attribute_shading_nodes(sg, material_table):
    for mat_id in range(0, material_table.GetMaterialsCount()):
        material = material_table.GetMaterial(mat_id)
        material_name = material.GetName()
        for mat_to_color in material_lookup:
            if mat_to_color["name_contains"] in material_name:
                # We've found the color to map this material to
                color = mat_to_color["color"]
                # Add a new channel to the material
                material.AddMaterialChannel(channel_name)
                # Hook a color shading node into the new channel
                # and set the color
                shading_node = sg.CreateShadingColorNode()
                shading_node.SetColor(color[0],color[1],color[2],color[3])
                material.SetShadingNetwork(channel_name, shading_node)

# Creates a vertex color caster that will cast the physics attribute to the mesh
def create_physics_attribute_caster(sg, scene):
    vc_caster = sg.CreateVertexColorCaster()
    vc_caster.SetScene(scene)
    vc_caster_settings = vc_caster.GetVertexColorCasterSettings()
    vc_caster_settings.SetMaterialChannel(channel_name)
    vc_caster_settings.SetOutputColorName(channel_name)
    return vc_caster

# Create a remeshing pipeline
def create_remeshing_pipeline(sg):
    remeshing_pipeline = sg.CreateRemeshingPipeline()
    remesher_settings = remeshing_pipeline.GetRemeshingSettings()
    remesher_settings.SetOnScreenSize(300)
    mapping_image_settings = remeshing_pipeline.GetMappingImageSettings()
    mapping_image_settings.SetGenerateMappingImage(True)
    output_material_settings = mapping_image_settings.GetOutputMaterialSettings(0)
    output_material_settings.SetTextureHeight(1024)
    output_material_settings.SetTextureWidth(1024)
    return remeshing_pipeline

# Function that sets up the hollow shell processing.
def create_physics_proxy(sg, scene): 
    # Map physics attributes to colors
    create_physics_attribute_shading_nodes(sg, scene.GetMaterialTable())
    # Create a remeshing pipline
    remeshing_pipeline = create_remeshing_pipeline(sg)
    # Create a vertex color caster to map the attributes
    physics_attribute_caster = create_physics_attribute_caster(sg, scene)
    # Add it to the pipeline
    remeshing_pipeline.AddMaterialCaster(physics_attribute_caster, 0)
    # Run the process
    remeshing_pipeline.RunScene(scene, Simplygon.EPipelineRunMode_RunInThisProcess)        
    return scene

# Export the current selected objects into a Simplygon scene
def export_selection(sg):
    cmds.Simplygon(exp = tmp_file)
    scene = sg.CreateScene()
    scene.LoadFromFile(tmp_file)
    return scene

# Import the Simplygon scene into Maya
def import_results(scene):
    scene.SaveToFile(tmp_file)
    cmds.Simplygon(imp=tmp_file)
    os.remove(tmp_file)

# Starting point for the processing
def process_selection(sg):
    scene = export_selection(sg)
    optimized_scene = create_physics_proxy(sg, scene)
    import_results(optimized_scene)


def main():
    sg = simplygon_loader.init_simplygon()
    print(sg.GetVersion())
    process_selection(sg)
    del sg

if __name__== "__main__":
    main()
⇐ Back to all posts

Request 30-days free evaluation license

*
*
*
*
Industry
*

Request 30-days free evaluation license

*
*
*
*
Industry
*