Introduction to micro-meshes for mesh compression

Written by Jesper Tingvall, Product Expert, Simplygon

Disclaimer: The code in this post is written using version 10.2.5200.0 of Simplygon. If you encounter this post at a later stage, some of the API calls might have changed. However, the core concepts should still remain valid.

Introduction

In this blog we will cover how to generate meshes using NVIDIAs new micro-mesh techology with Simplygon. We will create a batch processor for kit-bashed 3d scanned content intented to save disc space and optimize render performance.

This blog is related to Accelerated remeshing using tessellated attributes. That blog was written for an older version of Simplygon where displacement casting was not done automatically during remeshing. It also did not cover micro-meshes.

Prerequisites

This example will use the Simplygon Python API. Exporting tesselated attributes as *.bary files is currently an API only feature.

To get the most out of this blog you should have an understanding of what micro-meshes are. We will use NVIDIA Displacement Micro-Map Toolkit to view the output data.

Problem to solve

The assets we will optimize is from a church pack. We will just have a look at a subset of the assets. The assets uses two 4k textures; base color and normal map.

4 video games assets.

From left to right; Church_Wall_008, Church_Wall_007, Church_Wall_002 with Church_Door_Var01.

The original files are saved as fbx. But in order to not compare apples to oranges lets convert them to glb when comparing file size to optimized assets.

Model Triangle count .glb file size (excluding textures)
Church_Door_Var01 402 k 17 MB
Church_Wall_002 424 k 19 MB
Church_Wall_007 621 k 32 MB
Church_Wall_008 688 k 34 MB

If we look on the inside of the meshes we can see that a lot of geometry on the hiding in there. Geometry on the insides as well as a unconnected outside shell makes these assets a prime candidate for remeshing.

Asset with faces on inside.

Use cases for NVIDIA micro-meshes

Before we delve into solution to our problem and let us briefly talk about NVIDIA micro-meshes.

Micro-meshes is a very memory efficient way of storing displacement data that is then applied to a base mesh. While it is aimed at NVIDIA GeForce RTX 40 Series GPUs that has hardware acceleration for this technique, it is open source and cross platform. It can be used both for ray tracing but also traditional rasterization.

It can either be used in the rendering pipeline or as just a way of storing memory on disc for very dense meshes. Upon loading the meshes into the game the meshes can be tessellated and rendered using any traditional render pipeline. We can specify the level of tessellation upon loading. That means we could potentially use the same micro-mesh data at different tessellation for different LOD levels, and use the low poly base mesh for distant LODs.

The cost of storing a micro-mesh triangle is around 6-1 bits depending on asset and tessellation level. As we only store displacement data for micro-meshes we get better compression then other storage techniques for dense geometries which stores coordinates, UVs and normals for all triangles.

As we do not store normals per micro-mesh triangle it is good to use micro-meshes in conjunction with a high resolution normal map. This enables us to have a less dense base mesh and correct the normals with the texture.

Solution

We are going to use a number of concepts in the solution. First we are going to use the remeshing pipeline which creates a new watertight mesh from the input. This is a good way of cleaning up assets that has lots of faces on the inside, or holes which makes reduction tricky. To the remeshing we are going to apply attribute tessellation. This allows us to store data more frequently then per vertex. In our case we are going to store displacement data. That allows us to have a fairly low poly base mesh and to it apply tessellation to get more details. Lastly we are going to save the attribute tessellation data with the micro-mesh format *.bary. Bary files are a very efficient way of storing displacement data.

It is possible to use tessellated attributes for all pipelines in Simplygon. In most cases you need to use a displacement caster to cast the data into tessellation. In the case of remeshing one of the new features in Simplygon 10.2 is that this is done automatically.

It is important to point out that attribute tessellation is something internal in Simplygon and does not only work with NVIDIA micro-meshes. You can implement your own exporter for the tessellation data, or apply tessellation before saving the file to disc.

Screen size as tessellation density mode

Let us start by using screen size to determine when to tessellate a triangle. First let us create a standard remeshing pipeline and setup mapping image used for material casting as well as setting a target screen size.

