C: Decals#

Loads the same local decal scene as the Python example through an attached runtime stage, renders it, and saves the color output.

“Create a minimal C API example that loads USD-authored decals through a runtime stage, renders them, and saves the color output.”

Red RTX lettering and three silver NVIDIA logos distributed across a curved green logo

Enlarged silver NVIDIA logos (priority 10) overlap red RTX lettering (priority 0). Cutouts reveal the lower decal or the green receiver.#

Both examples consume the same USD authoring. For decal sizing, masks, and the priority comparison with equivalent USDA and usd-core Python snippets, see Overlapping Decals and Python: Decals.

The example populates the stage, advances its write floor, and renders the scene:

constexpr ovstage_ordinal_t ordinal = 1;
std::filesystem::path executable_dir =
    std::filesystem::absolute(argc > 0 ? argv[0] : "decals").parent_path();
std::string scene_path = (executable_dir / "ovrtx-test-decals.usda").string();
auto populate = ovstage_population_open_usd_from_file(
    stage,
    {scene_path.c_str(), scene_path.size()},
    ordinal,
    0.0,
    OVSTAGE_POPULATION_DOMAIN_RENDERING);
ovstage_write_floor_desc_t floor {ordinal, OVSTAGE_SCOPE_ALL};
if (wait_population(stage, populate) ||
    wait_stage(stage, ovstage_advance_write_floor(stage, &floor))) {
    std::cerr << "Could not populate the decal scene." << std::endl;
    return cleanup(1);
}

ovx_string_t product_path = {"/Render/Camera", strlen("/Render/Camera")};
ovrtx_render_product_set_t render_products {&product_path, 1};
ovrtx_step_result_handle_t final_handle = OVRTX_INVALID_HANDLE;
for (int frame = 0; frame <= 40; ++frame) {
    ovrtx_step_result_handle_t step_handle = OVRTX_INVALID_HANDLE;
    auto enqueue = ovrtx_step_with_stage(
        renderer, render_products, 1.0 / 60.0, ordinal, &step_handle);
    ovrtx_op_wait_result_t wait_result {};
    if (failed(enqueue, "step") ||
        failed(ovrtx_wait_op(renderer,
                             enqueue.op_index,
                             ovrtx_timeout_infinite,
                             &wait_result),
               "wait_op") ||
        report_wait_errors(wait_result)) {
        return cleanup(1);
    }
    if (frame < 40) {
        ovrtx_destroy_results(renderer, step_handle);
    } else {
        final_handle = step_handle;
    }
}

It maps LdrColor to CPU memory and writes a PNG:

ovrtx_render_product_set_outputs_t outputs {};
if (failed(ovrtx_fetch_results(
               renderer, final_handle, ovrtx_timeout_infinite, &outputs),
           "fetch_results")) {
    return cleanup(1);
}
auto output_handle = find_ldr_color(outputs);
ovrtx_map_output_description_t map_desc {};
map_desc.device_type = OVRTX_MAP_DEVICE_TYPE_CPU;
ovrtx_render_var_output_t output {};
if (output_handle == OVRTX_INVALID_HANDLE ||
    failed(ovrtx_map_render_var_output(renderer,
                                       output_handle,
                                       &map_desc,
                                       ovrtx_timeout_infinite,
                                       &output),
           "map_render_var_output") ||
    output.num_tensors != 1) {
    return cleanup(1);
}
DLTensor const& pixels = *output.tensors[0].dl;
int width = static_cast<int>(pixels.shape[1]);
int height = static_cast<int>(pixels.shape[0]);
std::filesystem::path output_path = executable_dir / "decals.png";
bool wrote_output =
    stbi_write_png(output_path.string().c_str(), width, height, 4, pixels.data, 4 * width) != 0;
ovrtx_cuda_sync_t no_sync {};
ovrtx_unmap_render_var_output(renderer, output.map_handle, no_sync);
ovrtx_destroy_results(renderer, final_handle);
if (!wrote_output) {
    std::cerr << "Could not write " << output_path << std::endl;
    return cleanup(1);
}
std::cout << "Saved " << output_path << std::endl;

Prerequisites#

  • CMake 3.16 or newer and a C++17 compiler

  • An NVIDIA RTX-capable GPU and a supported driver

Building and Running#

cd examples/c/decals
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release

Run decals-dynamic from build/shared for a single-configuration generator or build/shared/Release for a multi-configuration generator. A successful run writes decals.png beside the executable.