Wavefront OBJ and OpenUSD Conceptual Data Mapping#

Introduction#

Overview#

This is a Conceptual Data Mapping document for Wavefront OBJ and OpenUSD. This is not meant to be a complete mapping, but focuses on the most commonly used features of OBJ: polygonal meshes and materials.

References#

This document has been prepared in reference to the software or specification versions listed below. Adjustments or considerations may need to be made for previous or future versions than those referenced in this document.

OBJ Reference#

OpenUSD Reference#

General Assumptions and Constraints#

This document shows a one-way mapping of OBJ-to-OpenUSD focused on polygon meshes and materials. To that end, both OBJ and MTL file formats are included in the mapping. Most of the widely-adopted high-level concepts of OBJ are listed, but they are not broken down. They are instead labeled as Out-of-Scope (OOS) for brevity.

Units and Coordinate Systems#

The OBJ file format does not encode certain scene-level metadata that USD requires:

  • Up Axis: OBJ does not specify whether the coordinate system is Y-up or Z-up. Different authoring tools may export OBJ files with different conventions.

  • Linear Units: OBJ does not encode the unit of measurement (meters, centimeters, inches, etc.). The numeric values in vertex positions are unitless.

Since USD stages require both upAxis and metersPerUnit to be defined, converters must determine these values through other means. We recommend that converters provide configuration options (such as command-line arguments) to allow users to specify the up axis and linear units appropriate for their source data.

Naming Conventions#

OBJ object and group names may contain characters that are not valid in USD prim and property identifiers. USD has strict naming requirements that restrict names to letters, numbers, and underscores, with additional constraints on the first character. Converters must sanitize OBJ names to ensure USD compliance. See Appendix A: Object Naming for detailed guidance on handling naming restrictions and sanitization strategies.

Definitions, Acronyms, Abbreviations#

Term or Abbreviation

Description

OOS

Out-of-Scope: Denotes that a concept is out of scope of the purposes of this conceptual data mapping document.

1-based index

The count of an array begins at 1 rather than 0.

Element

Geometric primitives constructed from vertices: point (p), line (l), face (f), curve (curv), 2D curve (curv2), and surface (surf).

Concepts#

An OBJ file can be made up of one or more objects.

OBJ

OpenUSD

Description

Vertex Data

See Appendix B.

Object (with faces)

Mesh

An object with face (f) commands represents a polygon mesh.

Group (g)

Scope, Xform, or GeomSubset

Groups elements into named sets. See Groups for mapping approaches.

Smoothing Groups (s)

normals

Defines smoothing group membership for normal interpolation.

usemtl

MaterialBindingAPI::DirectBinding, [GeomSubset]

This indicates that subsequent faces in the OBJ file should use the material specified.

mtllib

Sublayer

This is a reference to an external MTL file.

MTL Materials

UsdShadeMaterial, UsdShadeShader

Material definitions from MTL files. See MTL Material Library.