def create_remeshing_pipeline_screen_size(sg, screen_size, tesselation_screen_size):
    sgRemeshingProcessor = sg.CreateRemeshingPipeline()
    
    sgRemeshingSettings = sgRemeshingProcessor.GetRemeshingSettings()
    mapping_image_settings = sgRemeshingProcessor.GetMappingImageSettings()
    material_output_settings = mapping_image_settings.GetOutputMaterialSettings(0)

    # Set quality of material casting.
    material_output_settings.SetTextureHeight(TEXTURE_SIZE)
    material_output_settings.SetTextureWidth(TEXTURE_SIZE)
    mapping_image_settings.SetGenerateMappingImage(True)
    mapping_image_settings.SetTexCoordName("MaterialLOD")

    # Set quality of remeshing.
    sgRemeshingSettings.SetOnScreenSize( screen_size ) 
    sgRemeshingSettings.SetSurfaceTransferMode( Simplygon.ESurfaceTransferMode_Accurate )
    sgRemeshingSettings.SetHoleFilling(Simplygon.EHoleFilling_Low)

Now comes the interesting part. First we'll set so the remeshed automatically cast displacement into tessellated attributes via PopulateAttributeTessellationDisplacement. We also need to enable attribute tessellation via EnableAttributeTessellation on the remesh pipeline's AttributeTessellationSettings.

With those settings enabled we can set options for our tessellated attributes. As AttributeTessellationDensityMode we are going to pick OnScreenSize. This will make the tessellated triangle take up roughly one pixel at the given OnScreenSize. This tessellation density mode is good for when you want to control triangle density per pixel on screen.

We are also setting OnlyAllowOneLevelOfDifference to True. This is required for exporting tessellated attributes into a *.bary file. This setting tells the processor to only allow one level of tessellation difference from one base triangle to its neighboring triangles. Larger differences would make the topology not connect, as micro-meshes can only handle one level of difference.

    # Populate the attribute tesselation with displacement during remeshing.
    sgRemeshingSettings.SetPopulateAttributeTessellationDisplacement( True )

    sgAttributeTessellationSettings = sgRemeshingProcessor.GetAttributeTessellationSettings()
    
    # Attribute tessellation settings
    sgAttributeTessellationSettings.SetEnableAttributeTessellation( True )
    sgAttributeTessellationSettings.SetAttributeTessellationDensityMode( Simplygon.EAttributeTessellationDensityMode_OnScreenSize )
    sgAttributeTessellationSettings.SetOnScreenSize( tesselation_screen_size )
    sgAttributeTessellationSettings.SetOnlyAllowOneLevelOfDifference( True )

    return sgRemeshingProcessor

Save micro-mesh

When saving the file as glTF data saved in tessellated attributes will automatically be saved in a .bary file in same output folder. The glTF file contains the base mesh and the .bary file contains the attribute tessellation displacement data.

def export_output(sg, scene, path):
    exporter = sg.CreateSceneExporter()
    exporter.SetScene(scene)
    print("Saving " + path)
    exporter.SetExportFilePath( path )
    exporter.Run()

Let us process one of the meshes with our remesher function and save to disc. We process SM_Church_Wall_008 at a remeshing screen size of 500 pixels and tessellation density mode of 500 pixels.

The result is saved as SM_Church_Wall_008_micromesh.glb. Since our mesh now contains displacement data in tessellated attributes we also get a SM_Church_Wall_008_micromesh_displacement0.bary. Let us load up SM_Church_Wall_008_micromesh.glb in micromesh_toolbox so we can inspect the result. The first image is how the base model look, and second is the base model with tessellation applied.

Castle wall

Base mesh - A remeshing of SM_Church_Wall_008 at 500 pixels.

High density castle wall

Micro-mesh - A remeshing of SM_Church_Wall_008 at 500 pixels tesselllated with 500 pixels screen size as target.

Absolute length as tessellation density mode

Another way to define the quality of our remeshing is to think about it in terms of deviation. As we want to set these attributes in metrics we have introduced a global constant for scene scale.

SCENE_SCALE = 100

First we are going to introduce a helper function for our remeshing pipeline which calculates a max deviation from original mesh to screen size.

def MaxDeviationToScreenSize(maxDeviation, sgScene):
    sceneDiameter = sgScene.GetRadius() * 2
    screenSize = int(sceneDiameter / maxDeviation)
    return screenSize

As above let us create a remeshing pipeline, but use the calculated screen size as OnScreenSize target, as well as taking SCENE_SCALE into account.

