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