#include "hydra_bridge.h"
#include <pxr/base/gf/frustum.h>
#include <pxr/base/gf/half.h>
#include <pxr/base/gf/matrix4d.h>
#include <pxr/base/gf/vec3d.h>
#include <pxr/base/gf/vec4d.h>
#include <pxr/base/gf/vec4f.h>
#include <pxr/base/vt/value.h>
#include <pxr/imaging/glf/simpleLight.h>
#include <pxr/imaging/glf/simpleMaterial.h>
#include <pxr/imaging/hd/aov.h>
#include <pxr/imaging/hd/driver.h>
#include <pxr/imaging/hd/renderBuffer.h>
#include <pxr/imaging/hd/rendererPluginRegistry.h>
#include <pxr/imaging/hd/types.h>
#include <pxr/imaging/hdx/tokens.h>
#include <pxr/imaging/hf/pluginDesc.h>
#include <pxr/imaging/hgi/hgi.h>
#include <pxr/imaging/hgi/tokens.h>
#include <pxr/usd/usd/timeCode.h>
#include <pxr/usdImaging/usdImagingGL/engine.h>
#include <pxr/usdImaging/usdImagingGL/renderParams.h>
#include <algorithm>
#include <cstring>
#include <functional>
#include <stdexcept>
namespace hydra_rs {
std::unique_ptr<SceneIndex> populate_from_path(rust::Str usd_path) {
std::string p(usd_path.data(), usd_path.size());
pxr::UsdStageRefPtr stage = pxr::UsdStage::Open(p);
if (!stage) {
throw std::runtime_error("UsdStage::Open returned null for: " + p);
}
auto si = pxr::UsdImagingStageSceneIndex::New();
si->SetStage(stage);
si->SetTime(pxr::UsdTimeCode::Default());
auto wrapped = std::make_unique<SceneIndex>();
wrapped->stage_owner = stage;
wrapped->scene_index = si;
return wrapped;
}
rust::String SceneIndex::stage_root() const {
if (!stage_owner) return rust::String();
auto root = stage_owner->GetRootLayer();
if (!root) return rust::String();
return rust::String(root->GetIdentifier());
}
size_t SceneIndex::prim_count() const {
if (!scene_index) return 0;
size_t count = 0;
std::function<void(const pxr::SdfPath&)> walk = [&](const pxr::SdfPath& p) {
++count;
for (const auto& child : scene_index->GetChildPrimPaths(p)) {
walk(child);
}
};
walk(pxr::SdfPath::AbsoluteRootPath());
return count;
}
std::unique_ptr<std::vector<std::string>> SceneIndex::prim_paths() const {
auto vec = std::make_unique<std::vector<std::string>>();
if (!scene_index) return vec;
std::function<void(const pxr::SdfPath&)> walk = [&](const pxr::SdfPath& p) {
vec->push_back(p.GetString());
for (const auto& child : scene_index->GetChildPrimPaths(p)) {
walk(child);
}
};
walk(pxr::SdfPath::AbsoluteRootPath());
return vec;
}
std::unique_ptr<std::vector<std::string>> list_render_delegate_ids() {
auto vec = std::make_unique<std::vector<std::string>>();
auto& reg = pxr::HdRendererPluginRegistry::GetInstance();
pxr::HfPluginDescVector descs;
reg.GetPluginDescs(&descs);
vec->reserve(descs.size());
for (const auto& d : descs) {
vec->push_back(d.id.GetString());
}
return vec;
}
std::unique_ptr<std::vector<uint8_t>> render_to_rgba(
rust::Str usd_path,
rust::Str render_delegate_id,
uint32_t width,
uint32_t height)
{
if (width == 0 || height == 0) {
throw std::runtime_error("render_to_rgba: width and height must be > 0");
}
std::string p(usd_path.data(), usd_path.size());
pxr::UsdStageRefPtr stage = pxr::UsdStage::Open(p);
if (!stage) {
throw std::runtime_error("UsdStage::Open returned null for: " + p);
}
pxr::HgiUniquePtr hgi = pxr::Hgi::CreatePlatformDefaultHgi();
if (!hgi) {
throw std::runtime_error(
"Hgi::CreatePlatformDefaultHgi returned null — no usable GPU "
"backend. On macOS, expected HgiMetal; on Linux, HgiGL/Vulkan.");
}
pxr::HdDriver hd_driver;
hd_driver.name = pxr::HgiTokens->renderDriver;
hd_driver.driver = pxr::VtValue(hgi.get());
pxr::UsdImagingGLEngine engine(hd_driver);
engine.SetEnablePresentation(false);
std::string delegate_str(render_delegate_id.data(), render_delegate_id.size());
if (!delegate_str.empty()) {
if (!engine.SetRendererPlugin(pxr::TfToken(delegate_str))) {
throw std::runtime_error("SetRendererPlugin failed: " + delegate_str);
}
}
pxr::GfMatrix4d view_mat;
view_mat.SetLookAt(
pxr::GfVec3d(5.0, 5.0, 5.0),
pxr::GfVec3d(0.0, 0.0, 0.0),
pxr::GfVec3d(0.0, 1.0, 0.0));
pxr::GfFrustum frustum;
const double aspect = static_cast<double>(width) / static_cast<double>(height);
frustum.SetPerspective(45.0, aspect, 0.1, 1000.0);
pxr::GfMatrix4d proj_mat = frustum.ComputeProjectionMatrix();
engine.SetCameraState(view_mat, proj_mat);
engine.SetRenderViewport(pxr::GfVec4d(0.0, 0.0,
static_cast<double>(width),
static_cast<double>(height)));
engine.SetRendererAov(pxr::HdAovTokens->color);
pxr::GlfSimpleLight key_light;
pxr::GfVec4f key_pos(2.5f, 4.0f, 5.0f, 1.0f); key_light.SetPosition(key_pos);
key_light.SetAmbient(pxr::GfVec4f(0.0f, 0.0f, 0.0f, 1.0f));
key_light.SetDiffuse(pxr::GfVec4f(1.0f, 1.0f, 1.0f, 1.0f));
key_light.SetSpecular(pxr::GfVec4f(1.0f, 1.0f, 1.0f, 1.0f));
pxr::GlfSimpleLightVector lights = {key_light};
pxr::GlfSimpleMaterial material;
material.SetAmbient(pxr::GfVec4f(0.2f, 0.2f, 0.2f, 1.0f));
material.SetDiffuse(pxr::GfVec4f(1.0f, 1.0f, 1.0f, 1.0f));
pxr::GfVec4f scene_ambient(0.1f, 0.1f, 0.1f, 1.0f);
engine.SetLightingState(lights, material, scene_ambient);
pxr::UsdImagingGLRenderParams params;
params.frame = pxr::UsdTimeCode::Default();
params.complexity = 1.0f;
params.enableLighting = true;
params.enableSceneLights = false; params.enableSceneMaterials = true;
params.clearColor = pxr::GfVec4f(0.1f, 0.1f, 0.15f, 1.0f);
engine.Render(stage->GetPseudoRoot(), params);
int safety = 0;
while (!engine.IsConverged() && safety < 256) {
engine.Render(stage->GetPseudoRoot(), params);
++safety;
}
pxr::HdRenderBuffer* color = engine.GetAovRenderBuffer(pxr::HdAovTokens->color);
if (!color) {
throw std::runtime_error("UsdImagingGLEngine has no color render buffer");
}
color->Resolve();
const uint32_t bw = color->GetWidth();
const uint32_t bh = color->GetHeight();
const pxr::HdFormat fmt = color->GetFormat();
void* mapped = color->Map();
if (!mapped) {
throw std::runtime_error("HdRenderBuffer::Map returned null — render may have failed");
}
auto out = std::make_unique<std::vector<uint8_t>>();
out->resize(static_cast<size_t>(bw) * bh * 4);
if (fmt == pxr::HdFormatFloat32Vec4) {
const float* src = static_cast<const float*>(mapped);
for (size_t i = 0; i < static_cast<size_t>(bw) * bh; ++i) {
for (int c = 0; c < 4; ++c) {
float v = src[i * 4 + c];
v = std::max(0.0f, std::min(1.0f, v));
(*out)[i * 4 + c] = static_cast<uint8_t>(v * 255.0f + 0.5f);
}
}
} else if (fmt == pxr::HdFormatFloat16Vec4) {
const pxr::GfHalf* src = static_cast<const pxr::GfHalf*>(mapped);
for (size_t i = 0; i < static_cast<size_t>(bw) * bh; ++i) {
for (int c = 0; c < 4; ++c) {
float v = static_cast<float>(src[i * 4 + c]);
v = std::max(0.0f, std::min(1.0f, v));
(*out)[i * 4 + c] = static_cast<uint8_t>(v * 255.0f + 0.5f);
}
}
} else if (fmt == pxr::HdFormatUNorm8Vec4) {
std::memcpy(out->data(), mapped, out->size());
} else {
color->Unmap();
throw std::runtime_error(
"Unsupported render buffer format from delegate (HdFormat=" +
std::to_string(static_cast<int>(fmt)) + ")");
}
color->Unmap();
return out;
}
}