def create_remeshing_pipeline_deviation_length(sg, scene, max_deviation, tesselation_length):
    sgRemeshingProcessor = sg.CreateRemeshingPipeline()
    
    sgRemeshingSettings = sgRemeshingProcessor.GetRemeshingSettings()
    mapping_image_settings = sgRemeshingProcessor.GetMappingImageSettings()
    material_output_settings = mapping_image_settings.GetOutputMaterialSettings(0)

    # Set quality of material casting.
    material_output_settings.SetTextureHeight(TEXTURE_SIZE)
    material_output_settings.SetTextureWidth(TEXTURE_SIZE)
    mapping_image_settings.SetGenerateMappingImage(True)
    mapping_image_settings.SetTexCoordName("MaterialLOD")
    
    # Set quality of remeshing.
    sgRemeshingSettings.SetOnScreenSize( MaxDeviationToScreenSize(max_deviation * SCENE_SCALE, scene)  ) 
    sgRemeshingSettings.SetHoleFilling(Simplygon.EHoleFilling_Low)
    sgRemeshingSettings.SetSurfaceTransferMode( Simplygon.ESurfaceTransferMode_Accurate )

Now we enable attribute tessellation and set AttributeTessellationDensityMode to AbsoluteLength. In this mode we control the tessellation amount by setting a max length of the tessellated triangles edges. We can specify this via SetMaxLengthOfTessellatedValue. We also need to take SCENE_SCALE into account here. In this mode one thing to look out for is that more slivery triangles will be relatively more tessellated than equal area isotropic triangles, since their length to area ratio is higher.

It is worth pointing out that we can only use one mode to control tessellation density. So we can not for example use a combination of screen size and max length.

    # Populate the attribute tesselation with displacement during remeshing.
    sgRemeshingSettings.SetPopulateAttributeTessellationDisplacement( True )
    sgAttributeTessellationSettings = sgRemeshingProcessor.GetAttributeTessellationSettings()
    sgAttributeTessellationSettings.SetEnableAttributeTessellation( True )

    # Attribute tessellation settings
    sgAttributeTessellationSettings.SetAttributeTessellationDensityMode( Simplygon.EAttributeTessellationDensityMode_AbsoluteLength )
    sgAttributeTessellationSettings.SetMaxLengthOfTessellatedValue( tesselation_length * SCENE_SCALE)
    sgAttributeTessellationSettings.SetOnlyAllowOneLevelOfDifference( True )

    return sgRemeshingProcessor

Let us process SM_Church_Wall_008 with max deviation of 5 mm and a max triangle length of 1 mm. We get a very dense asset of 33 M triangles. But the total geometry data only takes up 20 MB.

Very high density castle wall

Absolute area as tessellation density mode

Lastly we will look at absolute area as tessellation density mode. The first section is identical to absolute length part. We create a remeshing pipeline and use max deviation to control it.

def create_remeshing_pipeline_deviation_area(sg, scene, max_deviation, tesselation_area):
    sgRemeshingProcessor = sg.CreateRemeshingPipeline()
    
    sgRemeshingSettings = sgRemeshingProcessor.GetRemeshingSettings()
    mapping_image_settings = sgRemeshingProcessor.GetMappingImageSettings()
    material_output_settings = mapping_image_settings.GetOutputMaterialSettings(0)

    # Set quality of material casting.
    material_output_settings.SetTextureHeight(TEXTURE_SIZE)
    material_output_settings.SetTextureWidth(TEXTURE_SIZE)
    mapping_image_settings.SetGenerateMappingImage(True)
    mapping_image_settings.SetTexCoordName("MaterialLOD")

    # Set quality of remeshing.
    sgRemeshingSettings.SetOnScreenSize( MaxDeviationToScreenSize(max_deviation * SCENE_SCALE, scene)  ) 
    sgRemeshingSettings.SetHoleFilling(Simplygon.EHoleFilling_Low)
    sgRemeshingSettings.SetSurfaceTransferMode( Simplygon.ESurfaceTransferMode_Accurate )
    
    # Populate the attribute tesselation with displacement during remeshing.
    sgRemeshingSettings.SetPopulateAttributeTessellationDisplacement( True )
    sgAttributeTessellationSettings = sgRemeshingProcessor.GetAttributeTessellationSettings()
    sgAttributeTessellationSettings.SetEnableAttributeTessellation( True )

