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#
Version |
25.11 |
|---|---|
Reference Documents |
OpenUSD C++ and Schema Documentation, OpenUSD Github Repository, USD Terms and Concepts |
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. |
|
Mesh |
An object with face (f) commands represents a polygon mesh. |
|
Scope, Xform, or GeomSubset |
Groups elements into named sets. See Groups for mapping approaches. |
|
normals |
Defines smoothing group membership for normal interpolation. |
|
MaterialBindingAPI::DirectBinding, [GeomSubset] |
This indicates that subsequent faces in the OBJ file should use the material specified. |
|
Sublayer |
This is a reference to an external MTL file. |
|
UsdShadeMaterial, UsdShadeShader |
Material definitions from MTL files. See MTL Material Library. |
|
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 |
|---|---|---|
points |
Vertices that form the faces of the mesh. |
|
primvars:st |
2D coordinates for mapping textures onto the faces of the mesh. |
|
normals |
3D vectors used for calculating the geometric normal of the mesh surface. |
|
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 |
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:
Create a separate USD layer for materials (e.g.,
materials.usda).Author each material from the MTL file as a UsdShadeMaterial prim in that layer.
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 |
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 |
Ns |
roughness |
Specular exponent. Requires conversion: roughness ≈ 1 - sqrt(Ns / 1000). Note: When the PBR extension property |
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. |
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:
Author displayName metadata: Store the original names in
displayNamemetadata. This metadata can be used in applications to instead of the prim names to avoid renaming confusion for end users.Replace invalid characters: Convert spaces, hyphens, and other special characters to underscores. For example:
my-object→my_objectmy object→my_objectobject.001→object_001
Handle leading numbers: If a name starts with a number, prefix it with an underscore or a descriptive prefix. For example:
123_mesh→_123_meshorobject_123_mesh
Preserve readability: When possible, maintain the original name’s readability while ensuring USD compliance.
Handle name collisions: If sanitization results in duplicate names (e.g.,
my-objectandmy.objectboth becomemy_object), append a numeric suffix to ensure uniqueness:First occurrence:
my_objectSecond occurrence:
my_object_1Third occurrence:
my_object_2
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
displayNamefor 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
Set the interpolation mode to
faceVaryingfor bothprimvars:standnormals.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
Set the interpolation mode to
faceVaryingforprimvars:st.Create a compact array containing only unique UV values (de-duplicated).
Author a separate index array
primvars:st:indicesthat maps each face-vertex to the corresponding value in the compact array.Author
primvars:normalsdirectly with eithervertexorfaceVaryinginterpolation.Author a separate index array
primvars:normals:indicesthat 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.
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
documentationmetadata 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.