The Python API exposes the same pipeline as the CLI, but keeps each conversion step explicit and composable.
End-to-end pipeline
import fascat as fc
asset = fc.read_step("motor.step")
# Or import other CAD inputs:
# asset = fc.read_iges( b )
# asset = fc.read_brep( c )
asset = asset.tessellate(
fc.TessellationOptions(
sag=0.1,
sag_ratio=None,
angle=15.0,
relative=True,
min_edge_length=None,
max_edge_length=None,
max_polygon_length=None,
free_edge_report=False,
reuse_existing_meshes=True,
preserve_boundaries=True,
curvature_adaptive=False,
detail_adaptive=False,
avoid_skinny_triangles=False,
quality_report=False,
)
)
asset = asset.repair(
fc.RepairOptions(
tolerance=0.05,
merge_vertices=True,
delete_degenerate=True,
fix_winding=True,
quality_report=False,
face_orientation="exterior",
normal_orientation="from_faces",
fill_small_holes=False,
)
)
asset = asset.stage(
fc.StageOptions(
materials="cad",
material_mode="cad",
merge_equivalent_materials=False,
normals=True,
normal_mode="smooth",
normal_weighting="angle",
hard_edge_angle=30.0,
preserve_face_boundaries=False,
override_normals=True,
tangents=False,
tangent_uv_channel=0,
override_tangents=False,
validate_normals=False,
unwrap=fc.UnwrapOptions(),
atlas=fc.AtlasOptions(),
uv0="box",
uv1=None,
)
)
asset = asset.optimize(
fc.OptimizeOptions(
target_triangles=500_000,
preserve_instances=True,
simplify=True,
optimize_buffers=True,
preserve_hard_edges=False,
preserve_holes=False,
preserve_material_boundaries=False,
preserve_uv_seams=False,
preserve_small_parts=False,
preserve_silhouette=False,
)
)
asset = asset.lods(
fc.LODOptions(
ratios=[0.5, 0.25, 0.1],
mode="variants",
screen_coverage=[0.5, 0.2, 0.05],
per_part_budget=True,
drop_tiny_parts=True,
tiny_part_screen_size=2.0,
validate=True,
)
)
asset.write_usd("motor.usdc")
asset.write_gltf("motor.glb")
Pipeline operations return new Asset instances instead of mutating the previous asset. Write calls attach a final write step to the asset report.
Mesh-heavy per-part operations accept jobs to fan out independent parts while applying results
back in deterministic part order. jobs=1 is the default; set a higher value on
RepairOptions, MergeVerticesOptions, StageOptions, OptimizeOptions, DecimateOptions,
LODOptions, or LODGeneratorOptions for larger multi-part assets.
Core pipeline calls:
| API | Parameters | Purpose |
|---|---|---|
fc.read_step(path, options=None) | path is a STEP file path or - for stdin. options is StepReadOptions. | Import STEP assembly hierarchy, metadata, materials, and source BREP handles when the backend exposes them. With StepReadOptions(multi_file=True), quoted external .step / .stp references are recursively resolved from a master file and imported as deterministic member occurrences. |
fc.read_step_many(paths, options=None, continue_on_error=False) | paths is an ordered list of .step / .stp files. | Import explicit multi-root STEP members into deterministic per-file namespaces, prefix member warnings, and preserve each member as a top-level root. |
fc.read_iges(path, options=None) | path ends in .igs or .iges. options is IgesReadOptions. | Import IGES geometry through the same OCP/XDE hierarchy, transform, color, and material path used by STEP. |
fc.read_brep(path, options=None) | path ends in .brep. options is BrepReadOptions. | Import a native OpenCASCADE BREP file as one root occurrence and one source-shape part. |
asset.tessellate(options, where=None) | options is TessellationOptions. where optionally scopes the operation with a Filter. | Convert source BREP geometry into meshes. |
asset.repair(options, where=None) | options is RepairOptions. where optionally scopes selected parts. | Clean mesh-level issues after tessellation. |
asset.merge_vertices(options, where=None) | options is MergeVerticesOptions. where optionally scopes selected parts. | Merge exact or tolerance-close vertices with attribute and material-boundary protection. |
asset.delete_degenerate_polygons(options, where=None) | options is DeleteDegeneratePolygonsOptions. where optionally scopes selected parts. | Remove repeated-vertex, duplicate, or near-zero-area triangles as a standalone cleanup step. |
asset.stage(options, where=None) | options is StageOptions. where optionally scopes selected parts. | Prepare materials, normals, tangents, and UV metadata for runtime export. |
asset.optimize(options, where=None) | options is OptimizeOptions. where optionally scopes selected parts. | Reduce mesh complexity while preserving selected mechanical features. |
asset.lods(options, where=None) | options is LODOptions. where optionally scopes selected parts. | Generate lower-detail runtime meshes. |
asset.write_usd(path, options=None) | path ends in .usd, .usda, .usdc, or .usdz. options is UsdExportOptions. | Write OpenUSD output and append a write step to the report. |
asset.write_gltf(path, options=None) | path ends in .gltf or .glb. options is GltfExportOptions. | Write glTF 2.0 output and append a write step to the report. |
asset.write_fbx(path, options=None) | path ends in .fbx. options is FbxExportOptions. | Write ASCII FBX output and append a write step to the report. |
Assembly filters
Use Filter selectors to inspect or process one branch of an assembly while leaving the rest unchanged.
import fascat as fc
asset = fc.read_step("motor.step").tessellate()
fasteners = fc.Filter(
path="*/Fasteners/*",
name=["Bolt*", "Nut*", "Washer*"],
)
large_castings = fc.Filter.all(
fc.Filter.path("*/Housing/*"),
fc.Filter.size(min_diagonal=50.0),
)
print(asset.select(fasteners).stats())
asset = asset.optimize(
fc.OptimizeOptions(target_triangles=80_000),
where=fasteners,
)
asset = asset.stage(
fc.StageOptions(materials="display", uv0="none", uv1=None),
where=large_castings,
)
Filters support node path, node name, part id, part name, material, metadata, bounding box, size, triangle count, vertex count, and logical all, any, and not_ composition. If a selected occurrence shares a part with an unmatched occurrence, Fascat duplicates the selected occurrence's part before applying the operation so the unmatched branch stays intact. The scope planner skips that isolation copy when the selection already maps cleanly to whole unique parts. Report steps include where and matched fields when an operation is scoped.
Filter parameters:
| Parameter | Meaning |
|---|---|
path | Match the full assembly node path with shell-style patterns such as */Fasteners/*. |
name | Match node names. Accepts a string or list of patterns. |
part_name | Match the source part name. |
part_id | Match the stable Fascat part id. Filter.part(value) is shorthand for this. |
material | Match any material assigned to the selected part. |
metadata | Require metadata key/value matches on the node, part, material, or asset context. |
min_bounds, max_bounds | Match parts whose bounding box lies inside the supplied coordinate bounds. |
min_diagonal, max_diagonal | Match by bounding-box diagonal size. |
min_triangles, max_triangles | Match by mesh triangle count. Filter.triangle_count() builds these criteria. |
min_vertices, max_vertices | Match by mesh vertex count. Filter.vertex_count() builds these criteria. |
include | Require at least one nested filter to match before criteria are accepted. |
exclude | Drop matches selected by nested filters. |
Filter.all(...) | Require every child filter to match. |
Filter.any(...) | Require at least one child filter to match. |
Filter.not_(...) | Invert one child filter. |
where | Most pipeline methods accept where=Filter(...) to scope an operation without destroying unmatched hierarchy. |
Hierarchy merge
Use merge() to reduce node count and draw calls before optimization.
import fascat as fc
asset = fc.read_step("motor.step").tessellate().stage()
asset = asset.merge(
fc.MergeOptions(
mode="by_material",
keep_parent=True,
metadata="combine",
max_vertices_per_mesh=65_535,
preserve_materials=True,
),
where=fc.Filter.path("*/Fasteners/*"),
)
Merge modes include all, by_material, by_node_name, by_part_name, hierarchy_level, parent_children, final_level, and regions. Merging bakes node transforms into merged vertex positions, keeps material slots when requested, removes replaced empty nodes, and records before/after draw-call breakdown fields in the merge report step: draw_calls, draw_call_meshes, draw_call_materials, draw_call_submesh_slots, draw_call_material_slots, draw_call_mesh_instances, draw_call_reused_instances, draw_call_instanced_meshes, and draw_call_merged_batches. When merging reduces reusable instances, report steps include an export_advisor entry and warning so GLB file-size, memory, and culling tradeoffs are explicit.
Use explode() when runtime tools need separate meshes by material or connected component, and replace() when a selected part should become a proxy.
asset = asset.explode(
fc.ExplodeOptions(mode="connected_components"),
where=fc.Filter.material("rubber"),
)
asset = asset.replace(
fc.ReplaceOptions(mode="bounding_box", preserve_transform=True),
where=fc.Filter.triangle_count(max=12),
)
ReplaceOptions(mode="external_asset", external_path="proxy.glb") records an external proxy reference while keeping a bounding-box mesh fallback in the asset.
Hierarchy option parameters:
| Option | Parameter | Meaning |
|---|---|---|
MergeOptions | mode | Merge strategy: all, by_material, by_node_name, by_part_name, hierarchy_level, parent_children, final_level, or regions. |
MergeOptions | keep_parent | Keep a selected parent node and place merged geometry under it instead of flattening the selected branch completely. |
MergeOptions | metadata | Metadata policy: preserve, combine, summarize, or drop. |
MergeOptions | max_vertices_per_mesh | Split merged output before it exceeds this vertex count. Use 65_535 for 16-bit index friendly meshes. |
MergeOptions | preserve_materials | Keep material slots and face material assignments in merged geometry. |
MergeOptions | hierarchy_level | Level used by mode="hierarchy_level". 0 starts at the selected root. |
MergeOptions | region_size | Spatial cell size used by mode="regions". Required for region merging. |
MergeOptions | merge_strategy | Sub-strategy inside region merging: all or by_material. |
MergeOptions | remove_empty_nodes | Remove hierarchy nodes left empty after merging. |
ExplodeOptions | mode | Split selected meshes by by_material or connected_components. |
ExplodeOptions | metadata | Metadata policy applied to exploded parts. |
ExplodeOptions | remove_empty_nodes | Remove empty source nodes after selected geometry is replaced by exploded children. |
ReplaceOptions | mode | Replacement style: bounding_box, proxy_mesh, or external_asset. |
ReplaceOptions | preserve_transform | Keep the selected occurrence transform on the replacement. |
ReplaceOptions | metadata | Metadata policy applied to replacement parts. |
ReplaceOptions | proxy_mesh | Mesh object required when mode="proxy_mesh". |
ReplaceOptions | external_path | External asset path recorded when mode="external_asset". |
Metadata and PMI
Fascat keeps top-level asset metadata and typed PMI records alongside node, part, material, and mesh metadata.
import fascat as fc
asset = fc.read_step(
"motor.step",
options=fc.StepReadOptions(
metadata=True,
product_metadata=True,
properties=True,
layers=True,
validation_properties=True,
pmi=True,
design_variants=False,
design_variant_selection=(),
existing_meshes=True,
multi_file=False,
source_textures=True,
source_texture_search_paths=("textures",),
material_library_mapping=True,
material_library_paths=("vendor-materials.json",),
delete_free_vertices=False,
delete_lines=False,
source_units=None,
source_up_axis="Z",
source_handedness="right",
target_units="metre",
target_up_axis="Y",
target_handedness="right",
),
)
asset.metadata["review_state"] = "approved"
asset.pmi.append(
fc.PmiAnnotation(
id="pmi_001",
kind="dimension",
text="25.4 +/-0.1",
value=25.4,
unit="millimetre",
tolerance=fc.Tolerance(upper=0.1, lower=0.0),
applies_to=["part_123"],
)
)
glTF export writes metadata and PMI into extras.fascat. USD export writes Fascat metadata into customData on the scene, nodes, prototypes, materials, meshes, and /PMI/* annotation prims. When merge, explode, or replace operations create new parts, exporters resolve PMI links through source_part_id and source_part_ids metadata so annotations that targeted the original part still attach to the derived output.
STEP AP242 import includes a textual PMI scan for common typed records such as
DIMENSIONAL_SIZE, DIMENSIONAL_LOCATION, PLUS_MINUS_TOLERANCE,
GEOMETRIC_TOLERANCE, common named geometric-tolerance subtypes such as
FLATNESS_TOLERANCE, POSITION_TOLERANCE, and SURFACE_PROFILE_TOLERANCE,
DATUM, DATUM_REFERENCE, DATUM_TARGET,
FEATURE_CONTROL_FRAME, and annotation text entities. Those records become
typed PmiAnnotation objects with source STEP entity ids, references, and
numeric tolerance bounds where the textual record exposes them. If a
file advertises AP242 PMI but no supported typed record is extracted, the import
report records pmi_present=true, unsupported_pmi_count=1, and a warning
instead of silently implying that PMI was imported. Import reports and asset
metadata also include pmi_semantic_graph, a textual STEP reference graph with
PMI entity nodes, referenced STEP entity nodes, common shape-aspect/product
target support nodes, inbound callout/annotation associativity records,
tolerance-zone support records, annotation-presentation support records,
reference edges, and missing reference counts. metadata_and_visuals export
adds deterministic glTF/USD PMI marker meshes with simple vector text glyphs
linked to those records; full AP242 graphical presentation reconstruction and
semantic coverage beyond the supported textual records are still planned
backend work.
STEP design variant import can scan common configuration and effectivity records
such as CONFIGURATION_ITEM, PRODUCT_CONCEPT_FEATURE,
CONFIGURATION_DESIGN, CONFIGURATION_EFFECTIVITY,
PRODUCT_DEFINITION_EFFECTIVITY, SERIAL_NUMBERED_EFFECTIVITY,
LOT_EFFECTIVITY, DATED_EFFECTIVITY, EFFECTIVITY_RELATIONSHIP, and
simple AP242 condition/expression records such as AND_EXPRESSION,
OR_EXPRESSION, XOR_EXPRESSION, NOT_EXPRESSION, EQUALS_EXPRESSION,
COMPARISON_EQUAL, COMPARISON_NOT_EQUAL, COMPARISON_GREATER,
COMPARISON_GREATER_EQUAL, COMPARISON_LESS, COMPARISON_LESS_EQUAL,
INTERVAL_EXPRESSION, LIKE_EXPRESSION, numeric arithmetic expressions such
as PLUS_EXPRESSION, MINUS_EXPRESSION, MULT_EXPRESSION,
DIV_EXPRESSION, SLASH_EXPRESSION, MOD_EXPRESSION, and
POWER_EXPRESSION, RATIONAL_REPRESENTATION_ITEM,
EXPRESSION_EXTENSION_NUMERIC, numeric functions such as
ABS_FUNCTION,
MINUS_FUNCTION, SQUARE_ROOT_FUNCTION, MAXIMUM_FUNCTION,
MINIMUM_FUNCTION, SIN_FUNCTION, COS_FUNCTION, TAN_FUNCTION,
ASIN_FUNCTION, ACOS_FUNCTION, unary or binary ATAN_FUNCTION, EXP_FUNCTION,
LOG_FUNCTION, LOG2_FUNCTION, and LOG10_FUNCTION, simple string
expressions such as CONCAT_EXPRESSION, SUBSTRING_EXPRESSION,
INDEX_EXPRESSION, FORMAT_FUNCTION, and EXPRESSION_EXTENSION_STRING,
and string-derived numeric
functions such as
LENGTH_FUNCTION, VALUE_FUNCTION, and INT_VALUE_FUNCTION,
ODD_FUNCTION, BOOLEAN_LITERAL, BOOLEAN_REPRESENTATION_ITEM,
LOGICAL_LITERAL, LOGICAL_REPRESENTATION_ITEM, BOOLEAN_VARIABLE,
MATHS_BOOLEAN_VARIABLE, numeric literals, string literals, and named maths
numeric/string variables, plus wrappers such as
CONDITIONAL_CONFIGURATION, CONDITIONAL_CONCEPT_FEATURE,
CONDITIONAL_EFFECTIVITY, CONFIGURED_EFFECTIVITY_ASSIGNMENT, and
APPLIED_EFFECTIVITY_ASSIGNMENT / APPLIED_INEFFECTIVITY_ASSIGNMENT, plus
APPLIED_EFFECTIVITY_CONTEXT_ASSIGNMENT,
CONFIGURED_EFFECTIVITY_CONTEXT_ASSIGNMENT, and
CLASS_USAGE_EFFECTIVITY_CONTEXT_ASSIGNMENT when
design_variants=True. Detected records are
reported in asset metadata and import reports with counts, STEP references,
referenced record labels, transitively resolved reference labels, common
effectivity values, condition operators, and literal true/false condition
values, numeric condition values, or string condition values where present.
design_variant_selection=("left hand",) or
design_variant_selection=("SN-A-050",) can select matching variant or
effectivity records, including values inside supported serial/date/time-interval
ranges and referenced TIME_INTERVAL_WITH_BOUNDS date bounds using
CALENDAR_DATE, ORDINAL_DATE, or WEEK_OF_YEAR_AND_DAY_DATE records.
Supported condition records and effectivity-assignment wrappers
are evaluated before their operand labels can drive geometry pruning, so an
AND_EXPRESSION only selects geometry when all operands match and an
EQUALS_EXPRESSION / COMPARISON_EQUAL only select geometry when numeric or
string operands resolve to equal values, or when non-value operands participate
and resolve to the same boolean selection state. COMPARISON_NOT_EQUAL selects
when resolved numeric/string operand values differ, or when participating
non-value operand states differ. Numeric comparison and interval records select
when named numeric variables are
supplied as selection values such as load rating=15, including when those
variables flow through simple numeric arithmetic or numeric function expression
operands, including rational representation items, expression-extension numeric
values, and elementary trig/log/exp functions, with two-operand
ATAN_FUNCTION evaluated as atan2. ODD_FUNCTION selects when a named
integer numeric variable is
supplied as an odd integer assignment.
LIKE_EXPRESSION records select when named string variables are supplied as
selection values such as finish=black anodized, including when those values
flow through simple CONCAT_EXPRESSION, SUBSTRING_EXPRESSION, or
INDEX_EXPRESSION operands, EXPRESSION_EXTENSION_STRING values, or when
numeric variables flow through conservative FORMAT_FUNCTION operands.
LENGTH_FUNCTION, VALUE_FUNCTION, and INT_VALUE_FUNCTION can feed numeric
comparisons from selected string variables; value functions parse strict numeric
text, and integer value functions require strict integer text.
EFFECTIVITY_ASSIGNMENT /
APPLIED_EFFECTIVITY_ASSIGNMENT records only select assigned geometry when the
assigned effectivity matches. APPLIED_INEFFECTIVITY_ASSIGNMENT records are
reported and suppress assigned target labels when the selected effectivity
matches. Applied assignment targets can resolve through referenced
product-definition labels. Effectivity context-assignment records add their
context target labels only when the referenced effectivity assignment is
satisfied. Product-definition and configuration effectivity records add their
usage relationship labels only when the selected effectivity matches.
Effectivity relationship records can bridge selected effectivity labels to
related gated usage records.
Conditional wrapper records also gate their
configured target labels, and CONDITIONAL_CONCEPT_FEATURE gates its own
feature label, so the target label alone does not bypass an unsatisfied
condition. Boolean literal records, including named
BOOLEAN_REPRESENTATION_ITEM, LOGICAL_LITERAL, and
LOGICAL_REPRESENTATION_ITEM records, are evaluated
from STEP .T. / .F. arguments, and boolean variables including
MATHS_BOOLEAN_VARIABLE act as named operands selected by their label or STEP
record id, with explicit label=true / label=false assignments supported for
positive and NOT-gated conditions. Numeric arithmetic/function records can
participate in numeric comparisons, intervals, and equality/not-equality
conditions through selected label=value assignments.
STRING_LITERAL, EXPRESSION_EXTENSION_STRING,
MATHS_STRING_VARIABLE / STRING_VARIABLE, CONCAT_EXPRESSION, and
SUBSTRING_EXPRESSION records can participate in simple string equality,
not-equality, and
LIKE_EXPRESSION conditions with */% and ?/_ wildcard matching; string
variables require explicit label=value assignments, and operand-only
expression labels are not promoted to geometry selector terms. This is useful
for STEP files whose configuration labels line up with loaded product names;
full AP242 conditional/effectivity geometry evaluation remains planned backend
work.
STEP and IGES import can scan source-file string references for sidecar PNG, JPEG, and KTX2 textures, load them as first-class ImageResource objects, and bind semantic names such as baseColor, normal, ao, or emissive to material texture metadata. XDE visual material PBR/common values are preserved where exposed, and common CAD material names such as steel, aluminum, brass, copper, glass, plastic, rubber, and paint are mapped to deterministic PBR defaults with diagnostics in material metadata. Supported vendor material libraries can also be supplied explicitly, or referenced from the CAD source, as JSON/MTL files, ZIP packages containing JSON/MTL records plus textures, or folders containing those files; imported records update matching CAD materials with PBR factors and texture slots while reporting resolved, missing, unreadable, matched, and unmatched counts.
Construction-only STEP/IGES line shapes use an explicit construction-curve
policy. The default preserve_metadata keeps the source shape and reports that
it has no triangle mesh. delete drops construction-only line nodes during
import. tessellate_tubes preserves the source curve metadata and, during
tessellation, converts curve segments into deterministic low-sided triangle
tubes using construction_curve_tube_radius in source units. Legacy
delete_lines=True remains a delete-policy alias. STEP import also splits free
construction edges out of mixed face+curve shapes so the same policy applies to
those curve segments; when keep_brep=True later retains source BREP handles,
the original mixed source topology may still be available on the BREP part.
STEP import reports also include import_decisions, which records each import toggle as requested, effective, and honored, approximated, unsupported, disabled, not_present, or backend_default. The same report step includes loaded_representations, a per-part list of BREP, construction-point, construction-line, split mixed construction-line, or empty-shape inputs plus deleted construction-only nodes and source topology counts.
Use fc.read_step_many([...]), fc.convert([...], "out.glb"), or fascat convert root-a.step out.glb --input root-b.step when an assembly is delivered as several root STEP files rather than one file. Fascat imports each member through the normal STEP path, namespaces nodes, parts, materials, images, and PMI IDs with a deterministic member prefix, keeps each member root under a shared multi-file root node, and prefixes warnings with the member index and path. continue_on_error=True keeps successfully imported members and records failed members in the import report.
For a single master STEP, pass StepReadOptions(multi_file=True) or use --multi-file-import. Fascat scans quoted .step and .stp path records, resolves them relative to the referencing file, follows nested references once per resolved source, and imports the master plus each resolved reference occurrence through the same deterministic namespace merge. Repeated references to the same external STEP file become separate member occurrences while the resolver still scans that source only once to avoid cycles. The import report includes external_reference_graph with resolved, missing, unsupported, unique-source, and resolved-occurrence counts; missing references produce warnings instead of being silently dropped. This is graph-level loading, not deep reconstruction of every vendor-specific placement transform encoded around the external reference.
Metadata and PMI parameters:
| Option | Parameter | Meaning |
|---|---|---|
StepReadOptions | metadata | Enables general source metadata import. If False, the more specific metadata import groups are disabled by default. |
StepReadOptions | product_metadata | Import product and assembly-level metadata where the STEP backend exposes it. |
StepReadOptions | properties | Import user and product properties. |
StepReadOptions | layers | Request layer assignments as metadata. Current normalized layer extraction is reported as unsupported in import_decisions when requested. |
StepReadOptions | validation_properties | Request STEP validation properties. Current reports approximate this with source topology counts rather than typed validation-property entities. |
StepReadOptions | pmi | Import common typed AP242 PMI text records, including dimension, location, geometric tolerance and common named geometric-tolerance subtypes, plus/minus tolerance, datum, datum-reference, feature-control-frame, and note entities, into PmiAnnotation objects and report a textual semantic reference graph with common target, callout, annotation-associativity, tolerance-zone, and annotation-presentation support records; AP242 PMI markers are reported when no supported typed record can be extracted. |
StepReadOptions | design_variants | Scan common STEP configuration/design-variant/effectivity and simple boolean/numeric/string condition records into metadata and import reports. |
StepReadOptions | design_variant_selection | Select one or more variant labels, effectivity values/ranges, STEP record ids, referenced labels, or numeric/string/boolean assignments such as load rating=15, finish=black anodized, or service enabled=false, and prune imported geometry by matching node/part/source-name metadata. Effectivity selections follow resolved STEP references back to configuration/design feature labels, effectivity relationship links, product-definition/configuration effectivity usage targets, applied product-definition assignment targets, and gated effectivity context-assignment target labels when present; supported serial/date/time-interval ranges match values inside their bounds, including referenced TIME_INTERVAL_WITH_BOUNDS date bounds using CALENDAR_DATE, ORDINAL_DATE, or WEEK_OF_YEAR_AND_DAY_DATE records, simple AND/OR/XOR/NOT/equality/not-equality/numeric-comparison/numeric-interval/string-like/odd-function condition records gate operand labels before pruning, boolean/logical literals including BOOLEAN_REPRESENTATION_ITEM, LOGICAL_LITERAL, and LOGICAL_REPRESENTATION_ITEM evaluate .T. / .F. values, boolean variables including MATHS_BOOLEAN_VARIABLE evaluate as named operands selected by label, STEP record id, or explicit label=true / label=false assignments, named maths numeric variables evaluate from selected label=value assignments and can flow through simple numeric arithmetic and function expressions, rational representation items, and expression-extension numeric values before comparison/interval/equality evaluation, named string variables evaluate from selected label=value assignments for LIKE_EXPRESSION, equality/not-equality, CONCAT_EXPRESSION, SUBSTRING_EXPRESSION, EXPRESSION_EXTENSION_STRING, LENGTH_FUNCTION, VALUE_FUNCTION, and INT_VALUE_FUNCTION, applied ineffectivity assignments suppress assigned target labels, and conditional/effectivity-assignment wrappers gate their configured target labels while expression-only operand labels are kept out of geometry selectors. Full AP242 conditional/effectivity geometry evaluation remains planned. |
StepReadOptions | existing_meshes | Prefer existing tessellation payloads from the source file when the importer exposes them. Tessellation reuse_existing_meshes still controls whether loaded meshes are retessellated later. |
StepReadOptions | multi_file | Request multi-file STEP assembly import. read_step_many() honors explicit member lists; single-path STEP imports recursively resolve quoted external .step / .stp references, preserve repeated references as member occurrences, and report the external_reference_graph. |
StepReadOptions | source_textures | Scan STEP/IGES source text for referenced sidecar PNG/JPEG/KTX2 texture files, load resolved files into asset.images, and report resolved/missing/unreadable counts. |
StepReadOptions | source_texture_search_paths | Extra directories used to resolve relative source texture references in addition to the CAD file directory. |
StepReadOptions | material_library_mapping | Apply deterministic CAD material-name mapping rules to PBR metallic, roughness, opacity, and default color values when source visual material names are available. |
StepReadOptions | material_library_paths | Explicit vendor material-library JSON/MTL/ZIP files or folders to load during STEP/IGES import. Referenced library files are resolved relative to the CAD source and texture search paths. |
StepReadOptions | delete_free_vertices | Drop construction-only point shapes during import and record deletion counts in the import report. |
StepReadOptions | delete_lines | Legacy alias for deleting construction-only line shapes during import. Free construction edges split from mixed face+curve shapes follow the same delete policy. |
StepReadOptions | construction_curve_policy | Construction line policy for construction-only shapes and free construction edges split from mixed face+curve shapes: preserve_metadata, delete, or tessellate_tubes. Tube tessellation happens when the asset is tessellated. |
StepReadOptions | construction_curve_tube_radius | Tube radius in source units for construction_curve_policy="tessellate_tubes". |
StepReadOptions | source_units, source_meters_per_unit | Override the source unit declaration when the STEP header is wrong or ambiguous. Known unit names include metre, centimetre, millimetre, inch, and foot; custom factors use meters per source unit. |
StepReadOptions | source_up_axis, source_handedness | Declare the source coordinate basis before normalization. Defaults are STEP-style Z up and right handed. |
StepReadOptions | target_units, target_meters_per_unit | Normalize the imported asset to a target unit by applying a root transform and updating the asset's declared units. |
StepReadOptions | target_up_axis, target_handedness | Normalize the imported asset to a target up-axis or handedness. Import reports include the exact normalization transform and whether it changed the asset space. |
PmiAnnotation | id | Stable annotation id used for references from parts or mesh groups. |
PmiAnnotation | kind | Annotation type such as dimension, datum, tolerance, note, or backend-specific kinds. |
PmiAnnotation | text | Human-readable annotation text. |
PmiAnnotation | value, unit | Numeric measurement value and unit when available. |
PmiAnnotation | tolerance | Tolerance(upper=..., lower=...) values for dimensional or GD&T annotations. |
PmiAnnotation | applies_to | Target ids such as part ids, node ids, face groups, edge groups, or material ids. |
MetadataExportOptions | mode | Export metadata as full, count-only summary, or none. |
MetadataExportOptions | pmi | Export PMI as none, summary, metadata, metadata_and_visuals, or full. metadata_and_visuals emits metadata records, stable links, and deterministic glTF/USD marker geometry with simple vector text glyphs; full AP242 visual presentation reconstruction remains planned. |
asset.write_gltf(
"motor.glb",
options=fc.GltfExportOptions(
metadata=fc.MetadataExportOptions(mode="full", pmi="metadata"),
),
)
asset.write_usd(
"motor.usdc",
options=fc.UsdExportOptions(
metadata=fc.MetadataExportOptions(mode="full", pmi="metadata_and_visuals"),
),
)BREP Healing
Run BREP healing before tessellation when STEP topology needs sewing, edge fixing, tolerance unification, or open-shell and unstitched-edge reporting.
asset = fc.read_step("motor.step").heal_brep(
fc.BrepHealOptions(
tolerance=0.05,
group_open_shells=True,
sew_faces=True,
fix_edges=True,
unify_same_domain=True,
remove_overlapping_faces=True,
overlap_area_ratio=0.995,
remove_sliver_faces=True,
max_sliver_area=1e-4,
unify_tolerances=True,
fail_on_open_shells=False,
),
where=fc.Filter.path("*/Housing/*"),
)
The operation stores per-part brep_* metadata and records a heal_brep report step. Metadata includes BREP kind, solid/shell/wire/edge/face counts, open shells, free or unstitched edges, small edges at or below the healing tolerance, sliver-face counts, overlap/z-fighting face-pair counts, resolved overlap counts, open-shell grouping counts, and same-domain face/edge reductions. The report step also includes tolerance_policy, which records the effective source/local units used by the BREP backend, declared target units, meters-per-unit conversions, tolerance values in meters, sliver area in square meters, and whether open-shell grouping, sewing, edge fixing, same-domain cleanup, overlap/z-fighting cleanup, tolerance unification, sliver removal, T-junction sewing, and non-manifold cracking are enabled, disabled, requested, or not implemented. fc.convert(..., heal_brep=fc.BrepHealOptions()) runs healing before tessellation. Open-shell grouping processes disconnected shell groups independently before the cleanup stack so unrelated surface patches are not forced through one global sewing operation. Same-domain cleanup uses OCCT to merge neighboring faces and edges on coincident surfaces/curves. Overlap cleanup triangulates BREP faces, measures coplanar overlap against the configured smaller-face area ratio, and removes redundant z-fighting faces with OCCT BRepTools_ReShape. Sliver-face removal is requested through the BREP backend, but the current backend reports a warning when that removal path is unavailable instead of silently claiming that the source shape changed. Remaining open shells, free edges, small edges, or unresolved overlapping face pairs are also surfaced as report warnings.
Brep healing parameters:
| Parameter | Meaning |
|---|---|
tolerance | Working tolerance used for sewing, edge fixes, and tolerance unification. Must be greater than zero. |
group_open_shells | Group disconnected open shell patches before running the BREP cleanup stack. |
sew_faces | Attempt to sew adjacent faces into shells before tessellation. |
fix_edges | Attempt to repair bad trims and edge curves where supported by the backend. |
unify_same_domain | Merge neighboring faces/edges that lie on the same OCCT surface or curve domain. |
remove_overlapping_faces | Remove redundant coplanar faces whose projected overlap would cause z-fighting. |
overlap_area_ratio | Minimum overlap ratio against the smaller face before an overlapping face is removed. |
remove_sliver_faces | Request tiny sliver-face removal before tessellation. Current backend support is limited and reports a warning when removal is unavailable. |
max_sliver_area | Area threshold for sliver-face removal. |
unify_tolerances | Normalize shape tolerances to the requested working tolerance. |
fail_on_open_shells | Raise when healing detects open shells instead of reporting a warning. |
where | Optional filter that limits healing to selected assembly occurrences. |
Tessellation Controls
Tessellation supports global and per-part settings for edge limits, boundary preservation, curvature-adaptive OCCT meshing, material/metadata-driven detail adaptation, skinny-triangle cleanup, and per-part quality metrics.
asset = fc.read_step("motor.step").tessellate(
fc.TessellationOptions(
sag=0.05,
sag_ratio=None,
angle=10.0,
min_edge_length=0.02,
max_edge_length=2.0,
max_polygon_length=4.0,
preserve_boundaries=True,
curvature_adaptive=True,
detail_adaptive=True,
avoid_skinny_triangles=True,
quality_report=True,
free_edge_report=True,
reuse_existing_meshes=True,
part_settings={
"housing": {"sag": 0.03, "sag_ratio": 0.005, "max_edge_length": 1.0},
"Fastener": {"sag": 0.15},
},
)
)
quality = asset.tessellation_quality_report()
part_settings keys match a part id or part name. Quality reports include per-part edge length, triangle area, aspect ratio, skinny triangle, duplicate polygon, boundary edge, and non-manifold edge counts. Tessellation advisories warn when bulk criteria are risky for coarse absolute sag, aggressive polygon-length limits, or shiny/high-detail/curved BREP parts that lack sag_ratio or curvature_adaptive tuning. Set detail_adaptive=True to turn shiny/high-detail material metadata and curved BREP face detection into per-part settings: affected parts get sag_ratio=0.01 when unset and curvature_adaptive=True, while explicit part_settings remain authoritative. The tessellate report step includes a tolerance_policy with the active deflection kind, source/local units, target units, meters-per-unit conversions, and converted absolute sag or min/max polygon-length values when those settings are unit-bearing. Tessellated parts store that policy on part and mesh metadata and warn when source-to-target unit normalization makes absolute sag or max length suspiciously coarse or fine. Parts also record tessellation_face_groups, tessellation_estimated_draw_calls, and retained-patch counts when available; the tessellation step warns when retained BREP patches, CAD face groups, or material splits are likely to increase submesh, draw-call, or export-size pressure. Part and mesh metadata include tessellation_attribute_sources, which records whether positions, triangles, normals, tangents, UVs, face groups, free-edge diagnostics, and BREP patches came from tessellation, an imported mesh, or were not generated during tessellation. Mesh metadata also records tessellation_edge_control_passes, which is 2 only when the first edge-control pass changed the mesh and a cleanup pass was rerun.
Size-adaptive tessellation helpers can generate part_settings from part
bounding-box diagonals, using existing mesh bounds or source BREP bounds when
the OCCT backend is available:
asset = fc.read_step("motor.step")
tessellation = fc.profiles.size_adaptive_tessellation(
asset,
base=fc.TessellationOptions(sag=0.1, angle=15.0, quality_report=True),
bands=(
fc.profiles.TessellationSizeBand(max_diagonal=25.0, sag=0.02, angle=8.0, max_polygon_length=1.0),
fc.profiles.TessellationSizeBand(max_diagonal=None, sag=0.12, sag_ratio=0.01, angle=18.0),
),
)
asset = asset.tessellate(tessellation)
Explicit part_settings on base remain authoritative; the helper only fills
settings for parts that do not already have a part-id or part-name override.
Tessellation parameters:
| Parameter | Meaning |
|---|---|
sag | Maximum chordal deviation between source surface and tessellated mesh. Lower values produce more triangles. |
sag_ratio | Relative chordal deviation ratio. When set, it becomes the backend deflection value and enables relative tessellation explicitly. |
angle | Angular deviation limit in degrees. Lower values preserve curved surfaces with more triangles. |
relative | Compatibility switch for interpreting sag as a relative backend deflection when sag_ratio is unset. Prefer sag_ratio for new relative-tolerance workflows. |
min_edge_length | Collapse or avoid edges shorter than this length during post-processing. |
max_edge_length | Split long triangle edges to keep mesh density bounded. Very small values on non-elongated parts emit a quality advisory because this control is most useful for long objects with lighting artifacts. |
max_polygon_length | Report tessellated polygon edges longer than this threshold without subdividing geometry. Quality reports count these as long_edges; the tessellation step emits warnings when exceeded and advises when the limit is aggressive for ordinary parts. |
preserve_boundaries | Preserve CAD face and boundary edges during tessellation cleanup. |
curvature_adaptive | Request curvature-aware meshing from the backend when available. |
detail_adaptive | Auto-apply finer per-part tessellation to shiny material, high-detail metadata, or curved BREP-face parts by enabling curvature-adaptive meshing and setting sag_ratio=0.01 when unset. |
avoid_skinny_triangles | Run a cleanup pass that reduces long skinny triangles. |
quality_report | Record per-part tessellation quality metrics and advisories for later reporting. Coarse absolute sag relative to part size is flagged when relative=False and no sag_ratio is set; shiny, high-detail, or curved BREP parts also advise per-part sag_ratio or curvature_adaptive tuning. |
free_edge_report | Record free/boundary edge and non-manifold edge counts on tessellated parts and warn when free edges are present. |
create_normals | Generate normals during tessellation when the backend can provide them. Attribute provenance records tessellation, disabled, or missing for normals. |
keep_brep | Keep source BREP handles on parts after tessellation for later BREP-aware operations. When False, source BREP handles are dropped even when an imported mesh is reused instead of retessellated. Tessellated parts record brep_patch_cleanup=retained or deleted and warn when many retained patches could increase runtime/export risk. |
reuse_existing_meshes | Reuse meshes already present on imported parts. Set to False to retessellate from source BREP where available. |
part_settings | Per-part overrides keyed by part id or part name. Supports the same tessellation option names. |
Repair parameters:
| Parameter | Meaning |
|---|---|
tolerance | Merge tolerance for nearby vertices. 0.0 disables distance-based merging beyond exact duplicates. |
merge_vertices | Deduplicate vertices after tessellation. |
delete_degenerate | Remove triangles with repeated vertices or near-zero area. |
fix_winding | Normalize triangle winding where a consistent orientation can be inferred, including inward closed components detected by signed volume. |
quality_report | Run heavier before/after repair diagnostics for duplicate polygons, degenerate triangles, boundary and non-manifold edges, T-junctions, boundary gaps, and orientability. Defaults to False so conversion repair does geometric cleanup without paying for report-only topology scans. |
face_orientation | Face-orientation policy: exterior runs the current closed-component outward winding path; source_trusted and preserve keep source winding; viewer_standpoint, single_sided_open_shell, and unstitched_groups are recorded as intent until those strategies have a backend. |
normal_orientation | Normal-orientation policy: from_faces regenerates normals from repaired faces; source_trusted and preserve keep compatible source normals when possible; viewer_standpoint is recorded as intent until implemented. |
viewer_position | Three-number viewer position required when either orientation policy is viewer_standpoint. Recorded in metadata and reports. |
fill_small_holes | Fill small mesh boundary loops as a fallback mesh repair step. |
area_epsilon | Area threshold used to classify degenerate triangles. |
jobs | Worker count for independent mesh-bearing parts. 1 keeps serial behavior. |
Repair always records orientation policy metadata: repair_face_orientation_strategy, repair_face_orientation_status, repair_normal_orientation_strategy, repair_normal_orientation_status, and optional repair_orientation_viewer_position, so viewer-standpoint or source-trusted choices are visible even when the backend preserves source orientation. When quality_report=True, repair also records before/after counts for repair_duplicate_polygons, repair_degenerate_triangles, repair_boundary_edges, repair_non_manifold_edges, repair_t_junctions, and repair_boundary_gaps. Duplicate polygons are triangles that reference the same three vertices, regardless of winding. T-junction counts identify vertices that lie on another triangle edge without splitting that edge; non-zero counts after repair emit a warning because T-junction sewing is still not implemented. Boundary-gap counts identify nearby, unconnected boundary vertices within the repair tolerance; non-zero counts after repair warn because boundary stitching is also not implemented. With quality_report=True, repair also records orientability counts before and after winding normalization. The repair report step records tolerance_policy, including effective source/local units, declared target units, meter conversions, vertex merge tolerance in meters, degenerate area epsilon in square meters, and the status of vertex merge, degenerate-polygon cleanup, face orientation, normal orientation, quality diagnostics, T-junction sewing, boundary-gap stitching, and non-manifold edge cracking.
Use MergeVerticesOptions when you want Unity-style vertex merge as a standalone pipeline step instead of the broad repair pass. tolerance=0.0 merges exact duplicate positions; larger values merge Euclidean tolerance-close positions, including pairs that fall across spatial bucket boundaries. By default the operation keeps normals, tangents, UVs, and material-boundary signatures in the merge key so hard edges and UV seams are not collapsed accidentally. Set preserve_normals=False, preserve_tangents=False, or preserve_uvs=False to ignore and drop those attributes during merging. Default reports include merge_vertices_removed, merge_vertices_degenerate_triangles_removed, tolerance scale ratios against the part bounding-box diagonal and shortest mesh edge, warnings for high-risk tolerances, and a unit-aware tolerance_policy. Set quality_report=True (or --merge-vertex-quality-report) to add heavier same-position candidate counts, exact-duplicate, boundary, non-manifold, hard-edge, T-junction, and boundary-gap candidate counts, skipped merge reasons for normal, tangent, UV, and material-boundary protection, plus near-duplicate advisories for too-small tolerances.
Use jobs on MergeVerticesOptions to process independent mesh-bearing parts concurrently.
Use DeleteDegeneratePolygonsOptions when you want Unity-style degenerate
polygon cleanup as a standalone, reproducible step. area_epsilon controls the
near-zero-area threshold, and delete_duplicates=True also removes exact
duplicate polygons that reference the same three vertices regardless of winding.
The operation always writes a report step, even when no polygons are removed,
and per-part metadata records before/after degenerate and duplicate-polygon
counts, removed triangle counts, removed unreferenced vertices, primary removal
reasons for duplicate vertices, collapsed edges, near-flat area, and duplicate
polygons, plus the unit-aware area threshold.
DeleteDegeneratePolygonsOptions parameters:
| Parameter | Meaning |
|---|---|
area_epsilon | Area threshold used to classify near-flat triangles as degenerate. |
delete_duplicates | Remove exact duplicate polygons after degenerate triangles are removed. |
Feature-Preserving Simplification
Optimization can protect mechanical features while reducing triangle count. Preservation flags keep protected faces from being dropped when a target would otherwise remove them.
asset = asset.optimize(
fc.OptimizeOptions(
target_triangles=500_000,
simplify=True,
preserve_instances=True,
preserve_hard_edges=True,
hard_edge_angle=30.0,
preserve_holes=True,
preserve_material_boundaries=True,
preserve_uv_seams=True,
preserve_small_parts=True,
small_part_triangle_threshold=64,
preserve_silhouette=True,
)
)
Protected-feature counts are stored as part metadata under simplification_preserved_features. Parts below small_part_triangle_threshold are left unsimplified when preserve_small_parts=True.
Optimization parameters:
| Parameter | Meaning |
|---|---|
target_triangles | Absolute triangle budget for selected geometry. |
ratio | Fraction of original triangles to keep. Use this instead of target_triangles for proportional simplification. |
preserve_instances | Keep repeated part instances sharing geometry instead of expanding them unnecessarily. |
simplify | Enable triangle-count reduction. Disable to run only metadata and buffer optimization steps. |
optimize_buffers | Reorder and compact mesh buffers after simplification. |
preserve_hard_edges | Protect faces around hard normal edges from simplification. |
hard_edge_angle | Edge angle threshold in degrees used to detect hard edges. |
preserve_holes | Protect hole boundary loops and nearby faces. |
preserve_material_boundaries | Avoid collapsing across material boundaries. |
preserve_uv_seams | Avoid collapsing across UV seams. |
preserve_small_parts | Leave small parts unsimplified instead of spending budget on them. |
small_part_triangle_threshold | Parts at or below this triangle count are treated as small when preservation is enabled. |
preserve_silhouette | Protect bounding-box silhouette extremes to reduce visible shape loss. |
jobs | Worker count for independent mesh-bearing parts. 1 keeps serial behavior. |
Hard-Edge Normals And Tangents
Staging can generate smooth, flat, or hard-edge normals and glTF-ready tangents. Smooth and hard-edge normals can use angle or area weighting. Hard-edge mode splits vertices across hard normal edges, material boundaries, and optional CAD face-group boundaries.
asset = asset.stage(
fc.StageOptions(
materials="cad",
normals=True,
normal_mode="hard_edges",
normal_weighting="angle",
hard_edge_angle=30.0,
preserve_face_boundaries=True,
override_normals=True,
tangents=True,
tangent_uv_channel=0,
override_tangents=False,
validate_normals=True,
uv0="box",
)
)
Tangents require the selected UV channel when they are generated, which defaults to UV0. If tangents are requested without that channel, staging records missing-UV metadata and emits a stage warning instead of silently writing no tangent data. Existing tangents are preserved by default when staging has not invalidated them and the selected UV channel is still present; set override_tangents=True to force regeneration from tangent_uv_channel. When UV generation edits a mesh that already had tangents, staging invalidates the old tangent basis; if tangents=True, it regenerates tangents from the selected UV channel, otherwise it records the dropped tangent state. glTF export writes a TANGENT vertex attribute when staged meshes contain tangent data.
Normal and tangent parameters:
| Parameter | Meaning |
|---|---|
normals | Generate or preserve vertex normals. Automatically disabled when normal_mode="none". |
normal_mode | smooth averages face normals, flat keeps face normals, hard_edges splits vertices along hard edges, and none omits normals. |
normal_weighting | angle weights smooth or hard-edge normals by corner angle; area weights by triangle area. |
hard_edge_angle | Edge angle threshold in degrees for normal_mode="hard_edges". |
preserve_face_boundaries | Treat CAD face-group boundaries as hard normal boundaries. |
override_normals | Regenerate existing normals. Set False to preserve existing normals and only generate normals when missing. |
tangents | Ensure glTF-ready tangent vectors exist. Existing valid tangents are preserved by default. |
tangent_uv_channel | UV channel used when tangents need to be generated or regenerated. Defaults to 0. |
override_tangents | Regenerate existing tangents instead of preserving them when tangents=True. |
validate_normals | Check for missing, zero-length, or invalid normals after staging. |
UV And Material Pipeline
Staging can merge equivalent CAD materials, normalize simple CAD colors into PBR-friendly material values, tag UV unwrap settings, generate lightmap UV channels, and attach material-atlas metadata for later baking.
asset = asset.stage(
fc.StageOptions(
materials="cad",
material_mode="pbr",
merge_equivalent_materials=True,
uv0="unwrap",
uv1="lightmap",
unwrap=fc.UnwrapOptions(
texel_density=256.0,
padding=4,
max_stretch=0.15,
method="conformal",
iterations=32,
tolerance=0.001,
sharp_to_seam=True,
forbid_overlapping=True,
),
atlas=fc.AtlasOptions(
enabled=True,
max_size=4096,
),
normalize_uvs=(1,),
)
)
Atlas options on staging record texture-bake intent and layout limits. Dedicated material baking is the step that writes raster atlas images: baked maps are stored as first-class ImageResource objects, mirrored into material metadata for compatibility, and bound by the glTF/USD exporters as material textures.
Staged meshes also record UV layout quality metadata for each channel: uvN_domain, uvN_bounds, uvN_unit_domain_status, uvN_validation_status, uvN_out_of_unit_vertices, uvN_degenerate_faces, uvN_overlap_check, and uvN_overlap_pairs. Expensive overlap checks run only for bake-domain UVs or when forbid_overlapping=True; skipped tileable channels record uvN_overlap_pairs="not_evaluated". Every staged UV channel records seam graph diagnostics for duplicated-position UV discontinuities: uvN_seam_graph_status, uvN_seam_edges, uvN_seam_components, uvN_seam_position_vertices, uvN_seam_mesh_vertices, uvN_seam_length, and uvN_seam_longest_component_length; asset metadata also summarizes channels and edge counts with stage_uv_seam_graph_channels and stage_uv_seam_graph_edges. Bake-domain UVs, or any channel staged with max_stretch, also record diagnostic unwrap quality fields: uvN_distortion_check, uvN_island_count, uvN_pack_efficiency, uvN_normalized_pack_efficiency, uvN_max_angle_distortion_degrees, uvN_mean_angle_distortion_degrees, uvN_max_edge_length_distortion, and uvN_mean_edge_length_distortion; other channels record uvN_distortion_check="skipped". UV0 defaults to the tileable domain, where overlaps and coordinates outside 0..1 are allowed. UV1 and lightmap channels use the bake domain, where overlaps, degenerate UV faces, or coordinates outside 0..1 set uvN_validation_status and add stage warnings. Bake-domain channels generated with unwrap or lightmap are packed by xatlas with the configured padding/resolution and record uvN_pack_status="packed", uvN_padding_status, uvN_pack_width, uvN_pack_height, and uvN_pack_utilization. When uv0 or uv1 uses box, staging runs an AABB projection and records uvN_projection_* fields for local versus shared bounds, selected axes, destination channel, override policy, units, meters-per-unit, and uv3d_size. Unity-style UV policy controls are recorded per channel as uvN_sharp_to_seam_requested, uvN_sharp_to_seam_enforced, uvN_forbid_overlapping_requested, uvN_forbid_overlapping_effective, and uvN_forbid_overlapping_status; the current xatlas path records sharp-edge seam and forbid-overlap controls as intent and validates resulting UVs after generation. Use uv1="copy_uv0" when the secondary channel should reuse the generated or existing UV0 layout; staging records uv1_source_channel="0" and warns if UV0 is missing. Use normalize_uvs=(1,) to rescale selected channels into the 0..1 domain; staging records the original bounds and warns when a requested channel is absent.
asset = asset.stage(
fc.StageOptions(
uv0="box",
aabb_projection=fc.AabbProjectionOptions(
scope="shared",
uv3d_size=1.0,
override_existing=True,
),
)
)
When uv0 or uv1 uses unwrap or lightmap, fascat uses the optional xatlas backend for flattening, packing, and padding. method, iterations, tolerance, sharp_to_seam, and forbid_overlapping still record Unity-style solver and policy intent when xatlas does not expose a direct equivalent; Fascat validates the generated UVs and reports the actual pack status instead of treating the request as silently honored.
Staging, UV, and material parameters:
| Option | Parameter | Meaning |
|---|---|---|
StageOptions | materials | Material source policy: cad preserves CAD materials, display creates display materials, and none omits materials. |
StageOptions | material_mode | cad keeps source-style materials. pbr normalizes simple CAD colors into PBR-friendly material values. |
StageOptions | merge_equivalent_materials | Merge materials with equivalent visual values to reduce material count. |
StageOptions | uv0 | Primary UV channel mode: none, box, unwrap, or lightmap. |
StageOptions | uv1 | Secondary UV channel mode. Commonly lightmap for baked lighting, or copy_uv0 to duplicate UV0 into UV1. |
StageOptions | normalize_uvs | UV channels to rescale into 0..1 after generation/copy. Use explicitly because UV0 may intentionally tile outside 0..1. |
StageOptions | unwrap | UnwrapOptions used when a UV channel uses unwrap. |
StageOptions | atlas | AtlasOptions used to record atlas layout and baking intent. |
StageOptions | aabb_projection | AabbProjectionOptions used when a UV channel uses box projection. |
StageOptions | jobs | Worker count for independent mesh-bearing parts. 1 keeps serial behavior. |
AabbProjectionOptions | scope | local projects each part against its own AABB; shared projects selected parts against one shared AABB. |
AabbProjectionOptions | uv3d_size | Optional real-world size per UV tile. When unset, coordinates are normalized to the chosen AABB axes. |
AabbProjectionOptions | override_existing | Replace existing destination-channel UVs during box projection. Set False to preserve existing UVs and record that choice. |
UnwrapOptions | texel_density | Desired texture density for generated UVs. |
UnwrapOptions | padding | Padding between UV islands in pixels. |
UnwrapOptions | max_stretch | Maximum tolerated UV stretch before reporting unwrap risk. |
UnwrapOptions | method | Requested unwrap solver intent: default, conformal, or isometric. Non-default values are recorded as intent with the xatlas backend. |
UnwrapOptions | iterations | Requested unwrap solver iteration budget. Recorded as intent until a backend exposes this control. |
UnwrapOptions | tolerance | Requested unwrap solver error threshold. Recorded as intent until a backend exposes this control. |
UnwrapOptions | sharp_to_seam | Request sharp edges as UV seams for unwrap/lightmap channels. Recorded as intent until a backend exposes explicit seam policy controls. |
UnwrapOptions | forbid_overlapping | Request non-overlapping UV islands. Current backends validate and warn about overlaps instead of guaranteeing repacking. |
AtlasOptions | enabled | Record atlas metadata and prepare materials for later baking. |
AtlasOptions | max_size | Maximum atlas texture size in pixels. |
Scene Optimization
Use scene optimization to reduce draw calls after staging and optional hierarchy merging. It batches compatible meshes, can batch by material, reconstructs exact repeated mesh instances when vertex attributes, materials, and metadata match, can reconstruct near-identical instances within a configured position tolerance, reports duplicate vertex/triangle counts and estimated mesh-payload byte savings, splits large merged meshes, simplifies empty hierarchy, annotates the intended index-buffer width, and records how mesh count, material count, submesh/material slots, instances, and merged batches contributed to the draw-call estimate. When batching removes reusable instances, the report includes the same export advisor used by explicit merge operations.
asset = asset.optimize_scene(
fc.SceneOptimizeOptions(
batch_by_material=True,
merge_compatible_meshes=True,
split_large_meshes=True,
max_vertices_per_mesh=65_535,
index_buffer="auto",
flatten="safe",
remove_empty_nodes=True,
instance_policy="auto",
instance_similarity_tolerance=0.0,
)
)Scene optimization parameters:
| Parameter | Meaning |
|---|---|
batch_by_material | Group compatible geometry by material to reduce draw calls. |
merge_compatible_meshes | Merge meshes that can share buffers and material assignments safely. |
split_large_meshes | Split merged output that exceeds the configured vertex limit. |
max_vertices_per_mesh | Vertex limit used for splitting and index-buffer planning. |
index_buffer | auto chooses 16-bit or 32-bit indices. uint16 and uint32 force a width. |
flatten | none preserves hierarchy, safe removes only safe empty structure, and all aggressively flattens. |
remove_empty_nodes | Remove hierarchy nodes with no part and no children. |
instance_policy | auto and preserve reconstruct exact repeated mesh instances when vertex attributes, material assignments, and metadata match. expand duplicates instances per occurrence. |
instance_similarity_tolerance | Position tolerance for reconstructing near-identical repeated meshes with matching topology, vertex attributes, material assignments, and metadata. 0.0 keeps exact fingerprint matching only. |
Optimization Actions
Use explicit optimization actions when a realtime pipeline needs named preparation steps and separate report entries for each action.
asset = asset.bake_materials(
fc.BakeMaterialOptions(
maps_resolution=2048,
force_uv_generation=True,
bake=("base_color", "opacity"),
)
)
asset = asset.decimate(
fc.DecimateOptions(
criterion="target",
target_triangles=250_000,
surface_tolerance=0.1,
line_tolerance=0.02,
normal_tolerance=15.0,
uv_tolerance=0.01,
protect_topology=True,
preserve_painted_areas=True,
preserve_ambient_occlusion=True,
budget_scope="selection",
uv_importance="preserve_islands",
cleanup_attributes=("unused_uvs", "tangents"),
iterative_threshold=1_000_000,
)
)
asset = asset.remove_holes(fc.RemoveHolesOptions(max_diameter=3.0, prefer_brep=True))
asset = asset.remove_occluded(fc.RemoveOccludedOptions(strategy="advanced", level="triangles"))
asset = asset.run_lod_generators(
fc.LODGeneratorOptions(
preset="vr",
levels=(
fc.LODLevel(screen_coverage=0.5, target_ratio=0.5),
fc.LODLevel(screen_coverage=0.2, target_ratio=0.25),
fc.LODLevel(screen_coverage=0.05, target_ratio=0.1),
),
validate=True,
)
)
Material baking creates a shared flat material plus raster atlas images from selected material maps and per-face material assignments. The images are stored as first-class ImageResource objects, and glTF/USD exports bind them through material texture slots or UsdUVTexture shader networks connected to UsdPreviewSurface inputs. Hole removal uses deterministic mesh boundary classification and filling when BREP feature editing is unavailable. Occlusion removal uses deterministic visibility sampling, so the report records that thin occluders can require higher precision and asset metadata records the measured sample coverage, direction coverage, and confidence score. Decimation report options include target_strategy, which identifies whether the run used an explicit target triangle count, an explicit target ratio, or quality/error-bounded simplification. The same strategy is stored as decimate_target_strategy, source/workflow metadata, effective keep ratio when one exists, and quality-bound status on the asset and affected parts. Decimation records decimate_requested_keep_ratio metadata when a requested ratio can be derived, and warns when the request keeps less than 20% of source triangles because those settings are usually appropriate for distant LODs rather than close-view LOD0 assets. Decimation also records a memory estimate using the Unity rule of thumb of 5 GB RAM per million source triangles; iterative_threshold controls when intermediate simplification passes run, and reports include decimate_simplification_passes, decimate_iterative_passes, and decimate_iterative_recommended. Selection-wide decimation records per-part target allocation metadata, including assigned target triangles, reduced-versus-preserved part counts, and min/max assigned targets, so users can see which dense parts absorbed the global reduction. Decimation reports topology/material/UV protection counts for hard edges, hole boundaries, material boundaries, UV seams, silhouette faces, and total protected feature faces. preserve_painted_areas=True protects face groups or metadata-marked face indices named as painted, protected, weighted, or important, and preserve_ambient_occlusion=True protects low-AO faces from the sampled AO estimator. Reports include painted-area, AO, and combined importance-face counts. uv_importance="ignore" strips UV/tangent attributes before simplification; "preserve_seams" uses UVs for seam preservation and then strips them; "preserve_islands" keeps UVs through the output. cleanup_attributes=("unused_uvs", "tangents") removes degenerate or unused UV channels and tangents before simplification, while reports still warn when preserved UV seams or islands can reduce simplification efficiency.
LOD generation skips parts that do not have tessellated meshes, records lod_status="skipped_no_mesh" on those parts, and adds report warnings so partial LOD chains are visible. Asset metadata records lod_generated_parts and lod_skipped_no_mesh_parts. LOD reports also record source, added-LOD, and full-chain vertex/triangle counts plus estimated mesh payload bytes, so the memory and export-size cost of adding extra LOD levels is visible. Ratio-based LOD generation simplifies progressively: LOD1 is simplified from the source mesh, and later levels are simplified from the previous generated LOD while preserving the requested ratios against the source triangle count. LOD chain advisories warn when a chain has more than four generated levels, when LOD1 or LOD2 are aggressively reduced for close or mid views, and when the farthest LOD is still geometry-only and should eventually use one-mesh/one-material baking. Per-level LOD metadata records lod_simplification_source, whether source instances were reused, whether material merging or texture baking ran, whether culling granularity stayed per-part or changed because a tiny-part level was omitted, which policy advisory applies to that level, and the resolved export representation. engine_profile="unity" exports glTF LODs as MSFT_lod variant nodes, while engine_profile="unreal" exports separate _LOD# scene nodes for importer pipelines that ignore MSFT_lod.
Optimization action parameters:
| Option | Parameter | Meaning |
|---|---|---|
BakeMaterialOptions | maps_resolution | Raster atlas texture size in pixels for generated baked maps. |
BakeMaterialOptions | force_uv_generation | Generate UVs first when selected meshes do not have the required UV channel. |
BakeMaterialOptions | uv_channel | UV channel used for baking. |
BakeMaterialOptions | padding | Texture padding between islands in pixels. |
BakeMaterialOptions | bake | Maps to bake, such as base_color, opacity, normal, roughness, metallic, ao, or emissive. |
BakeMaterialOptions | merge_output | Replace selected materials with a shared baked output material. |
DecimateOptions | criterion | target prioritizes a triangle budget. quality passes the largest configured tolerance as a target error bound to the simplification backend and records bound/result metadata. Report target_strategy identifies whether the effective workflow was target count, target ratio, or quality/error-bounded simplification. |
DecimateOptions | target_triangles | Absolute triangle target for selected geometry. In the CLI, --decimate uses the selected profile or target-device triangle budget when no explicit target or ratio is supplied. |
DecimateOptions | target_ratio | Fraction of source triangles to keep when no absolute target is set. Ratios below 20% produce an LOD0 distortion warning. |
DecimateOptions | surface_tolerance | Surface tolerance input used by criterion="quality" to derive the simplification target error bound. |
DecimateOptions | line_tolerance | Line-feature tolerance input included in the quality target error bound. |
DecimateOptions | normal_tolerance | Maximum normal deviation in degrees. |
DecimateOptions | uv_tolerance | UV tolerance input included in the quality target error bound. |
DecimateOptions | protect_topology | Avoid topology changes that would remove important boundaries. Reports include protected hard-edge, hole-boundary, material-boundary, UV-seam, silhouette, and total feature-face counts. |
DecimateOptions | preserve_painted_areas | Preserve face groups or metadata-marked face indices named as painted, protected, weighted, or important. Reports include painted-area and combined importance-face counts. |
DecimateOptions | preserve_ambient_occlusion | Preserve low-AO faces from the sampled AO estimator during decimation. Reports include ambient-occlusion and combined importance-face counts. |
DecimateOptions | budget_scope | part budgets each part separately. selection uses a global selected-geometry target so sparse/simple parts can stay intact while dense parts absorb more reduction. Global selection decimation reports per-part target allocation, estimated RAM, and iterative-threshold status. |
DecimateOptions | uv_importance | Texture-coordinate handling: preserve_islands keeps UVs, preserve_seams protects seam topology then drops UVs, and ignore strips UVs/tangents before decimation. |
DecimateOptions | cleanup_attributes | Pre-decimation cleanup for attribute streams that are not useful to simplification. unused_uvs removes empty, constant, or zero-area UV channels. tangents removes tangents before simplification. Reports record removed channels, removed tangent parts, preserved UV channels, and UV seam/island constraint status. |
DecimateOptions | iterative_threshold | Source-triangle threshold above which decimation inserts intermediate simplification passes before the final target and reports actual pass counts. |
DecimateOptions | jobs | Worker count for independent mesh-bearing parts. 1 keeps serial behavior. |
RemoveHolesOptions | through, blind, surface | Hole-type filters for boundary-loop classification. through matches paired aligned openings, blind matches open pocket mouths, and surface matches remaining surface openings. |
RemoveHolesOptions | max_diameter | Only fill detected open boundary loops at or below the measured planar-span diameter. |
RemoveHolesOptions | prefer_brep | Request BREP-level feature removal. Current implementation warns and uses mesh boundary classification and filling. |
RemoveOccludedOptions | strategy | Visibility direction set: conservative checks cardinal views, exterior adds exterior diagonals, and advanced uses the densest deterministic direction set. |
RemoveOccludedOptions | level | Removal granularity: parts removes fully hidden occurrences, submeshes removes fully hidden material groups, and triangles removes hidden faces. |
RemoveOccludedOptions | precision | Maximum part-level face sample count before deterministic downsampling. Higher values can help thin occluders and large parts. |
RemoveOccludedOptions | hemi_evaluation | Restrict visibility rays to the upper hemisphere and side views for top/side-oriented evaluation. |
RemoveOccludedOptions | neighbors_preservation | Keep this many rings around visible triangles to reduce cracks. |
RemoveOccludedOptions | consider_transparency_opaque | Treat transparent materials as opaque for conservative visibility. |
RemoveOccludedOptions | preserve_cavities | Preserve interior cavities above the configured volume threshold. |
RemoveOccludedOptions | minimum_cavity_volume_m3 | Cavity volume threshold used when preserve_cavities=True. |
LODGeneratorOptions | preset | Default LOD level set: desktop, web, mobile, or vr. |
LODGeneratorOptions | levels | Explicit LODLevel entries overriding the preset. |
LODGeneratorOptions | validate | Validate monotonic triangle, material, and draw-call counts after generation. |
LODGeneratorOptions | output | LOD representation: variants, extras, or separate. |
LODGeneratorOptions | allow_non_monotonic | Permit non-monotonic LODs without failing validation. |
LODOptions | engine_profile | Switch-distance and glTF export profile: generic, unity, or unreal. unity resolves to MSFT_lod variant export; unreal resolves to separate _LOD# scene nodes for import tools that ignore MSFT_lod. |
LODOptions | far_lod_bake | For far-distance levels, collapse material indices to a one-material far LOD policy and record far texture-bake metadata. |
LODOptions | scene_far_proxy | Build an optional scene-level far proxy part from the final LOD occurrence geometry as one mesh, one material, and one draw-call proxy. glTF export attaches it as root MSFT_lod metadata. |
LODOptions / LODGeneratorOptions | jobs | Worker count for independent mesh-bearing parts. 1 keeps serial behavior. |
LODLevel | screen_coverage | Screen fraction at which this LOD becomes appropriate. |
LODLevel | target_ratio | Fraction of source triangles to keep for this LOD. |
LODOptions / LODGeneratorOptions | report metadata | LOD steps record lod_source_*, lod_added_*, and lod_chain_* counts for vertices, triangles, and estimated mesh payload bytes, plus per-level vertex/triangle counts, simplification source, omitted tiny-part LOD counts, instance-reuse counts, material-merge counts, texture-bake counts, culling-granularity change counts, scene-far-proxy counts, resolved export mode, and LOD chain advisory counts/codes. |
Occlusion metadata includes occlusion_candidate_count, occlusion_face_count, occlusion_sample_count, occlusion_visible_sample_count, occlusion_hidden_sample_count, occlusion_sample_coverage, occlusion_direction_coverage, and occlusion_confidence. The confidence score is the lower of sample coverage and direction coverage; lower values mean the result depends on sparse sampling or a reduced direction set.
Report examples for destructive and approximate operations:
{
"name": "merge",
"before": {"parts": 42, "triangles": 120000, "draw_calls": 42},
"after": {"parts": 8, "triangles": 120000, "draw_calls": 8},
"warnings": []
}{
"name": "bake_materials",
"before": {"materials": 12, "draw_calls": 18},
"after": {"materials": 1, "draw_calls": 1},
"warnings": []
}{
"name": "remove_holes",
"before": {"triangles": 8400},
"after": {"triangles": 8412},
"warnings": [
"BREP feature-level hole removal is not implemented; using mesh boundary classification and fill"
]
}{
"name": "remove_occluded",
"before": {"parts": 120, "triangles": 300000},
"after": {"parts": 118, "triangles": 296000},
"warnings": [
"remove_occluded uses deterministic sampled visibility; thin occluders may require higher precision"
]
}One-shot conversion
Use fc.convert() when you want the full default pipeline and output validation in one call.
import fascat as fc
asset = fc.convert(
"motor.step",
"motor.usdc",
profile="realtime-desktop",
where=fc.Filter.path("*/Fasteners/*"),
merge=fc.MergeOptions(mode="by_material", metadata="combine"),
)
print(asset.stats())
print(asset.report.summary())The output format is selected from the output suffix:
fc.convert("motor.step", "motor.usdc")
fc.convert("motor.step", "motor.usda", debug=True)
fc.convert("motor.step", "motor.glb", profile="virtual-reality")
fc.convert("motor.step", "motor.glb", profile="realtime-mobile")
fc.convert("motor.step", "motor.glb", profile="mixed-reality")
fc.convert("motor.step", "motor.gltf", profile="realtime-web")
fc.convert("legacy.igs", "legacy.glb")
fc.convert("native.brep", "native.usdc")
fc.convert() validates generated output by default. Pass validate_output=False only when another step in your pipeline validates the asset.
When where is provided to fc.convert(), tessellation, repair, and staging still run for the full asset, while standalone vertex merging, standalone degenerate-polygon cleanup, hierarchy merge, scene optimization, optimization actions, optimization, and LOD generation are scoped to the matched assembly subset.
Conversion parameters:
| Parameter | Meaning |
|---|---|
input_path | CAD input path ending in .step, .stp, .igs, .iges, or .brep; Python callers may pass a sequence of STEP paths for explicit multi-root import, and the CLI accepts repeated --input STEP roots. CLI stdin remains STEP-oriented because stdin has no suffix. |
output_path | Output path. Suffix selects USD, glTF, OBJ, STL, or FBX. |
profile | Profile name or ConversionProfile that supplies default tessellation, repair, stage, optimize, LOD, budget, and workflow-recipe metadata. |
import_options | StepReadOptions for STEP metadata and PMI import. |
tessellation | Overrides the profile tessellation step. |
heal_brep | Optional BREP healing step before tessellation. |
stage | Overrides the profile staging step. |
merge_vertices | Optional standalone vertex merge step after staging. |
delete_degenerate_polygons | Optional standalone degenerate-polygon cleanup step after vertex merging. |
merge, explode, replace | Optional hierarchy operations run after staging. |
scene | Optional scene optimization step. |
bake_materials, remove_holes, remove_occluded, decimate, lod_generator | Optional explicit optimization actions. |
optimize | Overrides the profile simplification step. |
lods | Overrides the profile ratio-based LOD step. |
progress | Callback receiving (step_name, stats) after major conversion steps. |
validate_output | Reopen and validate generated output before returning. Defaults to True. |
debug | Prefer debuggable USDA conventions. Only valid for .usd or .usda outputs. |
gltf_options, usd_options, obj_options, stl_options, fbx_options | Format-specific write options. |
pipeline | PipelineSpec loaded from TOML. When present, ordered pipeline steps drive the conversion. |
where | Optional Filter applied to scoped hierarchy, optimization, and LOD steps. |
For multiple branch-specific steps, load the same TOML pipeline format used by fascat convert --pipeline:
pipeline = fc.PipelineSpec.from_file("realtime.toml")
for advisory in pipeline.advisories():
print(advisory["message"])
asset = fc.convert("motor.step", "motor.glb", pipeline=pipeline)Pipeline files can also define import and export metadata policy:
[import]
metadata = "full"
pmi = true
design_variants = false
design_variant_selection = []
existing_meshes = true
multi_file = false
material_library_paths = ["vendor-materials.json"]
delete_free_vertices = false
delete_lines = false
construction_curve_policy = "preserve_metadata"
construction_curve_tube_radius = 0.01
target_units = "metre"
target_up_axis = "Y"
target_handedness = "right"
[export]
metadata = "summary"
pmi = "metadata"Runtime Export Options
glTF and USD exports accept runtime delivery options, and OBJ/STL/FBX are available for mesh and DCC handoff workflows.
asset.write_gltf(
"motor.glb",
options=fc.GltfExportOptions(
preset="web",
quantize=True,
meshopt=True,
draco=False,
texture_compression=None,
texture_fallback_format="auto",
png_compression=6,
jpeg_quality=85,
file_size_budget_mb=50,
size_ladder=True,
metadata=fc.MetadataExportOptions(mode="summary", pmi="metadata"),
),
)
asset.write_usd(
"motor.usdz",
options=fc.UsdExportOptions(package="usdz", file_size_budget_mb=100),
)
asset.write_obj("motor.obj", options=fc.ObjExportOptions(materials=True, write_mtl=True))
asset.write_stl("motor.stl", options=fc.StlExportOptions(binary=True, merge=True))
asset.write_fbx("motor.fbx", options=fc.FbxExportOptions(materials=True, normals=True, uvs=True))
preset="web", "mobile", "desktop", "vr", or "ar" resolves to concrete glTF compression defaults and, during fc.convert(), runs texture resize/dedupe cleanup with the preset's texture cap before writing. quantize=True writes KHR_mesh_quantization accessors and composes the dequantization transform into referencing nodes. meshopt=True writes EXT_meshopt_compression bufferView payloads while keeping fallback buffer data for validators and loaders that ignore the extension. draco=True runs the glTF Transform Draco encoder and writes required KHR_draco_mesh_compression payloads. texture_compression="ktx2" or "basisu" runs the KTX2/Basis encoder for referenced texture images and writes required KHR_texture_basisu payloads. USDZ output is built by writing a temporary USD stage and packaging it as .usdz. Scalar material transparency uses the effective opacity from base-color alpha and material opacity without double-counting duplicated CAD alpha values; glTF also marks scalar-transparent or baked-opacity materials as alpha-blended. When KTX2/Basis compression is not requested, texture_fallback_format records the PNG/JPEG fallback policy: auto chooses PNG for alpha-bearing texture sets and JPEG for color-only sets, while explicit png or jpeg records a forced fallback. png_compression and jpeg_quality are reported as fallback policy settings. glTF write report steps include runtime_dependencies, listing emitted extensions, required extensions, extras.fascat metadata, not-written runtime extensions, expected runtime support, a runtime_compatibility matrix for Unity glTFast, web, mobile, and XR targets, and a runtime_decision_matrix that explains when quantization, meshopt, Draco, KTX2/Basis, and PNG/JPEG fallbacks are appropriate. When size_ladder=True, direct writes and fc.convert() add a gltf_size_ladder report step that writes temporary GLB variants for baseline, quantized, meshopt, Draco, texture-compressed, and requested settings where available, then records measured bytes, baseline/requested/smallest sizes, and unavailable encoder warnings. Write report steps also include output file size, estimated geometry/texture/metadata payload bytes, referenced/unused/written material counts, source/referenced/unused/duplicate-reference/written image counts, and file-size budget warnings when a budget is provided. glTF, USD, and OBJ exports write only referenced materials, leaving the in-memory asset unchanged. glTF also omits images referenced only by unused materials and reuses repeated embedded texture URIs as one image/texture resource.
OBJ export writes vertex positions, normals, f v//vn face references, material assignments, and smoothing directives. Staged smooth normals export with smoothing enabled; flat, hard-edge, or generated face normals export with smoothing disabled.
FBX export writes ASCII FBX 7.4 files with Model, Geometry, Material, GlobalSettings, and Connections sections. Geometry uses FBX polygon-end index bits, preserves hierarchy transforms, and can write normal, tangent, UV, and per-face material layers. PBR material factors are approximated through legacy Phong properties, including effective material opacity.
Export option parameters:
| Option | Parameter | Meaning |
|---|---|---|
GltfExportOptions | preset | Named glTF export preset: desktop, web, mobile, vr, or ar. Presets request quantization, meshopt, KTX2/Basis texture compression, fallback quality, and fc.convert() texture resize/dedupe cleanup. |
GltfExportOptions | quantize | Write KHR_mesh_quantization accessors and dequantization transforms. |
GltfExportOptions | meshopt | Write EXT_meshopt_compression payloads with fallback uncompressed data. |
GltfExportOptions | draco | Run Draco geometry compression and require KHR_draco_mesh_compression when mesh payloads are present. |
GltfExportOptions | texture_compression | Run KTX2/Basis texture compression for referenced texture images: ktx2 or basisu. |
GltfExportOptions | texture_fallback_format | PNG/JPEG fallback policy when KTX2/Basis compression is not requested: auto, png, or jpeg. auto keeps alpha-bearing texture sets PNG-safe and color-only sets JPEG-compatible. |
GltfExportOptions | png_compression | PNG fallback compression level, 0 through 9. |
GltfExportOptions | jpeg_quality | JPEG fallback quality, 0 through 100. Reports warn when explicit JPEG fallback would discard alpha-bearing texture data. |
GltfExportOptions | file_size_budget_mb | Add report warnings when the output exceeds this size. |
GltfExportOptions | size_ladder | Add a measured gltf_size_ladder report comparing temporary baseline, optimized, compressed, and requested GLB variants. |
GltfExportOptions | metadata | MetadataExportOptions controlling metadata and PMI in extras.fascat. |
UsdExportOptions | package | default writes normal USD. usdz writes a packaged .usdz file. |
UsdExportOptions | file_size_budget_mb | Add report warnings when the output exceeds this size. |
UsdExportOptions | metadata | MetadataExportOptions controlling USD custom data and PMI prims. |
ObjExportOptions | materials | Write OBJ usemtl assignments when material data exists. |
ObjExportOptions | write_mtl | Write an .mtl sidecar next to the OBJ. |
ObjExportOptions | preserve_groups | Write OBJ group/object names from Fascat hierarchy and parts. |
ObjExportOptions | file_size_budget_mb | Add report warnings when the output exceeds this size. |
StlExportOptions | binary | Write binary STL when True; ASCII STL when False. |
StlExportOptions | merge | Merge selected triangles into one STL stream. STL does not preserve hierarchy or materials. |
StlExportOptions | file_size_budget_mb | Add report warnings when the output exceeds this size. |
FbxExportOptions | materials | Write FBX material nodes, per-face material indices, and model-material connections. |
FbxExportOptions | normals | Write FBX normal layers. |
FbxExportOptions | tangents | Write FBX tangent layers when mesh tangents exist. |
FbxExportOptions | uvs | Write FBX UV layers when mesh UV channels exist. |
FbxExportOptions | file_size_budget_mb | Add report warnings when the output exceeds this size. |
Profiles
Profiles provide practical defaults for tessellation, staging, optimization, LODs, and platform budget checks.
profile = fc.profiles.realtime_web(
tessellation_sag=0.2,
angle=20.0,
max_triangles=250_000,
lod_ratios=(0.5, 0.25),
)
asset = fc.convert("motor.step", "motor.glb", profile=profile)Available profiles:
| Profile | Use | Target FPS | Triangle budget | Per-mesh vertex budget | Texture resolution budget | Texture memory budget | Load-time budget | Draw-call budget | Unity reference range |
|---|---|---|---|---|---|---|---|---|---|
inspect-only | inspect STEP input without conversion | unset | unset | unset | unset | unset | unset | unset | unset |
realtime-desktop | higher-detail OpenUSD or glTF output | 60 | 1,000,000 | 65,535 | 4,096px | 512 MB | 2,000 ms | 2,000 | 10M-100M triangles, under 10,000 draw calls |
realtime-web | lower triangle budgets for web delivery | 60 | 250,000 | 65,535 | 2,048px | 128 MB | 3,000 ms | 500 | 100K-1M triangles, under 200 draw calls |
realtime-mobile | tighter mobile runtime budget for app-store builds | 60 | 150,000 | 65,535 | 2,048px | 128 MB | 2,500 ms | 250 | 100K-500K triangles, under 1,000 draw calls |
virtual-reality | balanced triangle budgets and LODs for VR runtimes | 90 | 500,000 | 65,535 | 2,048px | 256 MB | 1,500 ms | 250 | 500K-2M triangles, under 1,000 draw calls |
augmented-reality | stricter phone and tablet AR runtime budget | 60 | 100,000 | 65,535 | 1,024px | 64 MB | 1,500 ms | 150 | 50K-250K triangles, under 500 draw calls |
mixed-reality | stricter headset budget for mixed-reality runtimes | 60 | 75,000 | 65,535 | 1,024px | 64 MB | 1,200 ms | 100 | 50K-200K triangles, under 500 draw calls |
You can pass either a profile name or a ConversionProfile returned by fc.profiles. Built-in profiles also carry a WorkflowRecipe that names the Unity-inspired target, such as web-glb, mobile-glb, vr-glb, or high-fidelity-desktop. Conversion reports include a workflow_recipe step for those profiles, with each import, repair, tessellation, staging, optimization, LOD, texture, and export choice marked as honored, disabled, metadata_only, or unsupported.
Conversion reports include a profile_budget step when the selected profile has a budget. That step records target FPS, triangle, vertex, per-mesh vertex, texture-resolution, texture-memory, estimated load-time, draw-call budgets, draw-call breakdown fields, supported compression/runtime-extension caps, and Unity reference triangle/draw-call ranges when the profile has them. Fascat's defaults are intentionally stricter than Unity's broad reference ranges for repeatable export checks. Load time is a deterministic estimate based on output file size, geometry bytes, baked texture bytes, and draw-call overhead; it is not a measured engine runtime.
When referenced baked textures are present, conversion reports also include a pre-write texture_export_policy step. It records source, referenced, and unused texture-set counts, map counts, largest source and referenced resolution, estimated referenced texture bytes, selected profile texture-resolution and texture-memory caps, resize candidate counts, estimated resized bytes, estimated savings, whether KTX2/Basis was requested, and PNG/JPEG fallback policy when compression is not requested, including fallback format, PNG compression, JPEG quality, alpha-bearing set counts, and transparency-loss warnings for explicit JPEG fallback.
Custom target-device profiles can overlay a budget on any built-in base profile:
name = "factory-tablet-ar"
[budget]
target_fps = 60
max_triangles = 42000
max_texture_resolution = 512
max_draw_calls = 120
supported_compression = ["meshopt"]
supported_runtime_extensions = ["KHR_mesh_quantization", "EXT_meshopt_compression"]
unity_reference_profile = "tablet-ar"
unity_reference_triangles = [30000, 60000]profile = fc.profiles.from_file("factory-tablet.toml", base="realtime-mobile")
asset = fc.convert("motor.step", "motor.glb", profile=profile)
The CLI equivalent is fascat convert motor.step motor.glb --profile realtime-mobile --target-device-profile factory-tablet.toml. Profile files may be TOML or JSON and currently act as target-device budget overlays; tessellation, repair, staging, and LOD defaults still come from the selected base profile. When a file overrides max_triangles, Fascat uses that value as the profile optimization target, uses it as the explicit decimation target when --decimate is enabled without --target-triangles or --ratio, and derives max_vertices as three times the triangle budget unless max_vertices is set explicitly. supported_compression and supported_runtime_extensions are optional caps for glTF-oriented target devices; when the write report emits compression methods or runtime extensions outside those lists, the profile budget report records a violation.
Functional wrappers
The top-level functions mirror the fluent Asset methods.
import fascat as fc
asset = fc.read_step("motor.step")
asset = fc.tessellate(asset, sag=0.1, angle=15.0)
asset = fc.repair(asset, tolerance=0.05)
asset = fc.merge_vertices(asset, tolerance=0.001)
asset = fc.delete_degenerate_polygons(asset, area_epsilon=1e-12)
asset = fc.stage(asset, materials="cad", uv0="box")
asset = fc.optimize(asset, target_triangles=500_000)
asset = fc.lods(asset, ratios=(0.5, 0.25, 0.1))
fc.write_usd(asset, "motor.usdc")
fc.write_gltf(asset, "motor.glb")Reports and stats
Every imported or converted asset carries a report.
asset = fc.convert("motor.step", "motor.usdc")
print(asset.stats(include_lods=True))
print(asset.report.summary())
for step in asset.report.steps:
print(step.name, step.duration, step.before, step.after)
asset.report.write_json("report.json")
The report records options, before/after counts, warnings, errors, and timings for each pipeline step. Approximate operations put the limitation on the step that produced it, so callers can distinguish exact geometry changes from fallbacks or metadata-only intent. Conversion reports include a preflight step before expensive operations start, with checklist warnings for missing patch cleanup, orientation preparation, UV/tangent ordering, AO bake UV1 prerequisites, and LOD0 optimization, plus glTF compression applicability notes. They also include a workflow_recipe step for built-in profiles, a conversion_manifest step with the resolved profile, import options, direct or pipeline operation settings, and export options, followed by a workflow_summary step that maps Unity-inspired preparation stages such as import cleanup, UV preparation, material baking, LOD generation, export compression, and export to run or skipped status.
Use Asset.analyze() when you need geometry quality risks beyond raw part and triangle totals.
report = asset.analyze(
fc.AnalyzeOptions(
non_manifold_edges=True,
open_boundaries=True,
self_intersections=True,
sliver_triangles=True,
tiny_parts=True,
draw_call_estimate=True,
visual_risk=True,
)
)
print(report.summary)
report.write_json("quality-report.json")
The analysis report includes per-part topology counts, actual triangle
self-intersection counts, degenerate and sliver triangle stats, tiny-part stats,
material count, draw-call estimate, draw-call breakdown fields, and visual-risk
warnings derived from mesh quality and before/after pipeline report steps. Self-intersection checks ignore
adjacent triangles that share vertices. Coplanar overlaps count as intersections,
while point-only endpoint contact does not. If max_self_intersection_pairs is
reached, self_intersections_lower_bound is true and the report includes
self_intersection_pairs_checked and self_intersection_pair_limit;
self_intersection_warnings is kept as a compatibility alias for
self_intersections.
Analysis parameters:
| Parameter | Meaning |
|---|---|
non_manifold_edges | Count edges shared by more than two triangles. |
open_boundaries | Count boundary loops and boundary edges. |
self_intersections | Run bounded triangle-triangle intersection checks and report detected self-intersections. |
sliver_triangles | Report degenerate and high-aspect-ratio triangles. |
tiny_parts | Report parts below the configured diagonal threshold. |
draw_call_estimate | Include estimated draw calls, mesh count, referenced material count, submesh/material slots, instances, and merged batch counts. |
visual_risk | Enable risk-oriented warnings from geometry quality and report steps. |
sliver_aspect_ratio | Aspect-ratio threshold used to classify sliver triangles. |
degenerate_area_epsilon | Triangle area threshold used to classify degenerates. |
tiny_part_diagonal | Bounding-box diagonal threshold used to classify tiny parts. |
max_self_intersection_pairs | Maximum non-adjacent triangle pairs to check before reporting a lower-bound result. |
Use the visual preview helpers when you need deterministic review artifacts in CI or before handing an asset to a runtime viewer:
preview = fc.write_preview(asset, "preview.png")
comparison = fc.write_before_after_previews(before_asset, after_asset, "visual-review/")
lod_contact_sheet = fc.write_lod_switch_previews(asset_with_lods, "lod-previews/")
diff = fc.compare_images("baseline.png", "preview.png", fc.VisualDiffOptions(pixel_tolerance=2))
suite = fc.write_runtime_parity_suite("runtime-parity/")
captures = fc.capture_runtime_parity_suite(
"runtime-parity/",
targets=("browser", "unity"),
unity_command="Unity",
promote_goldens=True,
require_goldens=False,
)
The preview renderer is a local orthographic software renderer. It writes PNGs,
uses material base colors when available, respects node transforms, and can
substitute each part's LOD mesh to build an LOD switching contact sheet.
compare_images() reports mean absolute error, max channel error, changed pixel
counts, changed pixel ratio, and whether configured thresholds passed. The same
thresholds can gate engine preview baselines through fascat validate, but the
helper itself remains a general image-diff primitive. Use
write_runtime_parity_suite() to write bundled GLB fixtures, software baseline
PNGs, and a manifest for browser, Unity, and Unreal material/lighting and
compressed-texture preview comparisons. capture_runtime_parity_suite() runs
selected browser or engine preview targets for that suite, writes
runtime-parity-captures.json, and can promote rendered previews into
goldens/<target>/ for review. When goldens/<target>/<fixture>.png already
exists, captures compare against that target-specific golden instead of the
software baseline. Set require_goldens=True when a CI job should fail if the
checked-in target golden corpus is incomplete.
Validation
Direct write calls produce files but do not automatically reopen and validate them. Validate direct writes explicitly when you need the same safety as fc.convert().
asset.write_usd("motor.usdc")
usd_stats = fc.validate_usd("motor.usdc")
asset.write_gltf("motor.glb")
gltf_stats = fc.validate_gltf("motor.glb")
stats = fc.validate_output("motor.glb")
runtime = fc.measure_browser_runtime(
"motor.glb",
options=fc.RuntimeBrowserOptions(duration_seconds=2.0),
)
unity_project = fc.copy_engine_runtime_harness("unity", "FascatUnityHarness")
unity_runtime = fc.measure_engine_runtime(
"motor.glb",
options=fc.RuntimeEngineOptions(
engine="unity",
executable="Unity",
project=unity_project,
preview_path="motor-unity.png",
),
)
preview = fc.write_output_preview("motor.glb", "motor-preview.png")
browser_preview = fc.write_browser_render_preview("motor.glb", "motor-browser.png")The CLI can write a validation-time quality report for exported assets:
fascat validate motor.glb \
--filter 'material=Painted*' \
--geometry-quality \
--report quality-report.json
fascat validate motor.glb --runtime-browser
fascat validate motor.glb \
--runtime-engine unity
fascat validate motor.glb \
--runtime-engine unity \
--runtime-engine-project FascatUnityHarness \
--runtime-engine-preview motor-unity.png
fascat validate motor.glb \
--runtime-browser-preview motor-browser.png \
--visual-preview motor-preview.png \
--lod-preview-dir lod-previews/
fascat runtime-fixtures runtime-parity/
fascat runtime-fixtures runtime-parity/ \
--capture browser \
--capture unity \
--unity-command Unity \
--promote-goldens
fascat runtime-fixtures runtime-parity/ \
--capture unity \
--unity-command Unity \
--require-goldens
fascat runtime-fixtures runtime-parity/ \
--check-goldens \
--require-goldensValidation-time geometry reports use the same filter selectors as conversion when an exported format can be reconstructed for analysis.
--visual-preview writes a deterministic PNG from the validated output mesh.
--visual-baseline compares that PNG against a baseline image and exits non-zero
when the configured diff thresholds fail.
--lod-preview-dir writes lod0.png, each available LOD level, and a
lod-switching.png contact sheet. Fascat GLB exports preserve enough LOD
metadata for this validation path to reconstruct LOD previews.
--runtime-browser-preview launches the same Chromium-compatible browser
discovery path as --runtime-browser, renders supported glTF/GLB mesh
primitives with WebGL, and writes a PNG screenshot. This path gives a real
browser renderer artifact for simple assets, including node transforms,
material base-color factors, quantized vertex attributes, meshopt exports that
keep fallback buffer data, Draco geometry decoded through glTF Transform,
meshopt bufferViews without fallback buffer data decoded through local
meshoptimizer, KTX2/Basis textures decoded through the default Python
alktx2 backend on supported Python 3.11+ Linux/Windows x86_64 installs or glTF
Transform plus KTX-Software when available, optional KHR_texture_basisu textures
with PNG fallbacks, and base-color texture sampling for supported image URI/data
URI textures. The report lists decoded compressed payloads in
decoded_extensions; if Draco or meshopt decode tooling is unavailable, the
preview is reported as unsupported without writing a misleading screenshot. If
KTX2/Basis texture decode tooling is unavailable, fallback texture sources can
still render as status="rendered_partial" with unsupported_extensions and
preview_limitations in the JSON report; assets without fallbacks render
geometry only. Sparse accessors and full engine material/lighting parity remain
outside the packaged browser preview.
--runtime-browser is available for glTF/GLB outputs. It launches a local
Chromium-compatible browser when one is installed, loads the asset bytes in a
headless page, creates a WebGL workload from the asset triangle count, and
reports measured load time, FPS, frame count, memory bytes, and workload scale.
Set FASCAT_BROWSER or pass --runtime-browser-command when the browser is not
on the default PATH. If no browser is available, the report returns
status="unavailable" instead of substituting an estimate.
--runtime-engine unity and --runtime-engine unreal are also available for
glTF/GLB outputs. When --runtime-engine-project is omitted, Fascat copies a
packaged Unity or Unreal harness template into a temporary directory and runs
that project. Use fc.copy_engine_runtime_harness("unity", path) or
fc.copy_engine_runtime_harness("unreal", path) when you want an editable,
persistent harness project; the Unreal helper returns the copied .uproject
path. The CLI launches Unity with FascatRuntimeHarness.Run or Unreal with
-run=FascatRuntimeHarness, passes the asset path and a JSON report path, and
records engine-process load/parse time, frame count, memory bytes, engine
version, mesh count, and triangle count. --runtime-engine-preview also passes
a requested PNG path to the harness (-fascatPreview for Unity or
-FascatPreview= for Unreal) and records render_status, render_time_ms,
rendered_frames, render_backend, render_limitations, render_error, and
preview_path from the returned report.
--runtime-engine-baseline compares that rendered engine preview against a PNG
baseline with the --visual-diff-* thresholds and exits non-zero when the
engine preview drifts beyond the configured tolerance.
The packaged Unity template includes Unity glTFast, loads and instantiates the
asset, sets up a camera and key light, renders a fixed multi-frame camera loop,
and writes the requested preview PNG when Unity can run with graphics enabled.
That packaged path reports measured_fps, frame_count, and
measurement_duration_ms from the render loop. The packaged Unreal commandlet
validates the engine command contract and glTF/GLB file parsing. When a preview
path is requested for GLB assets with supported FLOAT VEC3 positions and
unsigned indices, it writes an asset-driven deterministic PNG by rasterizing
triangle geometry with material baseColorFactor, and reports
render_backend="unreal_commandlet_geometry_rasterizer". If the asset cannot
be rasterized by that limited commandlet path, it falls back to a count-based
placeholder PNG with render_status="rendered_partial" and a limitation
message. That Unreal packaged preview is still not a full Unreal scene,
texture, lighting, or material renderer. fascat runtime-fixtures DIR writes
bundled PBR material, texture-map, optional KTX2/Basis fallback,
normal/lighting, Unity MSFT_lod, and Unreal separate-node LOD-profile GLB
fixtures with software baseline PNGs and a manifest containing browser, Unity,
and Unreal preview commands. Add
--capture browser, --capture unity, or --capture unreal to run local
preview captures for the suite and write runtime-parity-captures.json;
--promote-goldens copies rendered captures into goldens/<target>/. Existing
goldens/<target>/<fixture>.png files become target-specific comparison
baselines on later captures, and --require-goldens fails the suite when any
requested target golden is missing. Add --check-goldens to write
runtime-parity-golden-coverage.json without rendering; with
--require-goldens, the command fails when any expected target PNG is missing,
invalid, or has dimensions that differ from the software baseline.
Engine-specific checked-in golden corpora and full Unreal scene-rendered
screenshots/FPS remain open. Set
FASCAT_UNITY, UNITY_EDITOR,
FASCAT_UNREAL, or UNREAL_EDITOR, or pass --runtime-engine-command, when
the engine executable is not on PATH. If the executable is missing, or an
explicitly supplied harness project is missing, the engine report is marked
unavailable.
Inspecting assets
Use to_dict() for structured inspection or JSON serialization.
asset = fc.read_step("motor.step")
print(asset.part_count)
print(asset.material_count)
print(asset.occurrence_count)
payload = asset.to_dict()
print(payload["root"])
print(payload["parts"])The asset model preserves hierarchy, part records, material records, transforms, units, and source metadata where the STEP backend can read them.
glTF notes
OpenUSD is the highest-fidelity export path for USD-style LOD variants and instance metadata.
glTF export writes valid glTF 2.0 files for runtime use:
.gltfuses embedded binary buffers.glbwrites a binary glTF container- geometry is exported in metres and Y-up
- original units and source up-axis are preserved in top-level Fascat extras
- material subsets are exported as separate glTF primitives
- generated LOD meshes are included as Fascat extras; Unity-profile exports add node-level
MSFT_lodreferences withMSFT_screencoveragehints, and Unreal-profile exports add separate_LOD#scene nodes - write reports include runtime compatibility notes for
KHR_mesh_quantization,EXT_meshopt_compression,KHR_draco_mesh_compression,KHR_texture_basisu,MSFT_lod, andextras.fascat