We set AttributeTessellationDensityMode to AbsoluteArea. In this mode the area our tessellated triangle covers determine the tessellation level. We can specify maximum area it can cover with MaxAreaOfTessellatedValue. As we are calculating area we need to apply the SCENE_SCALE twice to get it in correct metric format.

With mode set to absolute area we can not directly control the length of our tessellated edges directly, but we get the benefit of slivery triangles will not be as tessellated as with absolute length in example above.

    # Attribute tessellation settings
    sgAttributeTessellationSettings.SetAttributeTessellationDensityMode( Simplygon.EAttributeTessellationDensityMode_AbsoluteArea )
    sgAttributeTessellationSettings.SetMaxAreaOfTessellatedValue( tesselation_area * SCENE_SCALE * SCENE_SCALE)
    sgAttributeTessellationSettings.SetOnlyAllowOneLevelOfDifference( True )

    return sgRemeshingProcessor

To get around the same quality as absolute length we can use the triangle's area formula area = side_length * side_length / 2. So if we would want to use same quality metric as before, 1 mm we would want a target area of 1 mm * 1mm / 2 = 0.0000005.

Apply tessellation to scene

Instead of saving the file with tessellated data in a .bary file we can apply the tessellation to the scene. This gives us a new scene where all data previously saved in tessellated attributes instead are saved in geometry. This can be useful if main reason for us using tessellated attributes is speeding up remesher, rater then wanting a bary output file.

def tessellate_scene(sg, scene):
    # We will now create an attribute tessellation tool object, and generate a scene with the 
    # tessellated attribute displacement data generated into real tessellated mesh data, which is 
    # stored into a new scene. 
    sgAttributeTessellation = sg.CreateAttributeTessellation()
    sgTessellatedScene = sgAttributeTessellation.NewTessellatedScene(scene)
    return sgTessellatedScene

With that function we can create a tessellated scene then export it.

tesselated_scene = tessellate_scene(sg, scene)
path = without_fileending_name + "_tesselated.fbx"
export_output(sg, tesselated_scene, path)

Low poly door mesh

Base remeshing of SM_Church_Door_Var01 with screen size 300 pixels.

Tessellated high poly door mesh

Tessellated remeshing of SM_Church_Door_Var01 with screen size 300 pixels.

Result

Let us experiment with different remeshing and attribute tessellation settings.

Church_Door_Var01

3 doors.

Remeshing at 1080 pixels, 500 pixels, 300 pixels.

Zoomed in picture of doors.

Remeshing with tessellation applied at 1080 pixels, 500 pixels, 300 pixels.

Zoomed in picture of doors.

Remeshing with tessellation applied at 1080 pixels, 500 pixels, 300 pixels.

At low resolution we can see some artifacts around the handles. At high resolution we can see that the screw holes are well preserved. In the original mesh each plank was separated so we have removed a lot of internal geometry as well.

Remeshing target Tessellation density mode Base triangle count Micro-mesh triangle count .glb file size (excluding textures) bary file size
Original - 402 k - 17 MB -
Screen size 300 Screen size 300 92 73 k 7 KB 56 KB
Screen size 500 Screen size 500 570 233 k 39 KB 196 KB
Screen size 1080 Screen size 1080 2 k 917 k 95 KB 703 KB
Max deviation 5 mm (screen size 529) Absolute length 1 mm 536 491 k 35 KB 314 KB
Max deviation 5 mm (screen size 529) Absolute area 0.5 mm² 536 468 k 35 KB 304 KB

Church_Wall_002

Castle parts.

Base mesh remeshed at 300 pixels, 500 pixels, 1080 pixels.

Castle parts.

Remeshing at 300 pixels, 500 pixels, 1080 pixels.
Remeshing target Tessellation density mode Base triangle count Micro-mesh triangle count .glb file size (excluding textures) bary file size
Original - 424 k - 19 MB -
Screen size 300 Screen size 300 420 234 k 24 KB 159 KB
Screen size 500 Screen size 500 788 473 k 42 KB 328 KB
Screen size 1080 Screen size 1080 4 K 1 951 K 188 KB 1 MB
Max deviation 5 mm (screen size 1980) Absolute length 1 mm 10 K 9 928 K 451 KB 5 MB
Max deviation 5 mm (screen size 1980) Absolute area 0.5 mm² 10 K 9 723 K 415 KB 5 MB