Comments (#)

documentation metadata

Optional metadata preservation.

Object (with curves)

BasisCurves, NurbsCurves

OOS

Object (with 2D curves)

BasisCurves, NurbsCurves

OOS

Object (with surfaces)

NurbsPatch

OOS

Object (with points)

Points

OOS

Object (with lines)

BasisCurves

OOS

Object (with faces)#

An object with faces represents a polygon mesh in OBJ. This maps well to UsdGeomMesh. For details on how OBJ objects are named and how to handle unnamed objects, see Appendix A: Object Naming. In OBJ, the data primitives are represented as commands. To map these to UsdGeomMesh, a developer will need to collate matching commands to form the data arrays that UsdGeomMesh requires. A polygon mesh object is required to have vertex (v) and face (f) commands, but it may not have texture coordinate (vt) and vertex normal (vn) commands as those are optional. The same is true for UsdGeomMesh.

Properties#

OBJ

OpenUSD

Description

vertex (v)

points

Vertices that form the faces of the mesh.

texture coordinate (vt)

primvars:st

2D coordinates for mapping textures onto the faces of the mesh.

vertex normal (vn)

normals

3D vectors used for calculating the geometric normal of the mesh surface.

face (f)

faceVertexIndices & faceVertexCounts

A single polygonal face (in OBJ).

Property: vertex (v)#

Each vertex (v) command maps directly to a point in a UsdGeomMesh. Collate the vertices from OBJ and author them as a points array in OpenUSD.

Name

Data Type

OBJ

v

float[3]

OpenUSD

points

point3f[]

Property: texture coordinate (vt)#

Each texture coordinate (vt) command maps directly to a texture coordinate (ST or UV) in OpenUSD. A texture coordinate (vt) command may optionally include a W value. The W value can be discarded when authoring OpenUSD.

Name

Data Type

OBJ

vt

float[2] or float [3]

OpenUSD

primvars:st

texCoord2f[]

Take into consideration Appendix C: Handling Separate Indices as some consuming apps may require indexed arrays for texture coordinates.

Property: vertex normal (vn)#

Each vertex normal (vn) command maps directly to a normal in a UsdGeomMesh. Collate the normals from OBJ and author them as a normals array in OpenUSD. Refer to Appendix C: Handling Separate Indices decide whether to implement indexed arrays or not.

Name

Data Type

OBJ

vn

Float[3] (might not be unit vector)

OpenUSD

normals, primvars:normals

normal3f[]

Property: face (f)#

Each face (f) command represents a polygon face. Face (f) commands will need to be decomposed into faceVertexIndices and faceVertexCounts. For each face (f) command, count the number of vertices included in the face (f) command and append that count to the faceVertexCounts array. For faceVertexIndices, use the points to populate faceVertexIndices, but be sure to account for Appendix B: Vertex Data and take into consideration Appendix C: Handling Separate Indices.

Name

Data Type

OBJ

f

int[] or int2[] or int3[]

OpenUSD

faceVertexIndices

int[]

OpenUSD

faceVertexCounts

int[]

Groups (g)#

The group (g) command assigns subsequent elements (faces, lines, points) to one or more named groups. This is often used for organizing parts of a model or enabling per-group material assignments.

OBJ

OpenUSD

Description

g <group_name>

Scope, Xform, or GeomSubset

Named element groups. Mapping depends on use case—see below.

Mapping Approaches:

  • GeomSubset: When groups are used to partition faces within a single mesh (e.g., for material assignment), map each group to a GeomSubset with the group name. Mapping groups as unique meshes in OpenUSD is also an acceptable alternative.

  • Scope/Xform: When groups represent distinct parts of a model, consider creating separate prims for each group or organizing them under Scope/Xform prims for hierarchy.

Developers may choose to implement group mapping based on their specific requirements.

Smoothing Groups (s)#

The smoothing group (s) command specifies how normals should be interpolated across faces. Faces within the same smoothing group share smoothed normals at shared vertices, while faces in different groups have hard edges between them.

OBJ

OpenUSD

Description

s <group_number>

normals

Smoothing group assignment. Pre-compute smoothed normals based on group membership and author them to the normals attribute.

s off

normals

Disables smoothing. Each face should have flat shading (face normals).

During conversion, compute vertex normals by averaging face normals within each smoothing group. Author the resulting normals to UsdGeomMesh’s normals attribute with faceVarying interpolation to preserve hard edges at group boundaries.

usemtl#

The usemtl command specifies a material from a material library that should be applied to all subsequently defined faces. A direct material binding in OpenUSD can be used to map this concept. When an Object (with faces) has more than one usemtl command, we also recommend using this command to denote the start of a material face set. Each usemtl command would result in a UsdGeomSubset defined as a child of the UsdGeomMesh. In such a case, the material binding is applied to the UsdGeomSubsets instead of their related UsdGeomMesh. When there is only one usemtl per object the binding should be to the mesh itself and subsets should be avoided.

Properties#

OBJ

OpenUSD

Description

usemtl

MaterialBindingAPI.material:binding

A simple direct mapping. If an object has multiple usemtl commands, developers should use UsdGeomSubset in conjunction.

faces

GeomSubset.indices

An array of face indices that use the specified material. The GeomSubset should have its elementType set to “face” and its familyName set to “materialBind”. The parent mesh prim also needs to set the subsetFamily:materialBind:familyType to “partition”

mtllib#

The mtllib command is a reference to an external MTL file. This maps well to the sublayer composition arc in OpenUSD. For direct mapping, we can author two USD layers to match the two file structures that OBJ uses. For practical and portability reasons, this may be flattened into one layer as a part of post-processing.

Properties#

OBJ

OpenUSD

Description

mtllib <filename>

Sublayer

The MTL file path becomes a sublayer reference in the USD stage. Materials defined in the MTL file are authored as UsdShadeMaterial prims in that sublayer.

Composition#

The mtllib command maps to USD’s sublayer composition arc. When converting:

  1. Create a separate USD layer for materials (e.g., materials.usda).

  2. Author each material from the MTL file as a UsdShadeMaterial prim in that layer.

  3. Add the materials layer as a sublayer to the main geometry layer.

Alternatively, for simpler workflows, all materials can be authored directly in the same layer as the geometry, avoiding the need for sublayers.

MTL Material Library#

This section documents the mapping of MTL material properties to OpenUSD’s UsdPreviewSurface shader. Other standards like OpenPBR may provide better photoreal results and a mapping to OpenPBR may be provided in the future. MTL files define materials referenced by usemtl commands in OBJ files.

newmtl#

The newmtl command defines a new material. Each newmtl command maps to a UsdShadeMaterial prim in OpenUSD, encapsulating a graph/network of UsdShadeShader prims.

OBJ

OpenUSD

Description

newmtl <name>

UsdShadeMaterial

The material name becomes the UsdShadeMaterial prim name. A UsdShadeShader prim with its id attribute set to “UsdPreviewSurface” must be created as a child and connected to the material’s surface output.

Standard Material Properties#

The following standard MTL properties map to UsdPreviewSurface inputs:

MTL Property

OpenUSD (UsdPreviewSurface)

Description

Ka

N/A or custom attribute

Ambient color. UsdPreviewSurface does not have a direct ambient input; this may be stored as custom data or approximated.

Kd

diffuseColor

Diffuse color (RGB, 0-1 range). Transformation required: MTL colors are typically in sRGB color space and must be converted to linear color space for USD.

Ks

specularColor

Specular color (RGB, 0-1 range). Transformation required: Convert from sRGB to linear color space. When specular color is provided, set the useSpecularWorkflow input to 1 on the UsdPreviewSurface shader.

Ns

roughness

Specular exponent. Requires conversion: roughness ≈ 1 - sqrt(Ns / 1000). Note: When the PBR extension property Pr is available, prefer using it directly as it already represents roughness in the 0-1 range without conversion.

Ni

ior

Index of refraction.

d

opacity

Dissolve (transparency). A value of 1.0 is fully opaque.

Tr

opacity

Transparency (inverse of d). opacity = 1 - Tr.

illum

N/A

Illumination model (0-10). Used to determine shader behavior but has no direct USD equivalent.

Texture Maps#

Texture maps in MTL files map to UsdUVTexture shader nodes connected to the appropriate UsdPreviewSurface inputs using UsdPrim. You also need to create UsdPrimvarReader shaders to indicate how the texture should be mapped to the UVs. The UsdPreviewSurface spec covers how to use UsdUVTexture and UsdPrimvarReader.

MTL Property

OpenUSD Connection

Description

map_Kd

diffuseColor

Diffuse texture map.

map_Ks

specularColor

Specular texture map.

map_Ns

roughness

Specular exponent texture (requires conversion).

map_d

opacity

Opacity/alpha texture map.

bump / map_bump

normal

Bump or normal map. Connect to the normal input of UsdPreviewSurface.

map_Ka

N/A or custom

Ambient texture map (limited USD support).

Considerations:

  • Many MTL textures require “repeat” wrapS/wrapT, which is not the default for the UsdUVTexture shader. There is no indication of the wrap mode in MTL nor in the PNG metadata. You may want to utilize repeating textures by default to minimize edge cases.

Limitations:

  • map_Kd values are multiplied by the Kd values to derive the RGB components. This is not possible to reproduce with UsdPreviewSurface.

PBR Extension Properties#

The PBR extension adds physically-based rendering properties to MTL files. These map more directly to UsdPreviewSurface:

MTL PBR Property

OpenUSD (UsdPreviewSurface)

Description

Pr

roughness

PBR roughness (0-1 range). Preferred over Ns conversion.

map_Pr

roughness (texture)

Roughness texture map. Preferred over map_Ns conversion.

Pm

metallic

PBR metallic (0-1 range).

map_Pm

metallic (texture)

Metallic texture map.

Ps

N/A or custom

Sheen. No direct UsdPreviewSurface equivalent.

Pc

clearcoat

Clearcoat amount (intensity).

Pcr

clearcoatRoughness

Clearcoat roughness.

Ke

emissiveColor

Emissive color (RGB).

map_Ke

emissiveColor (texture)

Emissive texture map.

norm

normal

Normal map. Preferred over bump for PBR workflows.

aniso

N/A or custom

Anisotropy. No direct UsdPreviewSurface equivalent.

anisor

N/A or custom

Anisotropy rotation. No direct UsdPreviewSurface equivalent.

Comments (#)#

Lines beginning with # in OBJ files are comments. These have no direct behavioral mapping in OpenUSD but can optionally be preserved as metadata.

OBJ

OpenUSD

Description

# <comment text>

documentation metadata

Comments may be stored in the documentation metadata field on prims or layers. Note: This may not be a perfect fit for all comment types, particularly inline or contextual comments.

Alternatively, comments can be stored as custom metadata using USD’s customData dictionary.

Appendices#

Appendix A: Object Naming#

OBJ files can have one or more objects. An object is declared with an object (o) command and all subsequent commands define that object until a new object (o) command is encountered. An OBJ may not have any object (o) command, in which case we suggest using the name of the OBJ file for the default object when it is mapped to a USD prim.

USD Naming Restrictions#

OpenUSD has strict naming requirements for prim and property names that may not align with OBJ object or group names. When converting OBJ names to USD identifiers, the following rules must be observed:

Valid USD Identifier Rules:

  • Must start with a letter (A-Z, a-z) or underscore (_)

  • Can contain only letters, numbers (0-9), and underscores

  • Cannot start with a number

  • Cannot contain spaces, hyphens, dots, or other special characters

  • Colons (:) are reserved for namespace separators and should not be used in base names

Name Sanitization Strategies:

When an OBJ object or group name contains invalid characters, apply the following transformations to create a valid USD identifier:

  1. Author displayName metadata: Store the original names in displayName metadata. This metadata can be used in applications to instead of the prim names to avoid renaming confusion for end users.

  2. Replace invalid characters: Convert spaces, hyphens, and other special characters to underscores. For example:

    • my-objectmy_object

    • my objectmy_object

    • object.001object_001

  3. Handle leading numbers: If a name starts with a number, prefix it with an underscore or a descriptive prefix. For example:

    • 123_mesh_123_mesh or object_123_mesh

  4. Preserve readability: When possible, maintain the original name’s readability while ensuring USD compliance.

  5. Handle name collisions: If sanitization results in duplicate names (e.g., my-object and my.object both become my_object), append a numeric suffix to ensure uniqueness:

    • First occurrence: my_object

    • Second occurrence: my_object_1

    • Third occurrence: my_object_2

  6. Empty or invalid names: If an object name is empty or becomes empty after sanitization, use a default name such as mesh_0, mesh_1, etc.

Best Practices:

  • Document the original names in displayName for traceability and end user convenience.

  • Consider providing a mapping file or log that shows the correspondence between original OBJ names and sanitized USD names.

  • Use consistent sanitization rules throughout the conversion process to ensure predictable results.

Appendix B: Vertex Data#

Vertex data consists of the following types: vertices (v), texture vertices (vt), vertex normals (vn), and parameter space vertices (vp). Each vertex data type is encoded as a single array per OBJ file. That means that the vertex data indices are global and could be shared between objects or groups. This is different from OpenUSD that defines these arrays on a per prim basis. Therefore, when authoring UsdGeomMesh or other point-based prims, developers should map the global index numbers to per mesh indices and account for that when mapping OBJ elements to OpenUSD prims.

Appendix C: Handling Separate Indices#

In OBJ, a face (f) command can reference different indices for positions, texture coordinates, and normals using the format f v/vt/vn. This allows vertices to share positions but have different UVs or normals (common at UV seams or hard edges). OpenUSD handles this differently; primvars like primvars:st and normals use interpolation modes rather than separate index arrays. To map OBJ’s separate indices to USD, there are two approaches:

Approach 1: Expanded Arrays

  1. Set the interpolation mode to faceVarying for both primvars:st and normals.

  2. Expand the UV and normal arrays so that each face-vertex has its own value, ordered to match faceVertexIndices.

This may result in duplicated UV/normal values, but it correctly preserves the per-face-vertex data from OBJ.

Approach 2: Indexed Primvars

  1. Set the interpolation mode to faceVarying for primvars:st.

  2. Create a compact array containing only unique UV values (de-duplicated).

  3. Author a separate index array primvars:st:indices that maps each face-vertex to the corresponding value in the compact array.

  4. Author primvars:normals directly with either vertex or faceVarying interpolation.

  5. Author a separate index array primvars:normals:indices that maps each face-vertex to the corresponding value in the compact array.

This approach reduces data duplication and is more memory-efficient, especially for meshes with many shared UV/normal values.

Both approaches are valid. The expanded arrays approach is simpler to implement, while the indexed primvars approach is more efficient for storage and memory usage.