Documentation

Support

Asset Transformer SDK


User Manual

Python API

C# API

Changelog

Discussions

Asset Transformer SDK


Import guidelines

Learn how to import and prepare CAD files for real-time 3D experiences.
Read time 6 minutesLast updated 21 days ago

Why prepare CAD files for import?

CAD models are designed for engineering and manufacturing, not for real-time visualization. When importing CAD files into real-time applications, you need to transform precise parametric geometries (BREP) into polygonal meshes that can be efficiently rendered. A typical import workflow involves several key steps:
  1. Import - Load the file and configure what information to preserve
  2. Repair - Fix geometric issues that can cause rendering problems
  3. Tessellate - Convert CAD surfaces into polygonal meshes
  4. Orient - Ensure all polygon faces are oriented correctly for proper lighting
  5. Create attributes - Generate normals, UVs, and tangents needed for rendering
Following these guidelines will ensure your imported models display correctly and perform efficiently in real-time applications.

1. Import

1.1. Choose what to load

Before importing, configure the IO module properties to control what information is loaded from the file. This can significantly reduce import time and memory usage. Example: Configure module properties before import
# Load variants and PMI informationcore.setModuleProperty("IO", "LoadVariant", "True")# Prefer loading existing mesh data over BREPcore.setModuleProperty("IO", "PreferLoadMesh", "true")# Load PMI (Product Manufacturing Information)core.setModuleProperty("IO", "LoadPMI", "True")

1.2. Import files

Use io.importFiles to load one or more files into the scene. This function returns the root occurrences of the imported content.
# Import a single fileroot = io.importScene("path/to/model.stp")# Import multiple filesfile_list = [ "path/to/part1.stp", "path/to/part2.jt", "path/to/assembly.catpart"]roots = io.importFiles(file_list)

1.3. Delete unnecessary information

CAD files often contain data that isn't needed for real-time visualization. Removing this information early reduces memory usage and processing time.

Delete points and lines

Points and lines (free vertices and line meshes) are typically used for construction geometry in CAD but aren't needed for rendering.
# Delete free vertices (points)algo.deleteFreeVertices([root])# Delete line geometriesalgo.deleteLines([root])

Delete patches

CAD patches (BREP faces) should be removed after tessellation to free memory, as they're no longer needed for rendering.
# Delete CAD patch information after tessellationalgo.deletePatches([root])

2. Repair

Repairing geometry is critical before tessellation and optimization. CAD models often have small gaps, overlapping surfaces, and other topological issues that can cause problems during mesh generation.

2.1. Merge open shells

Before repairing CAD geometry, merge part occurrences that contain single open shells. This step identifies parts with unclosed surfaces and prepares them for proper stitching during the repair phase.
# Merge part occurrences with single open shell by assembliesopenShells = scene.mergePartOccurrencesWithSingleOpenShellByAssemblies(root)# Optionally, find unstitched faces for special handlingunstitchedFaces = scene.findPartOccurrencesWithUnstitchedOpenShells(root)

2.2. Repair CAD geometries

For models containing BREP data, repair the CAD surfaces to ensure properly connected geometry. This stitches open shells together and fixes topological issues.
# Compute a tolerance relative to model sizetolerance = 0.1 # In mm. Adjust based on your model scale# Repair CAD surfaces (after merging open shells)algo.repairCAD([root], tolerance, orient=False)

2.3. Repair mesh geometries

For models that already contain mesh data (or after tessellation), repair the polygonal meshes to fix connectivity issues, remove degenerate triangles, and merge nearby vertices.
# Repair mesh geometriesalgo.repairMesh([root], tolerance=0.1, crackNonManifold=True, orient=False)

3. Tessellate

Tessellation converts CAD surfaces (BREP) into polygonal meshes suitable for real-time rendering. This is one of the most important steps in the import process, as it determines the quality and polygon count of your final model.

Choose tessellation parameters

Tessellation quality is controlled by tolerance parameters that balance visual fidelity against polygon count:
  • Maximum sag - Maximum distance between CAD surface and mesh (in scene units)
  • Sag ratio - Maximum distance ratio relative to model size
  • Maximum angle - Maximum angle between adjacent polygon normals (in degrees)
  • Maximum length - Maximum edge length for polygons (optional)
# Tessellate with quality-based parametersalgo.tessellate([root], maxSag=0.1, # 0.1 units maximum deviation maxAngle=15, # 15 degrees for curved surfaces createNormals=True, # Generate normals during tessellation uvMode=algo.UVGenerationMode.NoUV) # Skip UV generation for now

Adaptive tessellation

For better results with models of varying sizes, use relative tessellation based on the model's bounding box:
# Tessellate relative to model sizealgo.tessellateRelativelyToAABB([root], maxSag=0.1, # Max value used sagRatio=0.0002, # 0.02% of bbox dimension maxLength=-1, # Disabled maxAngle=15, # 15 degrees createNormals=True, uvMode=algo.UVGenerationMode.NoUV, createTangents=False, createFreeEdges=False, keepBRepShape=False, # Delete BREP after tessellation overrideExistingTessellation=False)

4. Orient

Polygon faces have a front side and a back side, determined by the normal direction. Incorrect face orientation causes rendering issues where surfaces appear invisible or incorrectly lit.

Orient polygon faces

After tessellation and repair, ensure all polygon faces are oriented consistently:
# Orient faces using the exterior orientation strategyalgo.orientPolygonFaces([root])

Handle open shells and unstitched faces separately

If you identified open shells and unstitched faces earlier (in step 2.1), you may want to orient them separately:
# Orient open shells separately (if you saved them from step 2.1)if len(openShells) > 0: algo.orientPolygonFaces(openShells)# Orient unstitched faces separatelyif len(unstitchedFaces) > 0: algo.orientPolygonFaces(unstitchedFaces)