Church_Wall_007

Castle parts.

Tessellation applied at 300 pixels, 500 pixels, 1080 pixels.
Remeshing target Tessellation density mode Base triangle count Micro-mesh triangle count .glb file size (excluding textures) bary file size
Original - 621 k - 32 MB -
Screen size 300 Screen size 300 2 K 325 K 101 KB 290 KB
Screen size 500 Screen size 500 2 K 786 K 143 KB 609 KB
Screen size 1080 Screen size 1080 7 K 2 592 K 347 KB 2 M
Max deviation 5 mm (screen size 2013) Absolute length 1 mm 21 K 21 M 1 MB 11 MB
Max deviation 5 mm (screen size 2013) Absolute area 0.5 mm² 21 K 20 M 1 MB 10 MB

Church_Wall_008

Castle parts.

Tessellation applied at 300 pixels, 500 pixels, 1080 pixels.

Castle parts.

Base mesh remeshed at 300 pixels, 500 pixels, 1080 pixels.
Remeshing target Tessellation density mode Base triangle count Micro-mesh triangle count .glb file size (excluding textures) bary file size
Original - 688 k - 34 MB -
Screen size 300 Screen size 300 718 268 K 38 KB 252 KB
Screen size 500 Screen size 500 3 K 1 M 158 KB 921 KB
Screen size 1080 Screen size 1080 19 K 6 M 1 MB 4 MB
Max deviation 5 mm (screen size 1711) Absolute length 1 mm 33 K 33 M 2 MB 18 MB
Max deviation 5 mm (screen size 1711) Absolute area 0.5 mm² 33 K 29 M 2 MB 16 MB

Notice on low resolution base mesh

It is important to point out that if we have a low resolution we will get bad result no matter how dense the tessellation is. We can see this clearly on SM_Church_Wall_007.

High quality church window

Original SM_Church_Wall_007

Low quality church window

Remeshing of SM_Church_Wall_007 with screen size 300 pixels.

Dense church window with artifacts

Remeshing of SM_Church_Wall_007 with screen size 300 pixels and attribute tessellation of screen size 700 pixels.

What can mitigate this is to use lower or no hole filling using by changing SetHoleFilling. But it is likely that the only way to get good result is to increase the screen size of the base mesh. In our case the 300 screen size remesh only contains 1.5k triangles.

Let's try to increase base mesh to a screen size of 900 pixels. That gives us 5.9k triangles and much better looking result.

Good looking church window

Remeshing of SM_Church_Wall_007 with screen size 900 pixels and attribute tessellation of screen size 700 pixels.

Conclusions

Micro-meshes can be very useful when it comes to compress down geometry, but care need to be taked when using them. Here are some pointers for your journey into it.

  • While it is very tempting to go very low poly for base mesh it can cause artifacts that can not be solved with tessellation.
  • Avoid aggressive hole filling.
  • Use a high quality normal map.
  • It is worth pointing out that even when we over-tessellated some of our assets, made them contain more triangles then the original geometry, we did not go over the original assets file size.
  • Our take away from experimenting with micro-meshes is that uniform tessellated meshes, scan meshes, can usually have a lower resolution cage meshes while modeled meshes either from DCC tools or CAD require higher resolution base mesh.

Complete script

# Copyright (c) Microsoft Corporation. 
# Licensed under the MIT license. 
 
from simplygon10 import simplygon_loader
from simplygon10 import Simplygon
import os

TEXTURE_SIZE = 4096

INPUT_FILE_ENDING = ".FBX"
SCENE_SCALE = 100


