Material Binding#
Materials in USD are assigned to geometry prims through the material:binding relationship. An ovstage write can change these bindings at runtime without reloading the scene.
To bind a material, write the material:binding attribute on the target geometry prim with the absolute path of the material prim as a path string.
Note
The material prim must already exist in the stage (loaded from USD). This operation changes which existing material is assigned to a prim – it does not create new materials.
Binding a Material#
Intern the material path, then write the relationship through an ovstage query with AttributeSemantic.RELATIONSHIP_PATH_ID.
material_binding = paths.intern_token("material:binding")
material_path = np.array([paths.intern_path("/World/Looks/srf_glass")], dtype=np.uint64)
stage.write_attribute(
query,
material_binding,
ordinal=2,
tensors=material_path,
is_array=True,
semantic=ovstage.AttributeSemantic.RELATIONSHIP_PATH_ID,
).wait()
stage.advance_write_floor(2, ovstage.Scope.ALL).wait()
Use the ovrtx_set_path_attributes() convenience helper from <ovrtx/ovrtx_attributes.h>. It wraps the path value into the single-element relationship array that USD requires.
// Bind /World/Looks/srf_glass as the material for /World/logo/logo/logo.
// Material bindings are relationships from a geometry prim to a material
// prim, expressed in ovstage as an array attribute (`material:binding`)
// whose entries are RELATIONSHIP_PATH_ID rows — one uint64 path handle
// (ovx_primpath_t) per target material.
path_dictionary_instance_t* pd = ovstage_get_path_dictionary(stage_);
ovx_string_t prim_path = ovx_str("/World/logo/logo/logo");
ovx_primpath_list_t path_list{};
ASSERT_EQ(path_dictionary_create_path_list_from_strings(pd, &prim_path, 1, &path_list).status,
OVX_API_SUCCESS);
ovstage_query_handle_t query_handle = OVSTAGE_INVALID_QUERY_HANDLE;
ASSERT_EQ(ovstage_query_from_path_list(stage_, path_list, &query_handle), OVSTAGE_OK)
<< format_ovstage_last_error();
ovx_string_t binding_str = ovx_str("material:binding");
ovx_token_t binding_token{};
ASSERT_EQ(path_dictionary_create_tokens_from_strings(pd, &binding_str, 1, &binding_token).status,
OVX_API_SUCCESS);
// Intern the target material path so we can write its uint64 handle.
ovx_string_t material_str = ovx_str("/World/Looks/srf_glass");
ovx_primpath_t material_path[1] = {};
ASSERT_EQ(path_dictionary_create_paths_from_strings(pd, &material_str, 1, material_path).status,
OVX_API_SUCCESS);
int64_t write_shape[1] = {1};
DLTensor write_tensor{};
write_tensor.data = material_path;
write_tensor.device = {kDLCPU, 0};
write_tensor.ndim = 1;
write_tensor.dtype = {kDLUInt, 64, 1};
write_tensor.shape = write_shape;
ovstage_write_data_t write_data{};
write_data.tensors = &write_tensor;
write_data.tensor_count = 1;
write_data.is_array = true;
write_data.semantic = OVSTAGE_SEMANTIC_RELATIONSHIP_PATH_ID;
ovx_string_or_token_t attr_ref{};
attr_ref.token = binding_token;
ovstage_enqueue_result_t wq = ovstage_write_attribute(
stage_, query_handle, attr_ref, /*ordinal=*/2, write_data, OVSTAGE_PRIM_MODE_UPSERT);
ASSERT_EQ(wq.status, OVSTAGE_OK) << format_ovstage_last_error();
docs_wait_ovstage_no_errors(stage_, wq.op_index);
docs_ovstage_advance_write_floor(stage_, 2);
ovstage_release_query(stage_, query_handle);
path_dictionary_release_path_list_reference(pd, path_list);