5. Create missing attributes

Real-time rendering requires certain mesh attributes that CAD files typically don't contain. Generate these attributes after tessellation and orientation.

5.1. Normals

Normals define how surfaces react to lighting. This attribute must be present to achieve good visual quality. While Asset Transformer tessellation will create normals automatically, you might have imported a mesh file without normals.
# Create smooth normals (45-degree threshold)algo.createNormals([root], sharpEdge=45, # Preserve edges sharper than 45° override=False, # Keep existing normals if they already exist useAreaWeighting=True) # Weight by polygon area# Orient normals consistentlyalgo.orientNormals([root])

5.2. UVs

UV coordinates map 2D textures onto 3D surfaces. Generate UVs if you plan to apply textures or use certain rendering techniques.

Option 1: Simple box projection

For basic texture mapping or when precise UVs aren't critical:
# Generate UVs using axis-aligned bounding box projectionalgo.mapUvOnAABB([root], useLocalAABB=True, # Use each part's local bbox uv3dSize=1000, # 1 meter = 1 UV unit channel=0, # First UV channel overrideExistingUVs=True)

Option 2: Automatic unwrapping

For better UV layouts suitable for texturing:
# Automatic UV unwrapping with optimal settingsalgo.automaticUVMapping([root], channel=0, sharpToSeam=True, # Use sharp edges as seams forbidOverlapping=True) # No overlapping islands

Lightmap UVs (second UV channel)

If using Unity's lightmapping system, generate a second UV channel optimized for baked lighting:
print("Generating lightmap UVs...")algo.mapUvOnAABB(roots, useLocalAABB=False, uv3dSize=1, channel=1, overrideExistingUVs=True)algo.repackUV(roots, channel=1, shareMap=False, resolution=1024, padding=2, uniformRatio=False, removeOverlaps=False)algo.normalizeUV(roots, sourceUVChannel=1, destinationUVChannel=-1, uniform=True, sharedUVSpace=False, ignoreNullIslands=False)

5.3. Tangents

Tangents are required for normal mapping (bump mapping). Generate them after creating UVs:
# Generate tangents from UV channel 0algo.createTangents([root], uvChannel=0, override=False) # Keep existing tangents

Script

Here's a complete Python script that implements the entire import workflow:
import pxzfrom pxz import core, io, algo, scene# File path to importFILE_PATH = "/path/to/file.CATProduct"# Step 1: Configure import settingscore.setModuleProperty("IO", "LoadVariant", "False")core.setModuleProperty("IO", "PreferLoadMesh", "false") # We want to tessellate ourselvescore.setModuleProperty("IO", "LoadPMI", "False")# Step 2: Import the fileprint("Importing file...")root = io.importScene(FILE_PATH)roots = [root]# Step 3: Delete unnecessary dataprint("Cleaning up unnecessary data...")algo.deleteFreeVertices(roots) # Remove pointsalgo.deleteLines(roots) # Remove line geometries# Step 4: Merge open shells before repairingprint("Merging open shells...")openShells = scene.mergePartOccurrencesWithSingleOpenShellByAssemblies(root)unstitchedFaces = scene.findPartOccurrencesWithUnstitchedOpenShells(root)# Step 5: Repair CAD geometriesprint("Repairing CAD surfaces...")tolerance = 0.1 # Adjust based on your model unitsalgo.repairCAD(roots, tolerance, orient=False)# Step 6: Repair mesh geometries (for any existing meshes)print("Repairing meshes...")algo.repairMesh(roots, tolerance, crackNonManifold=True, orient=False)# Step 7: Tessellate BREP to meshesprint("Tessellating CAD surfaces...")algo.tessellateRelativelyToAABB(roots, maxSag=0.1, # Max value used sagRatio=0.0002, # 0.02% of bounding box maxLength=-1, maxAngle=15, # 15 degrees createNormals=True, uvMode=algo.UVGenerationMode.NoUV, createTangents=False, createFreeEdges=False, keepBRepShape=False, # Delete BREP overrideExistingTessellation=False)# Step 8: Delete patches (BREP faces) to free memoryalgo.deletePatches(roots)# Step 9: Orient polygon facesprint("Orienting faces...")algo.orientPolygonFaces(roots)# Handle open shells and unstitched faces separately if neededif len(openShells) > 0: algo.orientPolygonFaces(openShells)if len(unstitchedFaces) > 0: algo.orientPolygonFaces(unstitchedFaces)# Step 10: Create smooth normals if they don't existprint("Creating normals...")algo.createNormals(roots, sharpEdge=45, override=False, useAreaWeighting=True)# Step 11: Generate UVs for texturing (channel 0) if they don't existprint("Generating UVs...")algo.mapUvOnAABB(roots, useLocalAABB=True, uv3dSize=1000, # 1 meter = 1 UV unit channel=0, overrideExistingUVs=True)# Step 12: Generate tangents for normal mapping if they don't existprint("Creating tangents...")algo.createTangents(roots, uvChannel=0, override=False)# Optional: Generate lightmap UVs (channel 1)print("Generating lightmap UVs...")algo.mapUvOnAABB(roots, useLocalAABB=False, uv3dSize=1, channel=1, overrideExistingUVs=True)algo.repackUV(roots, channel=1, shareMap=False, resolution=1024, padding=2, uniformRatio=False, removeOverlaps=False)algo.normalizeUV(roots, sourceUVChannel=1, destinationUVChannel=-1, uniform=True, sharedUVSpace=False, ignoreNullIslands=False)