def process_asset(sg, asset):
    print("Optimizing " + asset)
    
    importer = sg.CreateSceneImporter()
    scene = None
    
    importer.SetImportFilePath(asset)
    if importer.Run() == Simplygon.EErrorCodes_NoError:
        without_fileending_name = asset.replace(INPUT_FILE_ENDING, '')
        scene = importer.GetScene()

        #pipeline = create_remeshing_pipeline_screen_size(sg, 300, 300)
        #pipeline = create_remeshing_pipeline_screen_size(sg, 500, 500)
        pipeline = create_remeshing_pipeline_screen_size(sg, 1080, 1080)
        #pipeline = create_remeshing_pipeline_deviation_length(sg, scene, 0.005, 0.001)
        #pipeline = create_remeshing_pipeline_deviation_area(sg, scene, 0.005, 0.0000005) # 0.5 cm deviation, 1 mm * 1mm / 2 area = 0.0000005

        add_default_casters(sg, pipeline, scene)
        pipeline.RunScene(scene, Simplygon.EPipelineRunMode_RunInThisProcess)

        path = without_fileending_name + "_micromesh.glb"
        export_output(sg, scene, path)

        tessellated_scene = tessellate_scene(sg, scene)
        path = without_fileending_name + "_tessellated.glb"
        export_output(sg, tessellated_scene, path)

    else:
        print("Error: Failed to import " + asset)



def create_remeshing_pipeline_screen_size(sg, screen_size, tessellation_screen_size):
    sgRemeshingProcessor = sg.CreateRemeshingPipeline()

    sgRemeshingSettings = sgRemeshingProcessor.GetRemeshingSettings()
    mapping_image_settings = sgRemeshingProcessor.GetMappingImageSettings()
    material_output_settings = mapping_image_settings.GetOutputMaterialSettings(0)

    # Set quality of material casting.
    material_output_settings.SetTextureHeight(TEXTURE_SIZE)
    material_output_settings.SetTextureWidth(TEXTURE_SIZE)
    mapping_image_settings.SetGenerateMappingImage(True)
    mapping_image_settings.SetTexCoordName("MaterialLOD")

    # Set quality of remeshing.
    sgRemeshingSettings.SetOnScreenSize( screen_size ) 
    sgRemeshingSettings.SetSurfaceTransferMode( Simplygon.ESurfaceTransferMode_Accurate )
    sgRemeshingSettings.SetHoleFilling(Simplygon.EHoleFilling_Low)

    # Populate the attribute tesselation with displacement during remeshing.
    sgRemeshingSettings.SetPopulateAttributeTessellationDisplacement( True )
    sgAttributeTessellationSettings = sgRemeshingProcessor.GetAttributeTessellationSettings()
    sgAttributeTessellationSettings.SetEnableAttributeTessellation( True )

    # Attribute tessellation settings
    sgAttributeTessellationSettings.SetAttributeTessellationDensityMode( Simplygon.EAttributeTessellationDensityMode_OnScreenSize )
    sgAttributeTessellationSettings.SetOnScreenSize( tessellation_screen_size )
    sgAttributeTessellationSettings.SetOnlyAllowOneLevelOfDifference( True )

    return sgRemeshingProcessor


def MaxDeviationToScreenSize(maxDeviation, sgScene):
    sceneDiameter = sgScene.GetRadius() * 2
    screenSize = int(sceneDiameter / maxDeviation)
    print("Calculated screen size: " + str(screenSize))
    return screenSize


def create_remeshing_pipeline_deviation_length(sg, scene, max_deviation, tessellation_length):
    sgRemeshingProcessor = sg.CreateRemeshingPipeline()
    
    sgRemeshingSettings = sgRemeshingProcessor.GetRemeshingSettings()
    mapping_image_settings = sgRemeshingProcessor.GetMappingImageSettings()
    material_output_settings = mapping_image_settings.GetOutputMaterialSettings(0)

    # Set quality of material casting.
    material_output_settings.SetTextureHeight(TEXTURE_SIZE)
    material_output_settings.SetTextureWidth(TEXTURE_SIZE)
    mapping_image_settings.SetGenerateMappingImage(True)
    mapping_image_settings.SetTexCoordName("MaterialLOD")
    
    # Set quality of remeshing.
    sgRemeshingSettings.SetOnScreenSize( MaxDeviationToScreenSize(max_deviation * SCENE_SCALE, scene)  ) 
    sgRemeshingSettings.SetHoleFilling(Simplygon.EHoleFilling_Low)
    sgRemeshingSettings.SetSurfaceTransferMode( Simplygon.ESurfaceTransferMode_Accurate )
    
    # Populate the attribute tesselation with displacement during remeshing.
    sgRemeshingSettings.SetPopulateAttributeTessellationDisplacement( True )
    sgAttributeTessellationSettings = sgRemeshingProcessor.GetAttributeTessellationSettings()
    sgAttributeTessellationSettings.SetEnableAttributeTessellation( True )

    # Attribute tessellation settings
    sgAttributeTessellationSettings.SetAttributeTessellationDensityMode( Simplygon.EAttributeTessellationDensityMode_AbsoluteLength )
    sgAttributeTessellationSettings.SetMaxLengthOfTessellatedValue( tessellation_length * SCENE_SCALE)
    sgAttributeTessellationSettings.SetOnlyAllowOneLevelOfDifference( True )

    return sgRemeshingProcessor


def create_remeshing_pipeline_deviation_area(sg, scene, max_deviation, tessellation_area):
    sgRemeshingProcessor = sg.CreateRemeshingPipeline()
    
    sgRemeshingSettings = sgRemeshingProcessor.GetRemeshingSettings()
    mapping_image_settings = sgRemeshingProcessor.GetMappingImageSettings()
    material_output_settings = mapping_image_settings.GetOutputMaterialSettings(0)

    # Set quality of material casting.
    material_output_settings.SetTextureHeight(TEXTURE_SIZE)
    material_output_settings.SetTextureWidth(TEXTURE_SIZE)
    mapping_image_settings.SetGenerateMappingImage(True)
    mapping_image_settings.SetTexCoordName("MaterialLOD")

    # Set quality of remeshing.
    sgRemeshingSettings.SetOnScreenSize( MaxDeviationToScreenSize(max_deviation * SCENE_SCALE, scene)  ) 
    sgRemeshingSettings.SetHoleFilling(Simplygon.EHoleFilling_Low)
    sgRemeshingSettings.SetSurfaceTransferMode( Simplygon.ESurfaceTransferMode_Accurate )
    
    # Populate the attribute tesselation with displacement during remeshing.
    sgRemeshingSettings.SetPopulateAttributeTessellationDisplacement( True )
    sgAttributeTessellationSettings = sgRemeshingProcessor.GetAttributeTessellationSettings()
    sgAttributeTessellationSettings.SetEnableAttributeTessellation( True )

    # Attribute tessellation settings
    sgAttributeTessellationSettings.SetAttributeTessellationDensityMode( Simplygon.EAttributeTessellationDensityMode_AbsoluteArea )
    sgAttributeTessellationSettings.SetMaxAreaOfTessellatedValue( tessellation_area * SCENE_SCALE * SCENE_SCALE)
    sgAttributeTessellationSettings.SetOnlyAllowOneLevelOfDifference( True )

    return sgRemeshingProcessor


def add_default_casters(sg: Simplygon.ISimplygon, pipeline: Simplygon.spPipeline, scene: Simplygon.spScene) -> None:
    caster = sg.CreateColorCaster()
    caster_settings = caster.GetColorCasterSettings()
    caster_settings.SetMaterialChannel("Diffuse") 
    pipeline.AddMaterialCaster( caster, 0 )

    caster = sg.CreateNormalCaster()
    caster_settings = caster.GetNormalCasterSettings()
    caster_settings.SetGenerateTangentSpaceNormals(True)
    caster_settings.SetMaterialChannel("Normals") 
    pipeline.AddMaterialCaster( caster, 0 )


def process_assets(sg: Simplygon.ISimplygon, input_dir: str):
    for asset in os.listdir(input_dir):
        if INPUT_FILE_ENDING in asset:
            process_asset(sg, input_dir + "/" + asset)


def export_output(sg, scene, path):
    exporter = sg.CreateSceneExporter()
    exporter.SetScene(scene)
    print("Saving " + path)
    exporter.SetExportFilePath( path )
    exporter.Run()


def tessellate_scene(sg, scene):
    sgAttributeTessellation = sg.CreateAttributeTessellation()
    sgTessellatedScene = sgAttributeTessellation.NewTessellatedScene(scene)
    return sgTessellatedScene


def main():
    print("Starting batch processing")
    sg = simplygon_loader.init_simplygon()
    process_assets(sg, "E:/Git/simplygon.blogs/Micromesh/Models")


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

Request 30-days free evaluation license

*
*
*
*
Industry
*

Request 30-days free evaluation license

*
*
*
*
Industry
*