Add Chromium-only Blender WebEngine parity work

This commit is contained in:
mes123456
2026-08-12 04:47:48 -04:00
commit 9fd26010f6
18225 changed files with 11622124 additions and 0 deletions

View File

@@ -0,0 +1,205 @@
# SPDX-FileCopyrightText: 2022 Blender Foundation
#
# SPDX-License-Identifier: Apache-2.0
#####################################################################
# Cycles Hydra render delegate
#####################################################################
set(INC
..
)
set(INC_SYS
)
set(LIB
cycles_scene
cycles_session
cycles_graph
bf::dependencies::epoxy
)
cycles_external_libraries_append(LIB)
set(SRC_HD_CYCLES_HEADERS
camera.h
config.h
curves.h
field.h
file_reader.h
geometry.h
geometry.inl
instancer.h
light.h
material.h
mesh.h
node_util.h
output_driver.h
pointcloud.h
render_buffer.h
render_delegate.h
render_pass.h
session.h
util.h
volume.h
)
set(SRC_HD_CYCLES
curves.cpp
camera.cpp
field.cpp
file_reader.cpp
geometry.cpp
instancer.cpp
light.cpp
material.cpp
mesh.cpp
node_util.cpp
output_driver.cpp
pointcloud.cpp
render_buffer.cpp
render_delegate.cpp
render_pass.cpp
session.cpp
util.cpp
volume.cpp
)
# Blender libraries do not include hgiGL, so build without display driver then.
if(EXISTS ${USD_INCLUDE_DIR}/pxr/imaging/hgiGL)
add_definitions(-DWITH_HYDRA_DISPLAY_DRIVER)
list(APPEND SRC_HD_CYCLES display_driver.cpp)
list(APPEND SRC_HD_CYCLES_HEADERS display_driver.h)
endif()
# Silence warning from USD headers using deprecated TBB header.
add_definitions(
-D__TBB_show_deprecation_message_atomic_H
-D__TBB_show_deprecation_message_task_H
)
include_directories(${INC})
include_directories(SYSTEM ${INC_SYS})
add_library(cycles_hydra STATIC
${SRC_HD_CYCLES}
${SRC_HD_CYCLES_HEADERS}
)
target_compile_options(cycles_hydra
PRIVATE
$<$<CXX_COMPILER_ID:MSVC>:/wd4003 /wd4244 /wd4506>
$<$<CXX_COMPILER_ID:GNU>:-Wno-float-conversion -Wno-double-promotion -Wno-deprecated>
)
target_compile_definitions(cycles_hydra
PRIVATE
GLOG_NO_ABBREVIATED_SEVERITIES=1
OSL_DEBUG=$<CONFIG:DEBUG>
TBB_USE_DEBUG=$<CONFIG:DEBUG>
$<$<CXX_COMPILER_ID:MSVC>:NOMINMAX=1>
)
target_link_libraries(cycles_hydra
PUBLIC
bf::dependencies::openimageio
bf::dependencies::optional::tbb
bf::dependencies::optional::usd
bf::dependencies::optional::python
PRIVATE
${LIB}
)
if(WITH_CYCLES_HYDRA_RENDER_DELEGATE)
set(SRC_HD_CYCLES_PLUGIN
plugin.h
plugin.cpp
)
set(HdCyclesPluginName hdCycles)
add_library(${HdCyclesPluginName} SHARED ${SRC_HD_CYCLES_PLUGIN})
set_target_properties(${HdCyclesPluginName}
PROPERTIES PREFIX ""
)
target_compile_definitions(${HdCyclesPluginName}
PRIVATE
MFB_PACKAGE_NAME=${HdCyclesPluginName}
MFB_ALT_PACKAGE_NAME=${HdCyclesPluginName}
GLOG_NO_ABBREVIATED_SEVERITIES=1
OSL_DEBUG=$<CONFIG:DEBUG>
TBB_USE_DEBUG=$<CONFIG:DEBUG>
$<$<CXX_COMPILER_ID:MSVC>:NOMINMAX=1>
)
target_link_libraries(${HdCyclesPluginName}
cycles_hydra
)
if(APPLE)
set_property(
TARGET
${HdCyclesPluginName}
APPEND_STRING PROPERTY LINK_FLAGS
" -Wl,-exported_symbols_list,'${CMAKE_CURRENT_SOURCE_DIR}/resources/apple_symbols.map'"
)
elseif(UNIX)
set_property(
TARGET
${HdCyclesPluginName}
APPEND_STRING PROPERTY LINK_FLAGS
" -Wl,--version-script='${CMAKE_CURRENT_SOURCE_DIR}/resources/linux_symbols.map'"
)
endif()
if(WITH_CYCLES_BLENDER)
# Install inside add-on
set(CYCLES_HYDRA_INSTALL_PATH ${CYCLES_INSTALL_PATH}/hydra)
else()
# Install next to cycles executable
set(CYCLES_HYDRA_INSTALL_PATH ${CMAKE_INSTALL_PREFIX}/hydra)
endif()
# Put the root `plugInfo.json` one level up.
delayed_install("${CMAKE_CURRENT_SOURCE_DIR}" "plugInfo.json" ${CYCLES_HYDRA_INSTALL_PATH})
delayed_install("" $<TARGET_FILE:${HdCyclesPluginName}> ${CYCLES_HYDRA_INSTALL_PATH})
set_target_properties(${HdCyclesPluginName}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
set(PLUG_INFO_ROOT "..")
if(WITH_CYCLES_BLENDER)
# Full path not yet known at this point and RELATIVE_PATH requires
# absolute path as input. So just set manually.
set(PLUG_INFO_LIBRARY_PATH "../${HdCyclesPluginName}${CMAKE_SHARED_LIBRARY_SUFFIX}")
set(PLUG_INFO_RESOURCE_PATH "../..")
else()
file(RELATIVE_PATH
PLUG_INFO_LIBRARY_PATH
"${CYCLES_HYDRA_INSTALL_PATH}/${HdCyclesPluginName}"
"${CYCLES_HYDRA_INSTALL_PATH}/${HdCyclesPluginName}${CMAKE_SHARED_LIBRARY_SUFFIX}")
file(RELATIVE_PATH PLUG_INFO_RESOURCE_PATH
"${CYCLES_HYDRA_INSTALL_PATH}/${HdCyclesPluginName}"
"${CYCLES_INSTALL_PATH}")
endif()
configure_file(resources/plugInfo.json
${CMAKE_CURRENT_BINARY_DIR}/resources/plugInfo.json
@ONLY
)
delayed_install("${CMAKE_CURRENT_BINARY_DIR}/resources" "plugInfo.json" "${CYCLES_HYDRA_INSTALL_PATH}/${HdCyclesPluginName}/resources")
if(WITH_CYCLES_BLENDER)
get_filename_component(_addons_core_dir "${CYCLES_INSTALL_PATH}" DIRECTORY)
delayed_install(
"${CMAKE_CURRENT_SOURCE_DIR}/addon"
"__init__.py"
"${_addons_core_dir}/hydra_cycles"
)
unset(_addons_core_dir)
endif()
endif()

View File

@@ -0,0 +1,87 @@
# SPDX-FileCopyrightText: 2026 Blender Authors
#
# SPDX-License-Identifier: Apache-2.0
# Basic add-on for the Cycles Hydra render delegate. This is very incomplete
# and intended for developer testing only. The most obvious limitation is that
# materials and render settings are not supported.
import bpy
bl_info = {
"name": "Hydra Cycles render engine",
"author": "Blender Foundation",
"version": (0, 1, 0),
"blender": (5, 0, 0),
"description": "Cycles path tracing renderer using the Hydra render delegate",
"support": 'OFFICIAL',
"category": "Render",
}
class CyclesHydraRenderEngine(bpy.types.HydraRenderEngine):
bl_idname = 'HYDRA_CYCLES'
bl_label = "Hydra Cycles"
bl_info = "Cycles path tracing renderer using the Hydra render delegate"
bl_use_preview = False
bl_use_gpu_context = False
bl_use_materialx = False
bl_delegate_id = 'HdCyclesPlugin'
@classmethod
def register(cls):
bpy.utils.expose_bundled_modules()
import os
plugin_dir = os.path.normpath(
os.path.join(os.path.dirname(__file__), "..", "cycles", "hydra")
)
if not os.path.isfile(os.path.join(plugin_dir, "plugInfo.json")):
print("Hydra Cycles: plugInfo.json not found at", plugin_dir)
return
import pxr.Plug
pxr.Plug.Registry().RegisterPlugins([plugin_dir])
def get_render_settings(self, engine_type):
cscene = bpy.context.scene.cycles
samples = cscene.preview_samples if engine_type == 'VIEWPORT' else cscene.samples
result = {'cycles:samples': samples}
if engine_type != 'VIEWPORT':
result |= {
'aovToken:Combined': "color",
'aovToken:Depth': "depth",
}
return result
def update_render_passes(self, scene, render_layer):
if render_layer.use_pass_combined:
self.register_pass(scene, render_layer, 'Combined', 4, 'RGBA', 'COLOR')
if render_layer.use_pass_z:
self.register_pass(scene, render_layer, 'Depth', 1, 'Z', 'VALUE')
def _shared_panels():
# Use all the same panels as regular Cycles, even if most options are
# currently not supported. But for the ones that are supported it's not
# worth making custom panels just for developer testing.
for panel in bpy.types.Panel.__subclasses__():
engines = getattr(panel, 'COMPAT_ENGINES', None)
if engines and 'CYCLES' in engines:
yield panel
def register():
bpy.utils.register_class(CyclesHydraRenderEngine)
for panel in _shared_panels():
panel.COMPAT_ENGINES.add(CyclesHydraRenderEngine.bl_idname)
def unregister():
for panel in _shared_panels():
panel.COMPAT_ENGINES.discard(CyclesHydraRenderEngine.bl_idname)
bpy.utils.unregister_class(CyclesHydraRenderEngine)

View File

@@ -0,0 +1,252 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "hydra/camera.h"
#include "hydra/session.h"
#include "hydra/util.h"
#include "scene/camera.h"
#include <pxr/base/gf/frustum.h>
#include <pxr/imaging/hd/cameraSchema.h>
#include <pxr/imaging/hd/sceneDelegate.h>
#include <pxr/imaging/hd/xformSchema.h>
#include <pxr/usd/usdGeom/tokens.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
extern Transform convert_transform(const GfMatrix4d &matrix);
static Transform convert_camera_transform(const GfMatrix4d &matrix, const float metersPerUnit)
{
Transform t = convert_transform(matrix);
// Flip Z axis
t.x.z *= -1.0f;
t.y.z *= -1.0f;
t.z.z *= -1.0f;
// Scale translation
t.x.w *= metersPerUnit;
t.y.w *= metersPerUnit;
t.z.w *= metersPerUnit;
return t;
}
HdCyclesCamera::HdCyclesCamera(const SdfPath &sprimId) : HdCamera(sprimId)
{
// Synchronize default values
_horizontalAperture = _data.GetHorizontalAperture() * GfCamera::APERTURE_UNIT;
_verticalAperture = _data.GetVerticalAperture() * GfCamera::APERTURE_UNIT;
_horizontalApertureOffset = _data.GetHorizontalApertureOffset() * GfCamera::APERTURE_UNIT;
_verticalApertureOffset = _data.GetVerticalApertureOffset() * GfCamera::APERTURE_UNIT;
_focalLength = _data.GetFocalLength() * GfCamera::FOCAL_LENGTH_UNIT;
_clippingRange = _data.GetClippingRange();
_fStop = _data.GetFStop();
_focusDistance = _data.GetFocusDistance();
}
HdCyclesCamera::~HdCyclesCamera() = default;
HdDirtyBits HdCyclesCamera::GetInitialDirtyBitsMask() const
{
return DirtyBits::AllDirty;
}
void HdCyclesCamera::Sync(HdSceneDelegate *sceneDelegate,
HdRenderParam * /*renderParam*/,
HdDirtyBits *dirtyBits)
{
if (*dirtyBits == DirtyBits::Clean) {
return;
}
const SdfPath &id = GetId();
const HdSceneIndexPrim prim = GetPrim(sceneDelegate, id);
const HdContainerDataSourceHandle &primDs = prim.dataSource;
HdCameraSchema cameraSchema = HdCameraSchema::GetFromParent(primDs);
HdContainerDataSourceHandle cameraDs = cameraSchema.GetContainer();
/* Read the shutter window from the camera prim so animated transforms can
* be sampled across it for motion blur. */
float shutterOpen = 0.0f;
float shutterClose = 0.0f;
if (auto ds = cameraSchema.GetShutterOpen()) {
shutterOpen = float(ds->GetTypedValue(0.0f));
}
if (auto ds = cameraSchema.GetShutterClose()) {
shutterClose = float(ds->GetTypedValue(0.0f));
}
if (*dirtyBits & DirtyBits::DirtyTransform) {
HdXformSchema xformSchema = HdXformSchema::GetFromParent(primDs);
if (auto matrixDs = xformSchema.GetMatrix()) {
SampleTyped<GfMatrix4d, 2>(matrixDs, shutterOpen, shutterClose, &_transformSamples);
bool transform_found = false;
for (size_t i = 0; i < _transformSamples.count; ++i) {
if (_transformSamples.times[i] == 0.0f) {
_transform = _transformSamples.values[i];
_data.SetTransform(_transform);
transform_found = true;
break;
}
}
if (!transform_found && _transformSamples.count) {
_transform = _transformSamples.values[0];
_data.SetTransform(_transform);
}
}
}
if (*dirtyBits & DirtyBits::DirtyWindowPolicy) {
/* The window policy is not part of the camera schema; it lives on the
* legacy data source. Leave the default of `CameraUtilFit`. */
}
if (*dirtyBits & DirtyBits::DirtyClipPlanes) {
if (auto ds = cameraSchema.GetClippingPlanes()) {
const VtArray<GfVec4d> clipPlanes = ds->GetTypedValue(0.0f);
_clipPlanes.assign(clipPlanes.cbegin(), clipPlanes.cend());
}
}
if (*dirtyBits & DirtyBits::DirtyParams) {
if (auto ds = cameraSchema.GetProjection()) {
const TfToken projection = ds->GetTypedValue(0.0f);
_projection = (projection == HdCameraSchemaTokens->orthographic) ? Orthographic :
Perspective;
_data.SetProjection(_projection != Orthographic ? GfCamera::Perspective :
GfCamera::Orthographic);
}
if (auto ds = cameraSchema.GetHorizontalAperture()) {
const float horizontalAperture = ds->GetTypedValue(0.0f);
_horizontalAperture = horizontalAperture;
_data.SetHorizontalAperture(horizontalAperture / GfCamera::APERTURE_UNIT);
}
if (auto ds = cameraSchema.GetVerticalAperture()) {
const float verticalAperture = ds->GetTypedValue(0.0f);
_verticalAperture = verticalAperture;
_data.SetVerticalAperture(verticalAperture / GfCamera::APERTURE_UNIT);
}
if (auto ds = cameraSchema.GetHorizontalApertureOffset()) {
const float horizontalApertureOffset = ds->GetTypedValue(0.0f);
_horizontalApertureOffset = horizontalApertureOffset;
_data.SetHorizontalApertureOffset(horizontalApertureOffset / GfCamera::APERTURE_UNIT);
}
if (auto ds = cameraSchema.GetVerticalApertureOffset()) {
const float verticalApertureOffset = ds->GetTypedValue(0.0f);
_verticalApertureOffset = verticalApertureOffset;
_data.SetVerticalApertureOffset(verticalApertureOffset / GfCamera::APERTURE_UNIT);
}
if (auto ds = cameraSchema.GetFocalLength()) {
const float focalLength = ds->GetTypedValue(0.0f);
_focalLength = focalLength;
_data.SetFocalLength(focalLength / GfCamera::FOCAL_LENGTH_UNIT);
}
if (auto ds = cameraSchema.GetClippingRange()) {
const GfVec2f range = ds->GetTypedValue(0.0f);
const GfRange1f clippingRange(range[0], range[1]);
_clippingRange = clippingRange;
_data.SetClippingRange(clippingRange);
}
if (auto ds = cameraSchema.GetFStop()) {
const float fStop = ds->GetTypedValue(0.0f);
_fStop = fStop;
_data.SetFStop(fStop);
}
if (auto ds = cameraSchema.GetFocusDistance()) {
const float focusDistance = ds->GetTypedValue(0.0f);
_focusDistance = focusDistance;
_data.SetFocusDistance(focusDistance);
}
}
*dirtyBits = DirtyBits::Clean;
}
void HdCyclesCamera::Finalize(HdRenderParam *renderParam)
{
HdCamera::Finalize(renderParam);
}
void HdCyclesCamera::ApplyCameraSettings(HdRenderParam *renderParam, Camera *cam) const
{
ApplyCameraSettings(renderParam, _data, _windowPolicy, cam);
const float metersPerUnit = static_cast<HdCyclesSession *>(renderParam)->GetStageMetersPerUnit();
array<Transform> motion(_transformSamples.count);
for (size_t i = 0; i < _transformSamples.count; ++i) {
motion[i] = convert_camera_transform(_transformSamples.values[i], metersPerUnit);
}
cam->set_motion(motion);
}
void HdCyclesCamera::ApplyCameraSettings(HdRenderParam *renderParam,
const GfCamera &dataUnconformedWindow,
CameraUtilConformWindowPolicy windowPolicy,
Camera *cam)
{
const float width = cam->get_full_width();
const float height = cam->get_full_height();
auto data = dataUnconformedWindow;
CameraUtilConformWindow(&data, windowPolicy, width / height);
if (data.GetProjection() == GfCamera::Orthographic) {
cam->set_camera_type(CAMERA_ORTHOGRAPHIC);
}
else {
cam->set_camera_type(CAMERA_PERSPECTIVE);
}
const float metersPerUnit = static_cast<HdCyclesSession *>(renderParam)->GetStageMetersPerUnit();
auto viewplane = data.GetFrustum().GetWindow();
auto focalLength = 1.0f;
if (data.GetProjection() == GfCamera::Perspective) {
viewplane *= 2.0 / viewplane.GetSize()[1]; // Normalize viewplane
focalLength = data.GetFocalLength() * GfCamera::FOCAL_LENGTH_UNIT * metersPerUnit;
cam->set_fov(GfDegreesToRadians(data.GetFieldOfView(GfCamera::FOVVertical)));
}
cam->set_sensorwidth(data.GetHorizontalAperture() * GfCamera::APERTURE_UNIT * metersPerUnit);
cam->set_sensorheight(data.GetVerticalAperture() * GfCamera::APERTURE_UNIT * metersPerUnit);
cam->set_nearclip(data.GetClippingRange().GetMin() * metersPerUnit);
cam->set_farclip(data.GetClippingRange().GetMax() * metersPerUnit);
cam->set_viewplane_left(viewplane.GetMin()[0]);
cam->set_viewplane_right(viewplane.GetMax()[0]);
cam->set_viewplane_bottom(viewplane.GetMin()[1]);
cam->set_viewplane_top(viewplane.GetMax()[1]);
if (data.GetFStop() != 0.0f) {
cam->set_focaldistance(data.GetFocusDistance() * metersPerUnit);
cam->set_aperturesize(focalLength / (2.0f * data.GetFStop()));
}
cam->set_matrix(convert_camera_transform(data.GetTransform(), metersPerUnit));
}
void HdCyclesCamera::ApplyCameraSettings(HdRenderParam *renderParam,
const GfMatrix4d &worldToViewMatrix,
const GfMatrix4d &projectionMatrix,
const std::vector<GfVec4d> & /*clipPlanes*/,
Camera *cam)
{
GfCamera data;
data.SetFromViewAndProjectionMatrix(worldToViewMatrix, projectionMatrix);
ApplyCameraSettings(renderParam, data, CameraUtilFit, cam);
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,46 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "hydra/config.h"
#include <pxr/base/gf/camera.h>
#include <pxr/imaging/hd/camera.h>
#include <pxr/imaging/hd/timeSampleArray.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesCamera final : public PXR_NS::HdCamera {
public:
HdCyclesCamera(const PXR_NS::SdfPath &sprimId);
~HdCyclesCamera() override;
void ApplyCameraSettings(PXR_NS::HdRenderParam *renderParam, CCL_NS::Camera *cam) const;
static void ApplyCameraSettings(PXR_NS::HdRenderParam *renderParam,
const PXR_NS::GfCamera &dataUnconformedWindow,
PXR_NS::CameraUtilConformWindowPolicy windowPolicy,
CCL_NS::Camera *cam);
static void ApplyCameraSettings(PXR_NS::HdRenderParam *renderParam,
const PXR_NS::GfMatrix4d &worldToViewMatrix,
const PXR_NS::GfMatrix4d &projectionMatrix,
const std::vector<PXR_NS::GfVec4d> &clipPlanes,
CCL_NS::Camera *cam);
PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override;
void Sync(PXR_NS::HdSceneDelegate *sceneDelegate,
PXR_NS::HdRenderParam *renderParam,
PXR_NS::HdDirtyBits *dirtyBits) override;
void Finalize(PXR_NS::HdRenderParam *renderParam) override;
private:
PXR_NS::GfCamera _data;
PXR_NS::HdTimeSampleArray<PXR_NS::GfMatrix4d, 2> _transformSamples;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,46 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include <pxr/pxr.h> // IWYU pragma: export
#define CCL_NS ccl
#define CCL_NAMESPACE_USING_DIRECTIVE using namespace CCL_NS;
#define HD_CYCLES_NS HdCycles
#define HDCYCLES_NAMESPACE_OPEN_SCOPE \
namespace HD_CYCLES_NS { \
CCL_NAMESPACE_USING_DIRECTIVE; \
PXR_NAMESPACE_USING_DIRECTIVE;
#define HDCYCLES_NAMESPACE_CLOSE_SCOPE }
namespace HD_CYCLES_NS {
class HdCyclesCamera;
class HdCyclesDelegate;
class HdCyclesSession;
class HdCyclesRenderBuffer;
} // namespace HD_CYCLES_NS
namespace CCL_NS {
class AttributeSet;
class BufferParams;
class Camera;
class Geometry;
class Hair;
class Light;
class Mesh;
class Object;
class ParticleSystem;
class Pass;
class PointCloud;
class Scene;
class Session;
class SessionParams;
class Shader;
class ShaderGraph;
class ShaderNode;
class Volume;
} // namespace CCL_NS

View File

@@ -0,0 +1,226 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "hydra/curves.h"
#include "hydra/geometry.inl"
#include "hydra/util.h"
#include "scene/hair.h"
#include "util/types_float3.h"
#include <pxr/imaging/hd/basisCurvesSchema.h>
#include <pxr/imaging/hd/basisCurvesTopologySchema.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
HdCyclesCurves::HdCyclesCurves(const SdfPath &rprimId) : HdCyclesGeometry(rprimId) {}
HdCyclesCurves::~HdCyclesCurves() = default;
HdDirtyBits HdCyclesCurves::GetInitialDirtyBitsMask() const
{
HdDirtyBits bits = HdCyclesGeometry::GetInitialDirtyBitsMask();
bits |= HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyWidths |
HdChangeTracker::DirtyPrimvar | HdChangeTracker::DirtyTopology;
return bits;
}
HdDirtyBits HdCyclesCurves::_PropagateDirtyBits(HdDirtyBits bits) const
{
if (bits & (HdChangeTracker::DirtyTopology)) {
// Changing topology clears the geometry, so need to populate everything again
bits |= HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyWidths |
HdChangeTracker::DirtyPrimvar;
}
return bits;
}
void HdCyclesCurves::Populate(HdSceneDelegate *sceneDelegate, HdDirtyBits dirtyBits, bool &rebuild)
{
if (HdChangeTracker::IsTopologyDirty(dirtyBits, GetId())) {
PopulateTopology(sceneDelegate);
}
if (dirtyBits & HdChangeTracker::DirtyPoints) {
PopulatePoints(sceneDelegate);
}
if (dirtyBits & HdChangeTracker::DirtyWidths) {
PopulateWidths(sceneDelegate);
}
if (dirtyBits & HdChangeTracker::DirtyPrimvar) {
PopulatePrimvars(sceneDelegate);
}
rebuild = (_geom->position_is_modified()) || (_geom->radius_is_modified());
}
void HdCyclesCurves::PopulatePoints(HdSceneDelegate *sceneDelegate)
{
const HdSceneIndexPrim prim = GetPrim(sceneDelegate, GetId());
const HdPrimvarsSchema primvars = HdPrimvarsSchema::GetFromParent(prim.dataSource);
const VtValue value = ReadPrimvar(primvars, HdTokens->points);
if (!value.IsHolding<VtVec3fArray>()) {
TF_WARN("Invalid points data for %s", GetId().GetText());
return;
}
const auto &points = value.UncheckedGet<VtVec3fArray>();
TF_VERIFY(points.size() >= _geom->num_keys());
static_assert(sizeof(GfVec3f) == sizeof(packed_float3));
std::copy_n(reinterpret_cast<const packed_float3 *>(points.data()),
std::min(points.size(), _geom->num_keys()),
_geom->get_position_for_write());
}
void HdCyclesCurves::PopulateWidths(HdSceneDelegate *sceneDelegate)
{
const HdSceneIndexPrim prim = GetPrim(sceneDelegate, GetId());
const HdPrimvarsSchema primvars = HdPrimvarsSchema::GetFromParent(prim.dataSource);
const VtValue value = ReadPrimvar(primvars, HdTokens->widths);
const HdInterpolation interpolation = ReadPrimvarInterpolation(primvars, HdTokens->widths);
if (!value.IsHolding<VtFloatArray>()) {
TF_WARN("Invalid widths data for %s", GetId().GetText());
return;
}
const auto &widths = value.UncheckedGet<VtFloatArray>();
float *radius = _geom->get_radius_for_write();
if (interpolation == HdInterpolationConstant) {
TF_VERIFY(widths.size() == 1);
const float constantRadius = widths[0] * 0.5f;
for (size_t i = 0; i < _geom->num_keys(); ++i) {
radius[i] = constantRadius;
}
}
else if (interpolation == HdInterpolationVertex) {
TF_VERIFY(widths.size() == _geom->num_keys());
for (size_t i = 0; i < _geom->num_keys(); ++i) {
radius[i] = widths[i] * 0.5f;
}
}
}
void HdCyclesCurves::PopulatePrimvars(HdSceneDelegate *sceneDelegate)
{
Scene *const scene = (Scene *)_geom->get_owner();
const HdSceneIndexPrim prim = GetPrim(sceneDelegate, GetId());
const HdPrimvarsSchema primvars = HdPrimvarsSchema::GetFromParent(prim.dataSource);
const std::pair<HdInterpolation, AttributeElement> interpolations[] = {
std::make_pair(HdInterpolationVertex, ATTR_ELEMENT_CURVE_KEY),
std::make_pair(HdInterpolationVarying, ATTR_ELEMENT_CURVE_KEY),
std::make_pair(HdInterpolationUniform, ATTR_ELEMENT_CURVE),
std::make_pair(HdInterpolationConstant, ATTR_ELEMENT_OBJECT),
};
for (const auto &interpolation : interpolations) {
for (const TfToken &primvarName : PrimvarNamesAtInterpolation(primvars, interpolation.first)) {
// Skip special primvars that are handled separately
if (primvarName == HdTokens->points || primvarName == HdTokens->widths) {
continue;
}
const VtValue value = ReadPrimvar(primvars, primvarName);
if (value.IsEmpty()) {
continue;
}
const TfToken role = ReadPrimvarRole(primvars, primvarName);
const ustring name(primvarName.GetString());
AttributeStandard std = ATTR_STD_NONE;
if (role == HdPrimvarRoleTokens->textureCoordinate) {
std = ATTR_STD_UV;
}
else if (primvarName == HdTokens->normals && interpolation.first == HdInterpolationVertex) {
std = ATTR_STD_VERTEX_NORMAL;
}
else if (primvarName == HdTokens->displayColor &&
interpolation.first == HdInterpolationConstant)
{
if (value.IsHolding<VtVec3fArray>() && value.GetArraySize() == 1) {
const GfVec3f color = value.UncheckedGet<VtVec3fArray>()[0];
_instances[0]->set_color(make_float3(color[0], color[1], color[2]));
}
}
// Skip attributes that are not needed
if ((std != ATTR_STD_NONE && _geom->need_attribute(scene, std)) ||
_geom->need_attribute(scene, name))
{
AttributeElement elem = interpolation.second;
if (std == ATTR_STD_VERTEX_NORMAL) {
elem = ATTR_ELEMENT_CURVE_KEY_NORMAL;
}
ApplyPrimvars(_geom->attributes, name, value, elem, std);
}
}
}
}
void HdCyclesCurves::PopulateTopology(HdSceneDelegate *sceneDelegate)
{
// Clear geometry before populating it again with updated topology
_geom->clear(true);
const HdSceneIndexPrim prim = GetPrim(sceneDelegate, GetId());
const HdBasisCurvesTopologySchema topoSchema =
HdBasisCurvesSchema::GetFromParent(prim.dataSource).GetTopology();
TfToken curveType = HdTokens->linear;
if (auto ds = topoSchema.GetType()) {
curveType = ds->GetTypedValue(0.0f);
}
TfToken curveBasis = HdTokens->bezier;
if (auto ds = topoSchema.GetBasis()) {
curveBasis = ds->GetTypedValue(0.0f);
}
TfToken curveWrap = HdTokens->nonperiodic;
if (auto ds = topoSchema.GetWrap()) {
curveWrap = ds->GetTypedValue(0.0f);
}
VtIntArray curveVertexCounts;
if (auto ds = topoSchema.GetCurveVertexCounts()) {
curveVertexCounts = ds->GetTypedValue(0.0f);
}
VtIntArray curveIndices;
if (auto ds = topoSchema.GetCurveIndices()) {
curveIndices = ds->GetTypedValue(0.0f);
}
const HdBasisCurvesTopology topology(
curveType, curveBasis, curveWrap, curveVertexCounts, curveIndices);
_geom->resize_curves(topology.GetNumCurves(), topology.CalculateNeededNumberOfControlPoints());
const VtIntArray vertCounts = topology.GetCurveVertexCounts();
int *curve_first_key = _geom->get_curve_first_key().data();
for (int curve = 0, key = 0; curve < topology.GetNumCurves(); ++curve) {
// Always reference shader at index zero, which is the primitive material
curve_first_key[curve] = key;
key += vertCounts[curve];
}
std::ranges::fill(_geom->get_curve_shader(), 0);
_geom->tag_curve_first_key_modified();
_geom->tag_curve_shader_modified();
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,37 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "hydra/config.h"
#include "hydra/geometry.h"
#include <pxr/imaging/hd/basisCurves.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesCurves final : public HdCyclesGeometry<PXR_NS::HdBasisCurves, CCL_NS::Hair> {
public:
HdCyclesCurves(const PXR_NS::SdfPath &rprimId);
~HdCyclesCurves() override;
PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override;
private:
PXR_NS::HdDirtyBits _PropagateDirtyBits(PXR_NS::HdDirtyBits bits) const override;
void Populate(PXR_NS::HdSceneDelegate *sceneDelegate,
PXR_NS::HdDirtyBits dirtyBits,
bool &rebuild) override;
void PopulatePoints(PXR_NS::HdSceneDelegate *sceneDelegate);
void PopulateWidths(PXR_NS::HdSceneDelegate *sceneDelegate);
void PopulatePrimvars(PXR_NS::HdSceneDelegate *sceneDelegate);
void PopulateTopology(PXR_NS::HdSceneDelegate *sceneDelegate);
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,292 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#ifdef _WIN32
// Include first to avoid "NOGDI" definition set in Cycles headers
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <Windows.h>
#endif
#include "hydra/display_driver.h"
#include "hydra/render_buffer.h"
#include "hydra/session.h"
#include <epoxy/gl.h>
#include <pxr/imaging/hgiGL/texture.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
HdCyclesDisplayDriver::HdCyclesDisplayDriver(HdCyclesSession *renderParam, Hgi *hgi)
: _renderParam(renderParam), _hgi(hgi)
{
}
HdCyclesDisplayDriver::~HdCyclesDisplayDriver()
{
if (texture_) {
_hgi->DestroyTexture(&texture_);
}
if (gl_pbo_id_) {
glDeleteBuffers(1, &gl_pbo_id_);
}
gl_context_dispose();
}
void HdCyclesDisplayDriver::gl_context_create()
{
#ifdef _WIN32
if (!gl_context_) {
hdc_ = GetDC(CreateWindowA("STATIC",
"HdCycles",
WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
0,
0,
64,
64,
nullptr,
nullptr,
GetModuleHandle(nullptr),
nullptr));
int pixelFormat = GetPixelFormat(wglGetCurrentDC());
PIXELFORMATDESCRIPTOR pfd = {sizeof(pfd)};
DescribePixelFormat((HDC)hdc_, pixelFormat, sizeof(pfd), &pfd);
SetPixelFormat((HDC)hdc_, pixelFormat, &pfd);
TF_VERIFY(gl_context_ = wglCreateContext((HDC)hdc_));
TF_VERIFY(wglShareLists(wglGetCurrentContext(), (HGLRC)gl_context_));
}
if (!gl_context_) {
return;
}
#endif
if (!gl_pbo_id_) {
glGenBuffers(1, &gl_pbo_id_);
graphics_interop_buffer_.clear();
}
}
bool HdCyclesDisplayDriver::gl_context_enable()
{
#ifdef _WIN32
if (!hdc_ || !gl_context_) {
return false;
}
mutex_.lock();
// Do not change context if this is called in the main thread
if (wglGetCurrentContext() == nullptr) {
if (!TF_VERIFY(wglMakeCurrent((HDC)hdc_, (HGLRC)gl_context_))) {
mutex_.unlock();
return false;
}
}
return true;
#else
return false;
#endif
}
void HdCyclesDisplayDriver::gl_context_disable()
{
#ifdef _WIN32
if (wglGetCurrentContext() == gl_context_) {
TF_VERIFY(wglMakeCurrent(nullptr, nullptr));
}
mutex_.unlock();
#endif
}
void HdCyclesDisplayDriver::gl_context_dispose()
{
#ifdef _WIN32
if (gl_context_) {
TF_VERIFY(wglDeleteContext((HGLRC)gl_context_));
DestroyWindow(WindowFromDC((HDC)hdc_));
}
#endif
}
void HdCyclesDisplayDriver::next_tile_begin() {}
bool HdCyclesDisplayDriver::update_begin(const Params &params,
int /*texture_width*/,
int /*texture_height*/)
{
if (!gl_context_enable()) {
return false;
}
if (gl_render_sync_) {
glWaitSync((GLsync)gl_render_sync_, 0, GL_TIMEOUT_IGNORED);
}
if (pbo_size_.x != params.full_size.x || pbo_size_.y != params.full_size.y) {
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_pbo_id_);
glBufferData(GL_PIXEL_UNPACK_BUFFER,
sizeof(half4) * params.full_size.x * params.full_size.y,
nullptr,
GL_DYNAMIC_DRAW);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
pbo_size_ = params.full_size;
graphics_interop_buffer_.clear();
}
need_update_ = true;
return true;
}
void HdCyclesDisplayDriver::update_end()
{
gl_upload_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
glFlush();
gl_context_disable();
}
void HdCyclesDisplayDriver::flush()
{
gl_context_enable();
if (gl_upload_sync_) {
glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED);
}
if (gl_render_sync_) {
glWaitSync((GLsync)gl_render_sync_, 0, GL_TIMEOUT_IGNORED);
}
gl_context_disable();
}
half4 *HdCyclesDisplayDriver::map_texture_buffer()
{
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_pbo_id_);
auto *const mapped_rgba_pixels = static_cast<half4 *>(
glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY));
if (need_zero_ && mapped_rgba_pixels) {
memset(mapped_rgba_pixels, 0, sizeof(half4) * pbo_size_.x * pbo_size_.y);
need_zero_ = false;
}
return mapped_rgba_pixels;
}
void HdCyclesDisplayDriver::unmap_texture_buffer()
{
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
GraphicsInteropDevice HdCyclesDisplayDriver::graphics_interop_get_device()
{
GraphicsInteropDevice interop_device;
interop_device.type = GraphicsInteropDevice::OPENGL;
return interop_device;
}
void HdCyclesDisplayDriver::graphics_interop_update_buffer()
{
if (graphics_interop_buffer_.is_empty()) {
graphics_interop_buffer_.assign(
GraphicsInteropDevice::OPENGL, gl_pbo_id_, pbo_size_.x * pbo_size_.y * sizeof(half4));
}
if (need_zero_) {
graphics_interop_buffer_.zero();
need_zero_ = false;
}
}
void HdCyclesDisplayDriver::graphics_interop_activate()
{
gl_context_enable();
}
void HdCyclesDisplayDriver::graphics_interop_deactivate()
{
gl_context_disable();
}
void HdCyclesDisplayDriver::zero()
{
need_zero_ = true;
}
void HdCyclesDisplayDriver::draw(const Params &params)
{
auto *const renderBuffer = static_cast<HdCyclesRenderBuffer *>(
_renderParam->GetDisplayAovBinding().renderBuffer);
if (!renderBuffer || // Ensure this render buffer matches the texture dimensions
(renderBuffer->GetWidth() != params.size.x || renderBuffer->GetHeight() != params.size.y))
{
return;
}
if (!renderBuffer->IsResourceUsed()) {
return;
}
gl_context_create();
// Cycles 'DisplayDriver' only supports 'half4' format
TF_VERIFY(renderBuffer->GetFormat() == HdFormatFloat16Vec4);
const thread_scoped_lock lock(mutex_);
const GfVec3i dimensions(params.size.x, params.size.y, 1);
if (!texture_ || texture_->GetDescriptor().dimensions != dimensions) {
if (texture_) {
_hgi->DestroyTexture(&texture_);
}
HgiTextureDesc texDesc;
texDesc.usage = 0;
texDesc.format = HgiFormatFloat16Vec4;
texDesc.type = HgiTextureType2D;
texDesc.dimensions = dimensions;
texDesc.sampleCount = HgiSampleCount1;
texture_ = _hgi->CreateTexture(texDesc);
renderBuffer->SetResource(VtValue(texture_));
}
HgiGLTexture *const texture = dynamic_cast<HgiGLTexture *>(texture_.Get());
if (!texture || !need_update_ || pbo_size_.x != params.size.x || pbo_size_.y != params.size.y) {
return;
}
if (gl_upload_sync_) {
glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED);
}
glBindTexture(GL_TEXTURE_2D, texture->GetTextureId());
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_pbo_id_);
glTexSubImage2D(
GL_TEXTURE_2D, 0, 0, 0, pbo_size_.x, pbo_size_.y, GL_RGBA, GL_HALF_FLOAT, nullptr);
glBindTexture(GL_TEXTURE_2D, 0);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
gl_render_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
glFlush();
need_update_ = false;
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,70 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "hydra/config.h"
#include "session/display_driver.h"
#include "util/thread.h"
#include <pxr/imaging/hgi/hgi.h>
#include <pxr/imaging/hgi/texture.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesDisplayDriver final : public CCL_NS::DisplayDriver {
public:
HdCyclesDisplayDriver(HdCyclesSession *renderParam, Hgi *hgi);
~HdCyclesDisplayDriver() override;
private:
void next_tile_begin() override;
bool update_begin(const Params &params,
const int texture_width,
const int texture_height) override;
void update_end() override;
void flush() override;
CCL_NS::half4 *map_texture_buffer() override;
void unmap_texture_buffer() override;
GraphicsInteropDevice graphics_interop_get_device() override;
void graphics_interop_update_buffer() override;
void graphics_interop_activate() override;
void graphics_interop_deactivate() override;
void zero() override;
void draw(const Params &params) override;
void gl_context_create();
bool gl_context_enable();
void gl_context_disable();
void gl_context_dispose();
HdCyclesSession *const _renderParam;
Hgi *const _hgi;
#ifdef _WIN32
void *hdc_ = nullptr;
void *gl_context_ = nullptr;
#endif
CCL_NS::thread_mutex mutex_;
PXR_NS::HgiTextureHandle texture_;
unsigned int gl_pbo_id_ = 0;
CCL_NS::int2 pbo_size_ = CCL_NS::make_int2(0, 0);
bool need_update_ = false;
std::atomic_bool need_zero_ = false;
std::atomic_bool need_recreate_interop_ = false;
void *gl_render_sync_ = nullptr;
void *gl_upload_sync_ = nullptr;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,109 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "hydra/field.h"
#include "hydra/util.h"
#include "util/log.h"
#ifdef WITH_OPENVDB
# include "hydra/session.h"
# include "scene/image_vdb.h"
# include "scene/scene.h"
#endif
#include <pxr/imaging/hd/sceneDelegate.h>
#include <pxr/imaging/hd/volumeFieldSchema.h>
#include <pxr/usd/sdf/assetPath.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
#ifdef WITH_OPENVDB
class HdCyclesVolumeLoader : public VDBImageLoader {
public:
HdCyclesVolumeLoader(const std::string &filePath, const std::string &gridName)
: VDBImageLoader(gridName)
{
/* Disable delay loading and file copying, this has poor performance on network drives. */
const bool delay_load = false;
try {
openvdb::io::File file(filePath);
# ifdef OPENVDB_USE_DELAYED_LOADING
file.setCopyMaxBytes(0);
# endif
if (file.open(delay_load)) {
grid = file.readGrid(gridName);
}
}
catch (const openvdb::IoError &e) {
LOG_ERROR << "Error loading OpenVDB file: " << e.what();
}
catch (...) {
LOG_ERROR << "Error loading OpenVDB file: Unknown error";
}
}
};
#endif
HdCyclesField::HdCyclesField(const SdfPath &bprimId, const TfToken & /*typeId*/) : HdField(bprimId)
{
}
HdCyclesField::~HdCyclesField() = default;
HdDirtyBits HdCyclesField::GetInitialDirtyBitsMask() const
{
return DirtyBits::DirtyParams;
}
void HdCyclesField::Sync(HdSceneDelegate *sceneDelegate,
HdRenderParam *renderParam,
HdDirtyBits *dirtyBits)
{
#ifdef WITH_OPENVDB
const SdfPath &id = GetId();
if (*dirtyBits & DirtyBits::DirtyParams) {
const HdSceneIndexPrim prim = GetPrim(sceneDelegate, id);
HdVolumeFieldSchema schema = HdVolumeFieldSchema::GetFromParent(prim.dataSource);
SdfAssetPath assetPath;
if (auto ds = schema.GetFilePath()) {
assetPath = ds->GetTypedValue(0.0f);
}
std::string filename = assetPath.GetResolvedPath();
if (filename.empty()) {
filename = assetPath.GetAssetPath();
}
if (!filename.empty()) {
TfToken fieldName;
if (auto ds = schema.GetFieldName()) {
fieldName = ds->GetTypedValue(0.0f);
}
if (!fieldName.IsEmpty()) {
unique_ptr<ImageLoader> loader = make_unique<HdCyclesVolumeLoader>(filename,
fieldName.GetString());
const SceneLock lock(renderParam);
ImageParams params;
params.frame = 0.0f;
_handle = lock.scene->image_manager->add_image(std::move(loader), params, false);
}
}
}
#else
(void)sceneDelegate;
(void)renderParam;
#endif
*dirtyBits = DirtyBits::Clean;
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,35 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "hydra/config.h"
#include "scene/image.h"
#include <pxr/imaging/hd/field.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesField final : public PXR_NS::HdField {
public:
HdCyclesField(const PXR_NS::SdfPath &bprimId, const PXR_NS::TfToken &typeId);
~HdCyclesField() override;
PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override;
void Sync(PXR_NS::HdSceneDelegate *sceneDelegate,
PXR_NS::HdRenderParam *renderParam,
PXR_NS::HdDirtyBits *dirtyBits) override;
CCL_NS::ImageHandle GetImageHandle() const
{
return _handle;
}
private:
CCL_NS::ImageHandle _handle;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,160 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "hydra/file_reader.h"
#include "hydra/camera.h"
#include "hydra/render_delegate.h"
#include "util/log.h"
#include "util/path.h"
#include "util/unique_ptr.h"
#include "scene/scene.h"
#include <pxr/base/plug/registry.h>
#include <pxr/imaging/hd/flatteningSceneIndex.h>
#include <pxr/imaging/hd/legacyTaskFactory.h>
#include <pxr/imaging/hd/legacyTaskSchema.h>
#include <pxr/imaging/hd/mergingSceneIndex.h>
#include <pxr/imaging/hd/renderDelegate.h>
#include <pxr/imaging/hd/renderIndex.h>
#include <pxr/imaging/hd/retainedDataSource.h>
#include <pxr/imaging/hd/retainedSceneIndex.h>
#include <pxr/imaging/hd/rprimCollection.h>
#include <pxr/imaging/hd/task.h>
#include <pxr/imaging/hd/tokens.h>
#include <pxr/imaging/hdsi/extComputationPrimvarPruningSceneIndex.h>
#include <pxr/imaging/hdsi/legacyDisplayStyleOverrideSceneIndex.h>
#include <pxr/usd/usd/primRange.h>
#include <pxr/usd/usd/stage.h>
#include <pxr/usd/usdGeom/camera.h>
#include <pxr/usd/usdGeom/metrics.h>
#include <pxr/usdImaging/usdImaging/flattenedDataSourceProviders.h>
#include <pxr/usdImaging/usdImaging/materialBindingsResolvingSceneIndex.h>
#include <pxr/usdImaging/usdImaging/stageSceneIndex.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
/* Dummy task whose only purpose is to provide render tag tokens to the render index. */
class DummyHdTask : public HdTask {
public:
DummyHdTask(HdSceneDelegate * /*delegate*/, const SdfPath &id)
: HdTask(id), tags({HdRenderTagTokens->geometry, HdRenderTagTokens->render})
{
}
protected:
void Sync(HdSceneDelegate * /*delegate*/,
HdTaskContext * /*ctx*/,
HdDirtyBits * /*dirtyBits*/) override
{
}
void Prepare(HdTaskContext * /*ctx*/, HdRenderIndex * /*render_index*/) override {}
void Execute(HdTaskContext * /*ctx*/) override {}
const TfTokenVector &GetRenderTags() const override
{
return tags;
}
TfTokenVector tags;
};
void HdCyclesFileReader::read(Session *session, const char *filepath, const bool use_camera)
{
/* Initialize USD. */
PlugRegistry::GetInstance().RegisterPlugins(path_get("usd"));
/* Open Stage. */
const UsdStageRefPtr stage = UsdStage::Open(filepath);
if (!stage) {
LOG_ERROR << "USD failed to read " << filepath;
return;
}
/* Init paths. */
const SdfPath root_path = SdfPath::AbsoluteRootPath();
const SdfPath task_path("/_hdCycles/DummyHdTask");
/* Create render delegate. */
HdRenderSettingsMap settings_map;
settings_map.insert(std::make_pair(HdCyclesRenderSettingsTokens->stageMetersPerUnit,
VtValue(UsdGeomGetStageMetersPerUnit(stage))));
HdCyclesDelegate render_delegate(settings_map, session, true);
/* Create render index. */
unique_ptr<HdRenderIndex> render_index(HdRenderIndex::New(&render_delegate, {}));
/* Set up scene index from USD stage for easy consumption.
* Do ext computation (e.g. skinning), flattening and resolve material bindings. */
UsdImagingStageSceneIndexRefPtr stage_si = UsdImagingStageSceneIndex::New(nullptr);
stage_si->SetStage(stage);
stage_si->SetTime(UsdTimeCode::Default());
HdSceneIndexBaseRefPtr filtered = stage_si;
filtered = HdSiExtComputationPrimvarPruningSceneIndex::New(filtered);
filtered = HdFlatteningSceneIndex::New(filtered, UsdImagingFlattenedDataSourceProviders());
filtered = UsdImagingMaterialBindingsResolvingSceneIndex::New(filtered, nullptr);
/* Provide DummyHdTask as a task prim to the scene index. */
HdRprimCollection collection(HdTokens->geometry, HdReprSelector(HdReprTokens->smoothHull));
collection.SetRootPath(root_path);
const TfTokenVector render_tags = {HdRenderTagTokens->geometry, HdRenderTagTokens->render};
HdContainerDataSourceHandle task_ds =
HdLegacyTaskSchema::Builder()
.SetFactory(HdRetainedTypedSampledDataSource<HdLegacyTaskFactorySharedPtr>::New(
HdMakeLegacyTaskFactory<DummyHdTask>()))
.SetCollection(HdRetainedTypedSampledDataSource<HdRprimCollection>::New(collection))
.SetRenderTags(HdRetainedTypedSampledDataSource<TfTokenVector>::New(render_tags))
.Build();
HdRetainedSceneIndexRefPtr task_si = HdRetainedSceneIndex::New();
task_si->AddPrims(
{{task_path,
HdPrimTypeTokens->task,
HdRetainedContainerDataSource::New(HdLegacyTaskSchema::GetSchemaToken(), task_ds)}});
HdMergingSceneIndexRefPtr merging_si = HdMergingSceneIndex::New();
merging_si->AddInputScene(filtered, root_path);
merging_si->AddInputScene(task_si, root_path);
const bool needs_prefixing = false;
render_index->InsertSceneIndex(merging_si, root_path, needs_prefixing);
stage_si->ApplyPendingUpdates();
render_index->EnqueueCollectionToSync(collection);
/* Sync prims. */
HdTaskContext task_context;
HdTaskSharedPtrVector tasks;
if (HdTaskSharedPtr const &task = render_index->GetTask(task_path)) {
tasks.push_back(task);
}
render_index->SyncAll(&tasks, &task_context);
render_delegate.CommitResources(&render_index->GetChangeTracker());
/* Use first camera in stage.
* TODO: get camera from UsdRender if available. */
if (use_camera) {
for (const UsdPrim &prim : stage->Traverse()) {
if (prim.IsA<UsdGeomCamera>()) {
HdSprim *sprim = render_index->GetSprim(HdPrimTypeTokens->camera, prim.GetPath());
if (sprim) {
HdCyclesCamera *camera = dynamic_cast<HdCyclesCamera *>(sprim);
camera->ApplyCameraSettings(render_delegate.GetRenderParam(), session->scene->camera);
break;
}
}
}
}
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,18 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "hydra/config.h"
#include "session/session.h"
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesFileReader {
public:
static void read(Session *session, const char *filepath, const bool use_camera = true);
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,9 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
/* Dummy file to make clangd happy. */
#include "hydra/geometry.h"
#include "hydra/geometry.inl"

View File

@@ -0,0 +1,47 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "hydra/config.h"
#include <pxr/imaging/hd/rprim.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
template<typename Base, typename CyclesBase> class HdCyclesGeometry : public Base {
public:
HdCyclesGeometry(const PXR_NS::SdfPath &rprimId);
void Sync(PXR_NS::HdSceneDelegate *sceneDelegate,
PXR_NS::HdRenderParam *renderParam,
PXR_NS::HdDirtyBits *dirtyBits,
const PXR_NS::TfToken &reprToken) override;
PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override;
void Finalize(PXR_NS::HdRenderParam *renderParam) override;
protected:
void _InitRepr(const PXR_NS::TfToken &reprToken, PXR_NS::HdDirtyBits *dirtyBits) override;
PXR_NS::HdDirtyBits _PropagateDirtyBits(PXR_NS::HdDirtyBits bits) const override;
virtual void Populate(PXR_NS::HdSceneDelegate *sceneDelegate,
PXR_NS::HdDirtyBits dirtyBits,
bool &rebuild) = 0;
CyclesBase *_geom = nullptr;
std::vector<CCL_NS::Object *> _instances;
private:
void Initialize(PXR_NS::HdRenderParam *renderParam);
void InitializeInstance(const int index);
PXR_NS::GfMatrix4d _geomTransform;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,239 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "hydra/geometry.h"
#include "hydra/instancer.h"
#include "hydra/material.h"
#include "hydra/session.h"
#include "hydra/util.h"
#include "scene/geometry.h"
#include "scene/object.h"
#include "scene/scene.h"
#include "util/hash.h"
#include <pxr/imaging/hd/materialBindingSchema.h>
#include <pxr/imaging/hd/materialBindingsSchema.h>
#include <pxr/imaging/hd/sceneDelegate.h>
#include <pxr/imaging/hd/tokens.h>
#include <pxr/imaging/hd/xformSchema.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
extern Transform convert_transform(const GfMatrix4d &matrix);
template<typename Base, typename CyclesBase>
HdCyclesGeometry<Base, CyclesBase>::HdCyclesGeometry(const SdfPath &rprimId)
: Base(rprimId), _geomTransform(1.0)
{
}
template<typename Base, typename CyclesBase>
void HdCyclesGeometry<Base, CyclesBase>::_InitRepr(const TfToken &reprToken,
HdDirtyBits *dirtyBits)
{
TF_UNUSED(reprToken);
TF_UNUSED(dirtyBits);
}
template<typename Base, typename CyclesBase>
HdDirtyBits HdCyclesGeometry<Base, CyclesBase>::GetInitialDirtyBitsMask() const
{
return HdChangeTracker::DirtyPrimID | HdChangeTracker::DirtyTransform |
HdChangeTracker::DirtyMaterialId | HdChangeTracker::DirtyVisibility |
HdChangeTracker::DirtyInstancer;
}
template<typename Base, typename CyclesBase>
HdDirtyBits HdCyclesGeometry<Base, CyclesBase>::_PropagateDirtyBits(HdDirtyBits bits) const
{
return bits;
}
template<typename Base, typename CyclesBase>
void HdCyclesGeometry<Base, CyclesBase>::Sync(HdSceneDelegate *sceneDelegate,
HdRenderParam *renderParam,
HdDirtyBits *dirtyBits,
const TfToken &reprToken)
{
TF_UNUSED(reprToken);
if (*dirtyBits == HdChangeTracker::Clean) {
return;
}
Initialize(renderParam);
Base::_UpdateInstancer(sceneDelegate, dirtyBits);
HdInstancer::_SyncInstancerAndParents(sceneDelegate->GetRenderIndex(), Base::GetInstancerId());
Base::_UpdateVisibility(sceneDelegate, dirtyBits);
const SceneLock lock(renderParam);
const SdfPath &id = Base::GetId();
const HdSceneIndexPrim prim = GetPrim(sceneDelegate, id);
const HdContainerDataSourceHandle &primDs = prim.dataSource;
if (*dirtyBits & HdChangeTracker::DirtyMaterialId) {
SdfPath materialId;
/* Purpose here matches #HdCyclesDelegate::GetMaterialBindingPurpose. */
if (auto pathDs =
HdMaterialBindingsSchema::GetFromParent(primDs).GetMaterialBinding().GetPath())
{
materialId = pathDs->GetTypedValue(0.0f);
}
Base::SetMaterialId(materialId);
const auto *const material = static_cast<const HdCyclesMaterial *>(
sceneDelegate->GetRenderIndex().GetSprim(HdPrimTypeTokens->material,
Base::GetMaterialId()));
array<Node *> usedShaders(1);
if (material && material->GetCyclesShader()) {
usedShaders[0] = material->GetCyclesShader();
}
else {
usedShaders[0] = lock.scene->default_surface;
}
for (Node *shader : usedShaders) {
static_cast<Shader *>(shader)->tag_used(lock.scene);
}
_geom->set_used_shaders(usedShaders);
}
if (HdChangeTracker::IsPrimIdDirty(*dirtyBits, id)) {
// This needs to be corrected in the AOV
_instances[0]->set_pass_id(Base::GetPrimId() + 1);
}
if (HdChangeTracker::IsTransformDirty(*dirtyBits, id)) {
_geomTransform = GfMatrix4d(1.0);
if (auto matrixDs = HdXformSchema::GetFromParent(primDs).GetMatrix()) {
_geomTransform = matrixDs->GetTypedValue(0.0f);
}
}
if (HdChangeTracker::IsTransformDirty(*dirtyBits, id) ||
HdChangeTracker::IsInstancerDirty(*dirtyBits, id))
{
auto *const instancer = static_cast<HdCyclesInstancer *>(
sceneDelegate->GetRenderIndex().GetInstancer(Base::GetInstancerId()));
// Make sure the first object attribute is the instanceId
assert(_instances[0]->attributes.size() >= 1 &&
_instances[0]->attributes.front().name() == HdAovTokens->instanceId.GetString());
VtMatrix4dArray transforms;
if (instancer) {
transforms = instancer->ComputeInstanceTransforms(id);
_instances[0]->attributes.front() = ParamValue(HdAovTokens->instanceId.GetString(), +0.0f);
}
else {
// Default to a single instance with an identity transform
transforms.push_back(GfMatrix4d(1.0));
_instances[0]->attributes.front() = ParamValue(HdAovTokens->instanceId.GetString(), -1.0f);
}
const size_t oldSize = _instances.size();
const size_t newSize = transforms.size();
// Resize instance list
for (size_t i = newSize; i < oldSize; ++i) {
lock.scene->delete_node(_instances[i]);
}
_instances.resize(newSize);
for (size_t i = oldSize; i < newSize; ++i) {
_instances[i] = lock.scene->create_node<Object>();
InitializeInstance(static_cast<int>(i));
}
// Update transforms of all instances
for (size_t i = 0; i < transforms.size(); ++i) {
const float metersPerUnit =
static_cast<HdCyclesSession *>(renderParam)->GetStageMetersPerUnit();
const Transform tfm = transform_scale(make_float3(metersPerUnit)) *
convert_transform(_geomTransform * transforms[i]);
_instances[i]->set_tfm(tfm);
}
}
if (HdChangeTracker::IsVisibilityDirty(*dirtyBits, id)) {
for (Object *instance : _instances) {
instance->set_visibility(Base::IsVisible() ? ~0 : 0);
}
}
// Must happen after material ID update, so that attribute decisions can be made
// based on it (e.g. check whether an attribute is actually needed)
bool rebuild = false;
Populate(sceneDelegate, *dirtyBits, rebuild);
if (_geom->is_modified() || rebuild) {
_geom->tag_update(lock.scene, rebuild);
}
for (Object *instance : _instances) {
instance->tag_update(lock.scene);
}
*dirtyBits = HdChangeTracker::Clean;
}
template<typename Base, typename CyclesBase>
void HdCyclesGeometry<Base, CyclesBase>::Finalize(HdRenderParam *renderParam)
{
if (!_geom && _instances.empty()) {
return;
}
const SceneLock lock(renderParam);
const bool keep_nodes = static_cast<const HdCyclesSession *>(renderParam)->keep_nodes;
if (!keep_nodes) {
lock.scene->delete_node(_geom);
}
_geom = nullptr;
if (!keep_nodes) {
lock.scene->delete_nodes(set<Object *>(_instances.begin(), _instances.end()));
}
_instances.clear();
_instances.shrink_to_fit();
}
template<typename Base, typename CyclesBase>
void HdCyclesGeometry<Base, CyclesBase>::Initialize(HdRenderParam *renderParam)
{
if (_geom) {
return;
}
const SceneLock lock(renderParam);
// Create geometry
_geom = lock.scene->create_node<CyclesBase>();
_geom->name = Base::GetId().GetString();
// Create default instance
_instances.push_back(lock.scene->create_node<Object>());
InitializeInstance(0);
}
template<typename Base, typename CyclesBase>
void HdCyclesGeometry<Base, CyclesBase>::InitializeInstance(int index)
{
Object *instance = _instances[index];
instance->set_geometry(_geom);
instance->attributes.emplace_back(HdAovTokens->instanceId.GetString(),
_instances.size() == 1 ? -1.0f : static_cast<float>(index));
instance->set_color(make_float3(0.8f, 0.8f, 0.8f));
instance->set_random_id(hash_uint2(hash_string(_geom->name.c_str()), index));
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,172 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "hydra/instancer.h"
#include "hydra/util.h"
#include <pxr/base/gf/quatd.h>
#include <pxr/base/gf/quatf.h>
#include <pxr/base/gf/quath.h>
#include <pxr/imaging/hd/instancedBySchema.h>
#include <pxr/imaging/hd/instancerTopologySchema.h>
#include <pxr/imaging/hd/primvarSchema.h>
#include <pxr/imaging/hd/primvarsSchema.h>
#include <pxr/imaging/hd/sceneDelegate.h>
#include <pxr/imaging/hd/xformSchema.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
HdCyclesInstancer::HdCyclesInstancer(HdSceneDelegate *delegate, const SdfPath &instancerId)
: HdInstancer(delegate, instancerId)
{
}
HdCyclesInstancer::~HdCyclesInstancer() = default;
void HdCyclesInstancer::Sync(HdSceneDelegate *sceneDelegate,
HdRenderParam * /*renderParam*/,
HdDirtyBits *dirtyBits)
{
_UpdateInstancer(sceneDelegate, dirtyBits);
if (HdChangeTracker::IsAnyPrimvarDirty(*dirtyBits, GetId())) {
SyncPrimvars();
}
}
void HdCyclesInstancer::SyncPrimvars()
{
HdSceneDelegate *const sceneDelegate = GetDelegate();
const SdfPath &id = GetId();
const HdSceneIndexPrim prim = GetPrim(sceneDelegate, id);
HdPrimvarsSchema primvars = HdPrimvarsSchema::GetFromParent(prim.dataSource);
auto read_array_primvar = [&](const TfToken &name, auto &out) {
using T = std::remove_reference_t<decltype(out)>;
if (HdPrimvarSchema pv = primvars.GetPrimvar(name)) {
if (auto valueDs = pv.GetPrimvarValue()) {
const VtValue v = valueDs->GetValue(0.0f);
if (v.IsHolding<T>()) {
out = v.UncheckedGet<T>();
}
}
}
};
read_array_primvar(HdInstancerTokens->instanceTranslations, _translate);
read_array_primvar(HdInstancerTokens->instanceScales, _scale);
read_array_primvar(HdInstancerTokens->instanceTransforms, _instanceTransform);
/* Accept different array types for quaternions. */
if (HdPrimvarSchema pv = primvars.GetPrimvar(HdInstancerTokens->instanceRotations)) {
if (auto valueDs = pv.GetPrimvarValue()) {
const VtValue v = valueDs->GetValue(0.0f);
if (v.IsHolding<VtVec4fArray>()) {
_rotate = v.UncheckedGet<VtVec4fArray>();
}
else if (v.IsHolding<VtQuatfArray>()) {
const VtQuatfArray &src = v.UncheckedGet<VtQuatfArray>();
_rotate.clear();
_rotate.reserve(src.size());
for (const GfQuatf &q : src) {
const GfVec3f &im = q.GetImaginary();
_rotate.push_back(GfVec4f(q.GetReal(), im[0], im[1], im[2]));
}
}
else if (v.IsHolding<VtQuathArray>()) {
const VtQuathArray &src = v.UncheckedGet<VtQuathArray>();
_rotate.clear();
_rotate.reserve(src.size());
for (const GfQuath &q : src) {
const GfVec3h &im = q.GetImaginary();
_rotate.push_back(GfVec4f(q.GetReal(), im[0], im[1], im[2]));
}
}
}
}
sceneDelegate->GetRenderIndex().GetChangeTracker().MarkInstancerClean(id);
}
VtMatrix4dArray HdCyclesInstancer::ComputeInstanceTransforms(const SdfPath &prototypeId)
{
HdSceneDelegate *const sceneDelegate = GetDelegate();
const SdfPath &id = GetId();
const HdSceneIndexPrim prim = GetPrim(sceneDelegate, id);
HdInstancerTopologySchema topology = HdInstancerTopologySchema::GetFromParent(prim.dataSource);
const VtIntArray instanceIndices = topology ?
topology.ComputeInstanceIndicesForProto(prototypeId) :
VtIntArray();
GfMatrix4d instanceTransform(1.0);
if (auto matrixDs = HdXformSchema::GetFromParent(prim.dataSource).GetMatrix()) {
instanceTransform = matrixDs->GetTypedValue(0.0f);
}
VtMatrix4dArray transforms;
transforms.reserve(instanceIndices.size());
for (const int index : instanceIndices) {
GfMatrix4d transform = instanceTransform;
if (index < _translate.size()) {
GfMatrix4d translateMat(1);
translateMat.SetTranslate(_translate[index]);
transform *= translateMat;
}
if (index < _rotate.size()) {
GfMatrix4d rotateMat(1);
const GfVec4f &quat = _rotate[index];
rotateMat.SetRotate(GfQuatd(quat[0], quat[1], quat[2], quat[3]));
transform *= rotateMat;
}
if (index < _scale.size()) {
GfMatrix4d scaleMat(1);
scaleMat.SetScale(_scale[index]);
transform *= scaleMat;
}
if (index < _instanceTransform.size()) {
transform *= _instanceTransform[index];
}
transforms.push_back(transform);
}
/* Recurse into the parent instancer. */
VtMatrix4dArray resultTransforms;
HdInstancedBySchema instancedBy = HdInstancedBySchema::GetFromParent(prim.dataSource);
SdfPath parentId;
if (auto pathsDs = instancedBy.GetPaths()) {
const VtArray<SdfPath> parentPaths = pathsDs->GetTypedValue(0.0f);
if (!parentPaths.empty()) {
parentId = parentPaths.front();
}
}
if (parentId.IsEmpty()) {
parentId = GetParentId();
}
if (!parentId.IsEmpty()) {
auto *const parentInstancer = static_cast<HdCyclesInstancer *>(
sceneDelegate->GetRenderIndex().GetInstancer(parentId));
if (parentInstancer) {
for (const GfMatrix4d &parentTransform : parentInstancer->ComputeInstanceTransforms(id)) {
for (const GfMatrix4d &localTransform : transforms) {
resultTransforms.push_back(parentTransform * localTransform);
}
}
return resultTransforms;
}
}
return transforms;
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,38 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "hydra/config.h"
#include <pxr/base/gf/matrix4d.h>
#include <pxr/base/gf/vec3f.h>
#include <pxr/base/gf/vec4f.h>
#include <pxr/base/vt/array.h>
#include <pxr/imaging/hd/instancer.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesInstancer final : public PXR_NS::HdInstancer {
public:
HdCyclesInstancer(PXR_NS::HdSceneDelegate *delegate, const PXR_NS::SdfPath &instancerId);
~HdCyclesInstancer() override;
void Sync(PXR_NS::HdSceneDelegate *sceneDelegate,
PXR_NS::HdRenderParam *renderParam,
PXR_NS::HdDirtyBits *dirtyBits) override;
PXR_NS::VtMatrix4dArray ComputeInstanceTransforms(const PXR_NS::SdfPath &prototypeId);
private:
void SyncPrimvars();
PXR_NS::VtVec3fArray _translate;
PXR_NS::VtVec4fArray _rotate;
PXR_NS::VtVec3fArray _scale;
PXR_NS::VtMatrix4dArray _instanceTransform;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,409 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "hydra/light.h"
#include "hydra/session.h"
#include "hydra/util.h"
#include "kernel/types.h"
#include "scene/light.h"
#include "scene/object.h"
#include "scene/scene.h"
#include "scene/shader.h"
#include "scene/shader_graph.h"
#include "scene/shader_nodes.h"
#include "util/hash.h"
#include "util/transform.h"
#include <pxr/imaging/hd/lightSchema.h>
#include <pxr/imaging/hd/sceneDelegate.h>
#include <pxr/imaging/hd/visibilitySchema.h>
#include <pxr/imaging/hd/xformSchema.h>
#include <pxr/usd/sdf/assetPath.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
extern Transform convert_transform(const GfMatrix4d &matrix);
// clang-format off
TF_DEFINE_PRIVATE_TOKENS(_tokens,
(visibleInPrimaryRay)
(treatAsPoint)
(falloff)
);
// clang-format on
HdCyclesLight::HdCyclesLight(const SdfPath &sprimId, const TfToken &lightType)
: HdLight(sprimId), _lightType(lightType)
{
}
HdCyclesLight::~HdCyclesLight() = default;
HdDirtyBits HdCyclesLight::GetInitialDirtyBitsMask() const
{
return DirtyBits::DirtyTransform | DirtyBits::DirtyParams;
}
void HdCyclesLight::Sync(HdSceneDelegate *sceneDelegate,
HdRenderParam *renderParam,
HdDirtyBits *dirtyBits)
{
if (*dirtyBits == DirtyBits::Clean) {
return;
}
Initialize(renderParam);
const SceneLock lock(renderParam);
const SdfPath &id = GetId();
const HdSceneIndexPrim prim = GetPrim(sceneDelegate, id);
const HdContainerDataSourceHandle &primDs = prim.dataSource;
const HdContainerDataSourceHandle lightDs = HdLightSchema::GetFromParent(primDs).GetContainer();
if (*dirtyBits & DirtyBits::DirtyTransform) {
const float metersPerUnit =
static_cast<HdCyclesSession *>(renderParam)->GetStageMetersPerUnit();
GfMatrix4d xform(1.0);
if (auto matrixDs = HdXformSchema::GetFromParent(primDs).GetMatrix()) {
xform = matrixDs->GetTypedValue(0.0f);
}
const Transform tfm = transform_scale(make_float3(metersPerUnit)) * convert_transform(xform);
_object->set_tfm(tfm);
}
if (*dirtyBits & DirtyBits::DirtyParams) {
float3 strength = make_float3(1.0f, 1.0f, 1.0f);
{
const GfVec3f color = GetTypedValue<GfVec3f>(
lightDs, HdLightTokens->color, GfVec3f(1.0f, 1.0f, 1.0f));
strength = make_float3(color[0], color[1], color[2]);
}
strength *= exp2(GetTypedValue<float>(lightDs, HdLightTokens->exposure, 0.0f));
strength *= GetTypedValue<float>(lightDs, HdLightTokens->intensity, 1.0f);
if (_lightType == HdPrimTypeTokens->distantLight) {
/* Unclear why, but approximately matches Karma. */
strength *= 4.0f;
}
else {
/* Convert from intensity to radiant flux. */
strength *= M_PI_F;
}
_light->set_normalize(GetTypedValue<bool>(lightDs, HdLightTokens->normalize, false));
if (auto ds = GetTypedDataSource<bool>(lightDs, _tokens->visibleInPrimaryRay)) {
if (ds->GetTypedValue(0.0f)) {
_object->set_visibility(_object->get_visibility() | PATH_RAY_VISIBILITY_CAMERA);
}
else {
_object->set_visibility(_object->get_visibility() & ~PATH_RAY_VISIBILITY_CAMERA);
}
}
if (auto ds = GetTypedDataSource<bool>(lightDs, HdLightTokens->shadowEnable)) {
_light->set_cast_shadow(ds->GetTypedValue(0.0f));
}
if (_lightType == HdPrimTypeTokens->distantLight) {
if (auto ds = GetTypedDataSource<float>(lightDs, HdLightTokens->angle)) {
static_cast<SunLight *>(_light)->set_angle(GfDegreesToRadians(ds->GetTypedValue(0.0f)));
}
}
else if (_lightType == HdPrimTypeTokens->diskLight) {
AreaLight *area_light = static_cast<AreaLight *>(_light);
if (auto ds = GetTypedDataSource<float>(lightDs, HdLightTokens->radius)) {
const float size = ds->GetTypedValue(0.0f) * 2.0f;
area_light->set_sizeu(size);
area_light->set_sizev(size);
}
}
else if (_lightType == HdPrimTypeTokens->rectLight) {
AreaLight *area_light = static_cast<AreaLight *>(_light);
if (auto ds = GetTypedDataSource<float>(lightDs, HdLightTokens->width)) {
area_light->set_sizeu(ds->GetTypedValue(0.0f));
}
if (auto ds = GetTypedDataSource<float>(lightDs, HdLightTokens->height)) {
area_light->set_sizev(ds->GetTypedValue(0.0f));
}
}
else if (_lightType == HdPrimTypeTokens->sphereLight) {
SpotLight *spot_light = static_cast<SpotLight *>(_light);
const bool treatAsPoint = GetTypedValue<bool>(lightDs, _tokens->treatAsPoint, false);
if (treatAsPoint) {
spot_light->set_radius(0.0f);
}
else if (auto ds = GetTypedDataSource<float>(lightDs, HdLightTokens->radius)) {
spot_light->set_radius(ds->GetTypedValue(0.0f));
}
bool shaping = false;
if (auto ds = GetTypedDataSource<float>(lightDs, HdLightTokens->shapingConeAngle)) {
spot_light->set_angle(GfDegreesToRadians(ds->GetTypedValue(0.0f)) * 2.0f);
shaping = true;
}
if (auto ds = GetTypedDataSource<float>(lightDs, HdLightTokens->shapingConeSoftness)) {
spot_light->set_smooth(ds->GetTypedValue(0.0f));
shaping = true;
}
_light->set_light_type(shaping ? LIGHT_SPOT : LIGHT_POINT);
}
bool visible = true;
if (auto ds = HdVisibilitySchema::GetFromParent(primDs).GetVisibility()) {
visible = ds->GetTypedValue(0.0f);
}
// Disable invisible lights by zeroing the strength
// So 'LightManager::test_enabled_lights' updates the enabled flag correctly
if (!visible) {
strength = zero_float3();
}
_light->set_strength(strength);
_light->set_is_enabled(visible);
PopulateShaderGraph(lightDs);
}
// Need to update shader graph when transform changes in case transform was baked into it
else if (_object->tfm_is_modified() && (_lightType == HdPrimTypeTokens->domeLight ||
_light->get_shader()->has_surface_spatial_varying))
{
PopulateShaderGraph(lightDs);
}
if (_light->is_modified()) {
_light->tag_update(lock.scene);
}
*dirtyBits = DirtyBits::Clean;
}
void HdCyclesLight::PopulateShaderGraph(const HdContainerDataSourceHandle &lightContainer)
{
unique_ptr<ShaderGraph> graph = make_unique<ShaderGraph>();
ShaderNode *outputNode = nullptr;
if (_lightType == HdPrimTypeTokens->domeLight) {
BackgroundNode *bgNode = graph->create_node<BackgroundNode>();
// Bake strength into shader graph, since only the shader is used for background lights
bgNode->set_color(_light->get_strength());
graph->connect(bgNode->output("Background"), graph->output()->input("Surface"));
outputNode = bgNode;
}
else if (lightContainer) {
if (auto ds = HdStringDataSource::Cast(lightContainer->Get(_tokens->falloff))) {
const std::string strVal = ds->GetTypedValue(0.0f);
if (strVal == "Constant" || strVal == "Linear" || strVal == "Quadratic") {
LightFalloffNode *lfoNode = graph->create_node<LightFalloffNode>();
lfoNode->set_strength(1.f);
graph->connect(lfoNode->output(strVal.c_str()), graph->output()->input("Surface"));
outputNode = lfoNode;
}
}
}
if (outputNode == nullptr) {
EmissionNode *emissionNode = graph->create_node<EmissionNode>();
emissionNode->set_color(one_float3());
emissionNode->set_strength(1.0f);
graph->connect(emissionNode->output("Emission"), graph->output()->input("Surface"));
outputNode = emissionNode;
}
bool hasSpatialVarying = false;
bool hasColorTemperature = false;
if (lightContainer) {
const bool enableColorTemperature = GetTypedValue<bool>(
lightContainer, HdLightTokens->enableColorTemperature, false);
if (enableColorTemperature) {
if (auto ds = HdFloatDataSource::Cast(lightContainer->Get(HdLightTokens->colorTemperature)))
{
BlackbodyNode *blackbodyNode = graph->create_node<BlackbodyNode>();
blackbodyNode->set_temperature(ds->GetTypedValue(0.0f));
if (_lightType == HdPrimTypeTokens->domeLight) {
VectorMathNode *mathNode = graph->create_node<VectorMathNode>();
mathNode->set_math_type(NODE_VECTOR_MATH_MULTIPLY);
mathNode->set_vector2(_light->get_strength());
graph->connect(blackbodyNode->output("Color"), mathNode->input("Vector1"));
graph->connect(mathNode->output("Vector"), outputNode->input("Color"));
}
else {
graph->connect(blackbodyNode->output("Color"), outputNode->input("Color"));
}
hasColorTemperature = true;
}
}
if (auto ds = HdAssetPathDataSource::Cast(lightContainer->Get(HdLightTokens->shapingIesFile)))
{
const SdfAssetPath assetPath = ds->GetTypedValue(0.0f);
std::string filename = assetPath.GetResolvedPath();
if (filename.empty()) {
filename = assetPath.GetAssetPath();
}
if (!filename.empty()) {
TextureCoordinateNode *coordNode = graph->create_node<TextureCoordinateNode>();
coordNode->set_ob_tfm(_object->get_tfm());
coordNode->set_use_transform(true);
IESLightNode *iesNode = graph->create_node<IESLightNode>();
iesNode->set_filename(ustring(filename));
graph->connect(coordNode->output("Normal"), iesNode->input("Vector"));
graph->connect(iesNode->output("Fac"), outputNode->input("Strength"));
hasSpatialVarying = true;
}
}
if (auto ds = HdAssetPathDataSource::Cast(lightContainer->Get(HdLightTokens->textureFile))) {
const SdfAssetPath assetPath = ds->GetTypedValue(0.0f);
std::string filename = assetPath.GetResolvedPath();
if (filename.empty()) {
filename = assetPath.GetAssetPath();
}
if (!filename.empty()) {
ImageSlotTextureNode *textureNode = nullptr;
if (_lightType == HdPrimTypeTokens->domeLight) {
Transform tfm = _object->get_tfm();
transform_set_column(&tfm, 3, zero_float3()); // Remove translation
TextureCoordinateNode *coordNode = graph->create_node<TextureCoordinateNode>();
coordNode->set_ob_tfm(tfm);
coordNode->set_use_transform(true);
textureNode = graph->create_node<EnvironmentTextureNode>();
static_cast<EnvironmentTextureNode *>(textureNode)->set_filename(ustring(filename));
graph->connect(coordNode->output("Object"), textureNode->input("Vector"));
hasSpatialVarying = true;
}
else {
GeometryNode *coordNode = graph->create_node<GeometryNode>();
textureNode = graph->create_node<ImageTextureNode>();
static_cast<ImageTextureNode *>(textureNode)->set_filename(ustring(filename));
graph->connect(coordNode->output("Parametric"), textureNode->input("Vector"));
}
if (hasColorTemperature) {
VectorMathNode *mathNode = graph->create_node<VectorMathNode>();
mathNode->set_math_type(NODE_VECTOR_MATH_MULTIPLY);
graph->connect(textureNode->output("Color"), mathNode->input("Vector1"));
ShaderInput *const outputNodeInput = outputNode->input("Color");
graph->connect(outputNodeInput->link, mathNode->input("Vector2"));
graph->disconnect(outputNodeInput);
graph->connect(mathNode->output("Vector"), outputNodeInput);
}
else if (_lightType == HdPrimTypeTokens->domeLight) {
VectorMathNode *mathNode = graph->create_node<VectorMathNode>();
mathNode->set_math_type(NODE_VECTOR_MATH_MULTIPLY);
mathNode->set_vector2(_light->get_strength());
graph->connect(textureNode->output("Color"), mathNode->input("Vector1"));
graph->connect(mathNode->output("Vector"), outputNode->input("Color"));
}
else {
graph->connect(textureNode->output("Color"), outputNode->input("Color"));
}
}
}
}
Shader *const shader = _light->get_shader();
shader->set_graph(std::move(graph));
shader->tag_update((Scene *)_light->get_owner());
shader->has_surface_spatial_varying = hasSpatialVarying;
}
void HdCyclesLight::Finalize(HdRenderParam *renderParam)
{
if (!_light) {
return;
}
const SceneLock lock(renderParam);
const bool keep_nodes = static_cast<const HdCyclesSession *>(renderParam)->keep_nodes;
if (!keep_nodes) {
lock.scene->delete_node(_light);
lock.scene->delete_node(_object);
}
_light = nullptr;
_object = nullptr;
}
void HdCyclesLight::Initialize(HdRenderParam *renderParam)
{
if (_light) {
return;
}
const SceneLock lock(renderParam);
_object = lock.scene->create_node<Object>();
_object->name = GetId().GetString();
if (_lightType == HdPrimTypeTokens->domeLight) {
_light = lock.scene->create_node<BackgroundLight>();
}
else if (_lightType == HdPrimTypeTokens->distantLight) {
_light = lock.scene->create_node<SunLight>();
}
else if (_lightType == HdPrimTypeTokens->diskLight) {
_light = lock.scene->create_node<AreaLight>();
static_cast<AreaLight *>(_light)->set_ellipse(true);
}
else if (_lightType == HdPrimTypeTokens->rectLight) {
_light = lock.scene->create_node<AreaLight>();
static_cast<AreaLight *>(_light)->set_ellipse(false);
}
else if (_lightType == HdPrimTypeTokens->sphereLight) {
/* We can't know in advance if this is spot light or point light, so we set to derived class
* SpotLight and change the type later. */
_light = lock.scene->create_node<SpotLight>();
}
_light->set_use_mis(true);
_light->name = GetId().GetString();
_object->set_geometry(_light);
_object->set_random_id(hash_uint2(hash_string(_light->name.c_str()), 0));
_object->set_visibility(PATH_RAY_VISIBILITY_ALL & ~PATH_RAY_VISIBILITY_CAMERA);
Shader *const shader = lock.scene->create_node<Shader>();
array<Node *> used_shaders;
used_shaders.push_back_slow(shader);
_light->set_used_shaders(used_shaders);
// Create default shader graph
PopulateShaderGraph(HdContainerDataSourceHandle());
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,38 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "hydra/config.h"
#include <pxr/imaging/hd/dataSource.h>
#include <pxr/imaging/hd/light.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesLight final : public PXR_NS::HdLight {
public:
HdCyclesLight(const PXR_NS::SdfPath &sprimId, const PXR_NS::TfToken &lightType);
~HdCyclesLight() override;
PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override;
void Sync(PXR_NS::HdSceneDelegate *sceneDelegate,
PXR_NS::HdRenderParam *renderParam,
PXR_NS::HdDirtyBits *dirtyBits) override;
void Finalize(PXR_NS::HdRenderParam *renderParam) override;
private:
void Initialize(PXR_NS::HdRenderParam *renderParam);
void PopulateShaderGraph(const PXR_NS::HdContainerDataSourceHandle &lightContainer);
CCL_NS::Object *_object = nullptr;
CCL_NS::Light *_light = nullptr;
PXR_NS::TfToken _lightType;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,602 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "hydra/material.h"
#include "hydra/node_util.h"
#include "hydra/session.h"
#include "hydra/util.h"
#include "scene/scene.h"
#include "scene/shader.h"
#include "scene/shader_graph.h"
#include "scene/shader_nodes.h"
#include <pxr/imaging/hd/material.h>
#include <pxr/imaging/hd/materialConnectionSchema.h>
#include <pxr/imaging/hd/materialNetworkSchema.h>
#include <pxr/imaging/hd/materialNodeParameterSchema.h>
#include <pxr/imaging/hd/materialNodeSchema.h>
#include <pxr/imaging/hd/materialSchema.h>
#include <pxr/imaging/hd/sceneDelegate.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
/* Normalize a material network node name to a full SdfPath. The schema may
* provide either a full path or a bare identifier. */
static SdfPath MaterialNodeNameToSdfPath(const TfToken &nodeName)
{
const std::string &s = nodeName.GetString();
if (s.empty()) {
return SdfPath::EmptyPath();
}
if (s[0] == '/' && SdfPath::IsValidPathString(s)) {
return SdfPath(s);
}
return SdfPath::AbsoluteRootPath().AppendChild(nodeName);
}
// clang-format off
TF_DEFINE_PRIVATE_TOKENS(CyclesMaterialTokens,
(cycles)
((cyclesSurface, "cycles:surface"))
((cyclesDisplacement, "cycles:displacement"))
((cyclesVolume, "cycles:volume"))
(UsdPreviewSurface)
(UsdUVTexture)
(UsdPrimvarReader_float)
(UsdPrimvarReader_float2)
(UsdPrimvarReader_float3)
(UsdPrimvarReader_float4)
(UsdPrimvarReader_int)
(UsdTransform2d)
(a)
(rgb)
(r)
(g)
(b)
(result)
(st)
(wrapS)
(wrapT)
(periodic)
);
// clang-format on
/* Simple class to handle remapping of USDPreviewSurface nodes and parameters to Cycles
* equivalents. */
class UsdToCyclesMapping {
using ParamMap = std::unordered_map<TfToken, ustring, TfToken::HashFunctor>;
public:
UsdToCyclesMapping(const char *nodeType, ParamMap paramMap)
: _nodeType(nodeType), _paramMap(std::move(paramMap))
{
}
ustring nodeType() const
{
return _nodeType;
}
virtual std::string parameterName(const TfToken &name,
const ShaderInput *inputConnection,
VtValue * /*value*/ = nullptr) const
{
/* UsdNode.name -> Node.input. These all follow a simple pattern that we can just
* remap based on the name or 'Node.input' type. */
if (inputConnection) {
if (name == CyclesMaterialTokens->a) {
return "alpha";
}
if (name == CyclesMaterialTokens->rgb) {
return "color";
}
/* TODO: Is there a better mapping than 'color'? */
if (name == CyclesMaterialTokens->r || name == CyclesMaterialTokens->g ||
name == CyclesMaterialTokens->b)
{
return "color";
}
if (name == CyclesMaterialTokens->result) {
switch (inputConnection->socket_type.type) {
case SocketType::BOOLEAN:
case SocketType::FLOAT:
case SocketType::INT:
case SocketType::UINT:
return "alpha";
case SocketType::COLOR:
case SocketType::VECTOR:
case SocketType::POINT:
case SocketType::NORMAL:
default:
return "color";
}
}
}
/* Simple mapping case */
const auto it = _paramMap.find(name);
return it != _paramMap.end() ? it->second.string() : name.GetString();
}
private:
const ustring _nodeType;
ParamMap _paramMap;
};
class UsdToCyclesTexture : public UsdToCyclesMapping {
public:
using UsdToCyclesMapping::UsdToCyclesMapping;
std::string parameterName(const TfToken &name,
const ShaderInput *inputConnection,
VtValue *value) const override
{
if (value) {
/* Remap UsdUVTexture.wrapS and UsdUVTexture.wrapT to cycles_image_texture.extension. */
if (name == CyclesMaterialTokens->wrapS || name == CyclesMaterialTokens->wrapT) {
const std::string valueString = VtValue::Cast<std::string>(*value).Get<std::string>();
/* A value of 'repeat' in USD is equivalent to 'periodic' in Cycles. */
if (valueString == "repeat") {
*value = VtValue(CyclesMaterialTokens->periodic);
}
return "extension";
}
}
return UsdToCyclesMapping::parameterName(name, inputConnection, value);
}
};
namespace {
class UsdToCycles {
const UsdToCyclesMapping UsdPreviewSurface = {
"principled_bsdf",
{
{TfToken("diffuseColor"), ustring("base_color")},
{TfToken("emissiveColor"), ustring("emission")},
{TfToken("specularColor"), ustring("specular")},
{TfToken("clearcoatRoughness"), ustring("coat_roughness")},
{TfToken("opacity"), ustring("alpha")},
/* opacityThreshold */
/* occlusion */
/* displacement */
}};
const UsdToCyclesTexture UsdUVTexture = {
"image_texture",
{
{CyclesMaterialTokens->st, ustring("vector")},
{CyclesMaterialTokens->wrapS, ustring("extension")},
{CyclesMaterialTokens->wrapT, ustring("extension")},
{TfToken("file"), ustring("filename")},
{TfToken("sourceColorSpace"), ustring("colorspace")},
}};
const UsdToCyclesMapping UsdPrimvarReader = {"attribute",
{{TfToken("varname"), ustring("attribute")}}};
public:
const UsdToCyclesMapping *findUsd(const TfToken &usdNodeType)
{
if (usdNodeType == CyclesMaterialTokens->UsdPreviewSurface) {
return &UsdPreviewSurface;
}
if (usdNodeType == CyclesMaterialTokens->UsdUVTexture) {
return &UsdUVTexture;
}
if (usdNodeType == CyclesMaterialTokens->UsdPrimvarReader_float ||
usdNodeType == CyclesMaterialTokens->UsdPrimvarReader_float2 ||
usdNodeType == CyclesMaterialTokens->UsdPrimvarReader_float3 ||
usdNodeType == CyclesMaterialTokens->UsdPrimvarReader_float4 ||
usdNodeType == CyclesMaterialTokens->UsdPrimvarReader_int)
{
return &UsdPrimvarReader;
}
return nullptr;
}
const UsdToCyclesMapping *findCycles(const ustring & /*cyclesNodeType*/)
{
return nullptr;
}
};
TfStaticData<UsdToCycles> sUsdToCyles;
} // namespace
HdCyclesMaterial::HdCyclesMaterial(const SdfPath &sprimId) : HdMaterial(sprimId) {}
HdCyclesMaterial::~HdCyclesMaterial() = default;
HdDirtyBits HdCyclesMaterial::GetInitialDirtyBitsMask() const
{
return DirtyBits::DirtyResource | DirtyBits::DirtyParams;
}
void HdCyclesMaterial::Sync(HdSceneDelegate *sceneDelegate,
HdRenderParam *renderParam,
HdDirtyBits *dirtyBits)
{
if (*dirtyBits == DirtyBits::Clean) {
return;
}
Initialize(renderParam);
const SceneLock lock(renderParam);
const bool dirtyParams = (*dirtyBits & DirtyBits::DirtyParams);
const bool dirtyResource = (*dirtyBits & DirtyBits::DirtyResource);
const SdfPath &id = GetId();
if (dirtyResource || dirtyParams) {
const HdSceneIndexPrim prim = GetPrim(sceneDelegate, id);
const HdContainerDataSourceHandle &primDs = prim.dataSource;
HdMaterialSchema matSchema = HdMaterialSchema::GetFromParent(primDs);
/* Prefer cycles network if it exists, otherwise use universal network. */
HdMaterialNetworkSchema network = matSchema.GetMaterialNetwork(CyclesMaterialTokens->cycles);
if (!network) {
network = matSchema.GetMaterialNetwork();
}
if (network) {
if (!_nodes.empty() && !dirtyResource) {
UpdateParameters(network);
_shader->tag_modified();
}
else {
PopulateShaderGraph(network);
}
}
else {
TF_RUNTIME_ERROR("Could not get a material network for %s.", id.GetText());
}
}
if (_shader->is_modified()) {
_shader->tag_update(lock.scene);
}
*dirtyBits = DirtyBits::Clean;
}
void HdCyclesMaterial::UpdateParameters(NodeDesc &nodeDesc,
HdMaterialNodeParameterContainerSchema params,
const SdfPath &nodePath)
{
for (const TfToken &paramName : params.GetNames()) {
auto valueDs = params.Get(paramName).GetValue();
if (!valueDs) {
continue;
}
VtValue value = valueDs->GetValue(0.0f);
/* See if the parameter name is in USDPreviewSurface terms, and needs to be converted .*/
const UsdToCyclesMapping *inputMapping = nodeDesc.mapping;
const std::string inputName = inputMapping ?
inputMapping->parameterName(paramName, nullptr, &value) :
paramName.GetString();
/* Find the input to write the parameter value to. */
const SocketType *input = nullptr;
for (const SocketType &socket : nodeDesc.node->type->inputs) {
if (string_iequals(socket.name.string(), inputName) || socket.ui_name == inputName) {
input = &socket;
break;
}
}
if (!input) {
TF_WARN("Could not find parameter '%s' on node '%s' ('%s')",
paramName.GetText(),
nodePath.GetText(),
nodeDesc.node->name.c_str());
continue;
}
SetNodeValue(nodeDesc.node, *input, value);
}
}
void HdCyclesMaterial::UpdateParameters(HdMaterialNetworkSchema network)
{
HdMaterialNodeContainerSchema nodes = network.GetNodes();
for (const TfToken &nodeName : nodes.GetNames()) {
const SdfPath nodePath = MaterialNodeNameToSdfPath(nodeName);
const auto nodeIt = _nodes.find(nodePath);
if (nodeIt == _nodes.end()) {
TF_RUNTIME_ERROR("Could not update parameters on missing node '%s'", nodePath.GetText());
continue;
}
UpdateParameters(nodeIt->second, nodes.Get(nodeName).GetParameters(), nodePath);
}
}
void HdCyclesMaterial::UpdateConnections(NodeDesc &nodeDesc,
HdMaterialNodeSchema nodeSchema,
const SdfPath &nodePath,
ShaderGraph *shaderGraph)
{
HdMaterialConnectionVectorContainerSchema conns = nodeSchema.GetInputConnections();
for (const TfToken &dstSocketName : conns.GetNames()) {
HdMaterialConnectionVectorSchema connVec = conns.Get(dstSocketName);
const size_t count = connVec.GetNumElements();
if (count == 0) {
continue;
}
const UsdToCyclesMapping *inputMapping = nodeDesc.mapping;
const std::string inputName = inputMapping ?
inputMapping->parameterName(dstSocketName, nullptr) :
dstSocketName.GetString();
/* Find the input to connect to on the passed in node. */
ShaderInput *input = nullptr;
for (ShaderInput *in : nodeDesc.node->inputs) {
if (string_iequals(in->socket_type.name.string(), inputName)) {
input = in;
break;
}
}
if (!input) {
TF_WARN("Ignoring connection on '%s.%s', input '%s' was not found",
nodePath.GetText(),
dstSocketName.GetText(),
dstSocketName.GetText());
continue;
}
/* USD allows N connections per input (MaterialX <switch>, <combine>, struct
* inputs etc). Cycles inputs are single-connection, and the right lowering
* depends on the node type, so just take the first and warn. */
if (count > 1) {
TF_WARN(
"Ignoring multiple connections to '%s.%s'", nodePath.GetText(), dstSocketName.GetText());
}
HdMaterialConnectionSchema connSchema = connVec.GetElement(0);
const SdfPath upstreamNodePath =
connSchema.GetUpstreamNodePath() ?
MaterialNodeNameToSdfPath(connSchema.GetUpstreamNodePath()->GetTypedValue(0.0f)) :
SdfPath();
const TfToken upstreamOutputName = connSchema.GetUpstreamNodeOutputName() ?
connSchema.GetUpstreamNodeOutputName()->GetTypedValue(
0.0f) :
TfToken();
const auto srcNodeIt = _nodes.find(upstreamNodePath);
if (srcNodeIt == _nodes.end()) {
TF_WARN("Ignoring connection from '%s.%s' to '%s.%s', node '%s' was not found",
upstreamNodePath.GetText(),
upstreamOutputName.GetText(),
nodePath.GetText(),
dstSocketName.GetText(),
upstreamNodePath.GetText());
continue;
}
const UsdToCyclesMapping *outputMapping = srcNodeIt->second.mapping;
const std::string outputName = outputMapping ?
outputMapping->parameterName(upstreamOutputName, input) :
upstreamOutputName.GetString();
ShaderOutput *output = nullptr;
for (ShaderOutput *out : srcNodeIt->second.node->outputs) {
if (string_iequals(out->socket_type.name.string(), outputName)) {
output = out;
break;
}
}
if (!output) {
TF_WARN("Ignoring connection from '%s.%s' to '%s.%s', output '%s' was not found",
upstreamNodePath.GetText(),
upstreamOutputName.GetText(),
nodePath.GetText(),
dstSocketName.GetText(),
upstreamOutputName.GetText());
continue;
}
shaderGraph->connect(output, input);
}
}
void HdCyclesMaterial::PopulateShaderGraph(HdMaterialNetworkSchema network)
{
_nodes.clear();
unique_ptr<ShaderGraph> graph = make_unique<ShaderGraph>();
HdMaterialNodeContainerSchema nodes = network.GetNodes();
/* Iterate all the nodes first and build a complete but unconnected graph with parameters set. */
for (const TfToken &nodeName : nodes.GetNames()) {
HdMaterialNodeSchema nodeSchema = nodes.Get(nodeName);
const SdfPath nodePath = MaterialNodeNameToSdfPath(nodeName);
NodeDesc nodeDesc = {};
const auto nodeIt = _nodes.find(nodePath);
/* Create new node only if it does not exist yet. */
if (nodeIt != _nodes.end()) {
nodeDesc = nodeIt->second;
}
else {
/* E.g. cycles_principled_bsdf or UsdPreviewSurface. */
const TfToken nodeTypeIdToken = nodeSchema.GetNodeIdentifier() ?
nodeSchema.GetNodeIdentifier()->GetTypedValue(0.0f) :
TfToken();
const std::string &nodeTypeId = nodeTypeIdToken.GetString();
ustring cyclesType(nodeTypeId);
if (nodeTypeId.starts_with("cycles_") || nodeTypeId.starts_with("cycles:")) {
/* Native Cycles note embedded in USDShade. */
cyclesType = nodeTypeId.substr(strlen("cycles_"));
nodeDesc.mapping = sUsdToCyles->findCycles(cyclesType);
}
else {
/* Check if any remapping is needed (e.g. for USDPreviewSurface to Cycles nodes). */
nodeDesc.mapping = sUsdToCyles->findUsd(nodeTypeIdToken);
if (nodeDesc.mapping) {
cyclesType = nodeDesc.mapping->nodeType();
}
}
/* If it's a native Cycles' node-type, just do the lookup now. */
if (const NodeType *nodeType = NodeType::find(cyclesType)) {
nodeDesc.node = graph->create_node(nodeType);
_nodes.emplace(nodePath, nodeDesc);
}
else {
TF_RUNTIME_ERROR("Could not create node '%s'", nodePath.GetText());
continue;
}
}
UpdateParameters(nodeDesc, nodeSchema.GetParameters(), nodePath);
}
/* Now that all nodes have been constructed, iterate the network again and build up any
* connections between nodes. */
for (const TfToken &nodeName : nodes.GetNames()) {
const SdfPath nodePath = MaterialNodeNameToSdfPath(nodeName);
const auto nodeIt = _nodes.find(nodePath);
if (nodeIt == _nodes.end()) {
TF_RUNTIME_ERROR("Could not find node '%s' to connect", nodePath.GetText());
continue;
}
UpdateConnections(nodeIt->second, nodes.Get(nodeName), nodePath, graph.get());
}
/* Finally connect the terminals to the graph output (Surface, Volume, Displacement). */
HdMaterialConnectionContainerSchema terminals = network.GetTerminals();
for (const TfToken &terminalName : terminals.GetNames()) {
HdMaterialConnectionSchema termSchema = terminals.Get(terminalName);
const SdfPath upstreamNodePath =
termSchema.GetUpstreamNodePath() ?
MaterialNodeNameToSdfPath(termSchema.GetUpstreamNodePath()->GetTypedValue(0.0f)) :
SdfPath();
const TfToken upstreamOutputName = termSchema.GetUpstreamNodeOutputName() ?
termSchema.GetUpstreamNodeOutputName()->GetTypedValue(
0.0f) :
TfToken();
const auto nodeIt = _nodes.find(upstreamNodePath);
if (nodeIt == _nodes.end()) {
TF_RUNTIME_ERROR("Could not find terminal node '%s'", upstreamNodePath.GetText());
continue;
}
ShaderNode *const node = nodeIt->second.node;
const char *inputName = nullptr;
const char *outputName = nullptr;
if (terminalName == HdMaterialTerminalTokens->surface ||
terminalName == CyclesMaterialTokens->cyclesSurface)
{
inputName = "Surface";
/* Find default output name based on the node if none is provided. */
if (node->type->name == "add_closure" || node->type->name == "mix_closure") {
outputName = "Closure";
}
else if (node->type->name == "emission") {
outputName = "Emission";
}
else {
outputName = "BSDF";
}
}
else if (terminalName == HdMaterialTerminalTokens->displacement ||
terminalName == CyclesMaterialTokens->cyclesDisplacement)
{
inputName = outputName = "Displacement";
}
else if (terminalName == HdMaterialTerminalTokens->volume ||
terminalName == CyclesMaterialTokens->cyclesVolume)
{
inputName = outputName = "Volume";
}
/* For native Cycles nodes we use the upstream output name as is, for
* mapping from e.g. UsdPreviewSurface we need to use the default output
* name that is known to exist. */
if (!upstreamOutputName.IsEmpty() && nodeIt->second.mapping == nullptr) {
outputName = upstreamOutputName.GetText();
}
ShaderInput *const input = inputName ? graph->output()->input(inputName) : nullptr;
if (!input) {
TF_RUNTIME_ERROR("Could not find terminal input '%s.%s'",
upstreamNodePath.GetText(),
inputName ? inputName : "<null>");
continue;
}
ShaderOutput *const output = outputName ? node->output(outputName) : nullptr;
if (!output) {
TF_RUNTIME_ERROR("Could not find terminal output '%s.%s'",
upstreamNodePath.GetText(),
outputName ? outputName : "<null>");
continue;
}
graph->connect(output, input);
}
/* Create the instanceId AOV output. */
{
const ustring instanceId(HdAovTokens->instanceId.GetString());
OutputAOVNode *aovNode = graph->create_node<OutputAOVNode>();
aovNode->set_name(instanceId);
AttributeNode *instanceIdNode = graph->create_node<AttributeNode>();
instanceIdNode->set_attribute(instanceId);
graph->connect(instanceIdNode->output("Fac"), aovNode->input("Value"));
}
_shader->set_graph(std::move(graph));
}
void HdCyclesMaterial::Finalize(HdRenderParam *renderParam)
{
if (!_shader) {
return;
}
const SceneLock lock(renderParam);
const bool keep_nodes = static_cast<const HdCyclesSession *>(renderParam)->keep_nodes;
_nodes.clear();
if (!keep_nodes) {
lock.scene->delete_node(_shader);
}
_shader = nullptr;
}
void HdCyclesMaterial::Initialize(HdRenderParam *renderParam)
{
if (_shader) {
return;
}
const SceneLock lock(renderParam);
_shader = lock.scene->create_node<Shader>();
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,60 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "hydra/config.h"
#include <pxr/imaging/hd/material.h>
#include <pxr/imaging/hd/materialNetworkSchema.h>
#include <pxr/imaging/hd/materialNodeParameterSchema.h>
#include <pxr/imaging/hd/materialNodeSchema.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesMaterial final : public PXR_NS::HdMaterial {
public:
HdCyclesMaterial(const PXR_NS::SdfPath &sprimId);
~HdCyclesMaterial() override;
PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override;
void Sync(PXR_NS::HdSceneDelegate *sceneDelegate,
PXR_NS::HdRenderParam *renderParam,
PXR_NS::HdDirtyBits *dirtyBits) override;
void Finalize(PXR_NS::HdRenderParam *renderParam) override;
CCL_NS::Shader *GetCyclesShader() const
{
return _shader;
}
private:
struct NodeDesc {
CCL_NS::ShaderNode *node;
const class UsdToCyclesMapping *mapping;
};
void Initialize(PXR_NS::HdRenderParam *renderParam);
void UpdateParameters(NodeDesc &nodeDesc,
PXR_NS::HdMaterialNodeParameterContainerSchema params,
const PXR_NS::SdfPath &nodePath);
void UpdateParameters(PXR_NS::HdMaterialNetworkSchema network);
void UpdateConnections(NodeDesc &nodeDesc,
PXR_NS::HdMaterialNodeSchema nodeSchema,
const PXR_NS::SdfPath &nodePath,
CCL_NS::ShaderGraph *shaderGraph);
void PopulateShaderGraph(PXR_NS::HdMaterialNetworkSchema network);
CCL_NS::Shader *_shader = nullptr;
std::unordered_map<PXR_NS::SdfPath, NodeDesc, PXR_NS::SdfPath::Hash> _nodes;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,617 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "hydra/mesh.h"
#include "hydra/geometry.inl"
#include "hydra/util.h"
#include "scene/mesh.h"
#include <pxr/base/gf/vec2f.h>
#include <pxr/imaging/hd/geomSubsetSchema.h>
#include <pxr/imaging/hd/legacyDisplayStyleSchema.h>
#include <pxr/imaging/hd/materialBindingSchema.h>
#include <pxr/imaging/hd/materialBindingsSchema.h>
#include <pxr/imaging/hd/meshSchema.h>
#include <pxr/imaging/hd/meshTopologySchema.h>
#include <pxr/imaging/hd/sceneIndex.h>
#include <pxr/imaging/hd/subdivisionTagsSchema.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
namespace {
template<typename T>
VtValue ComputeTriangulatedUniformPrimvar(VtValue value, const VtIntArray &primitiveParams)
{
T output;
output.reserve(primitiveParams.size());
const T &input = value.Get<T>();
for (size_t i = 0; i < primitiveParams.size(); ++i) {
const int faceIndex = HdMeshUtil::DecodeFaceIndexFromCoarseFaceParam(primitiveParams[i]);
output.push_back(input[faceIndex]);
}
return VtValue(output);
}
VtValue ComputeTriangulatedUniformPrimvar(VtValue value,
const HdType valueType,
const VtIntArray &primitiveParams)
{
switch (valueType) {
case HdTypeFloat:
return ComputeTriangulatedUniformPrimvar<VtFloatArray>(value, primitiveParams);
case HdTypeFloatVec2:
return ComputeTriangulatedUniformPrimvar<VtVec2fArray>(value, primitiveParams);
case HdTypeFloatVec3:
return ComputeTriangulatedUniformPrimvar<VtVec3fArray>(value, primitiveParams);
case HdTypeFloatVec4:
return ComputeTriangulatedUniformPrimvar<VtVec4fArray>(value, primitiveParams);
default:
TF_RUNTIME_ERROR("Unsupported attribute type %d", static_cast<int>(valueType));
return VtValue();
}
}
VtValue ComputeTriangulatedFaceVaryingPrimvar(VtValue value,
const HdType valueType,
HdMeshUtil &meshUtil)
{
if (meshUtil.ComputeTriangulatedFaceVaryingPrimvar(
HdGetValueData(value), value.GetArraySize(), valueType, &value)
#if PXR_VERSION >= 2511
!= HdMeshComputationResult::Error
#endif
)
{
return value;
}
return VtValue();
}
} // namespace
Transform convert_transform(const GfMatrix4d &matrix)
{
return make_transform(matrix[0][0],
matrix[1][0],
matrix[2][0],
matrix[3][0],
matrix[0][1],
matrix[1][1],
matrix[2][1],
matrix[3][1],
matrix[0][2],
matrix[1][2],
matrix[2][2],
matrix[3][2]);
}
HdCyclesMesh::HdCyclesMesh(const SdfPath &rprimId)
: HdCyclesGeometry(rprimId), _util(&_topology, rprimId)
{
}
HdCyclesMesh::~HdCyclesMesh() = default;
HdDirtyBits HdCyclesMesh::GetInitialDirtyBitsMask() const
{
HdDirtyBits bits = HdCyclesGeometry::GetInitialDirtyBitsMask();
bits |= HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyNormals |
HdChangeTracker::DirtyPrimvar | HdChangeTracker::DirtyTopology |
HdChangeTracker::DirtyDisplayStyle | HdChangeTracker::DirtySubdivTags;
return bits;
}
HdDirtyBits HdCyclesMesh::_PropagateDirtyBits(HdDirtyBits bits) const
{
if (bits & (HdChangeTracker::DirtyMaterialId)) {
// Update used shaders from geometry subsets if any exist in the topology
bits |= HdChangeTracker::DirtyTopology;
}
if (bits & (HdChangeTracker::DirtyTopology | HdChangeTracker::DirtyDisplayStyle |
HdChangeTracker::DirtySubdivTags))
{
// Do full topology update when display style or subdivision changes
bits |= HdChangeTracker::DirtyTopology | HdChangeTracker::DirtyDisplayStyle |
HdChangeTracker::DirtySubdivTags;
}
if (bits & (HdChangeTracker::DirtyTopology)) {
// Changing topology clears the geometry, so need to populate everything again
bits |= HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyNormals |
HdChangeTracker::DirtyPrimvar;
}
return bits;
}
void HdCyclesMesh::Populate(HdSceneDelegate *sceneDelegate, HdDirtyBits dirtyBits, bool &rebuild)
{
if (HdChangeTracker::IsTopologyDirty(dirtyBits, GetId())) {
PopulateTopology(sceneDelegate);
}
if (dirtyBits & HdChangeTracker::DirtyPoints) {
PopulatePoints(sceneDelegate);
}
// Must happen after topology update, so that normals attribute size can be calculated
if (dirtyBits & HdChangeTracker::DirtyNormals) {
PopulateNormals(sceneDelegate);
}
// Must happen after topology update, so that appropriate attribute set can be selected
if (dirtyBits & HdChangeTracker::DirtyPrimvar) {
PopulatePrimvars(sceneDelegate);
}
rebuild = (_geom->triangles_is_modified()) || (_geom->subd_start_corner_is_modified()) ||
(_geom->subd_num_corners_is_modified()) || (_geom->subd_shader_is_modified()) ||
(_geom->subd_smooth_is_modified()) || (_geom->subd_ptex_offset_is_modified()) ||
(_geom->subd_face_corners_is_modified());
}
void HdCyclesMesh::PopulatePoints(HdSceneDelegate *sceneDelegate)
{
const HdSceneIndexPrim prim = GetPrim(sceneDelegate, GetId());
const HdPrimvarsSchema primvars = HdPrimvarsSchema::GetFromParent(prim.dataSource);
const VtValue value = ReadPrimvar(primvars, HdTokens->points);
if (!value.IsHolding<VtVec3fArray>()) {
TF_WARN("Invalid points data for %s", GetId().GetText());
return;
}
const auto &points = value.UncheckedGet<VtVec3fArray>();
TF_VERIFY(points.size() >= static_cast<size_t>(_topology.GetNumPoints()));
const bool subdivision = _geom->get_subdivision_type() != Mesh::SUBDIVISION_NONE;
AttributeSet &attributes = (subdivision) ? _geom->subd_attributes : _geom->attributes;
Attribute *attr_P = attributes.add(ATTR_STD_POSITION);
packed_float3 *verts = attr_P->data_for_write<packed_float3>();
std::copy_n(
reinterpret_cast<const packed_float3 *>(points.data()), _topology.GetNumPoints(), verts);
_geom->tag_position_modified();
}
void HdCyclesMesh::PopulateNormals(HdSceneDelegate *sceneDelegate)
{
_geom->attributes.remove(ATTR_STD_VERTEX_NORMAL);
// Authored normals should only exist on triangle meshes
if (_geom->get_subdivision_type() != Mesh::SUBDIVISION_NONE) {
return;
}
const HdSceneIndexPrim prim = GetPrim(sceneDelegate, GetId());
const HdPrimvarsSchema primvars = HdPrimvarsSchema::GetFromParent(prim.dataSource);
const HdInterpolation interpolation = ReadPrimvarInterpolation(primvars, HdTokens->normals);
if (interpolation == HdInterpolationCount) {
return; // Ignore missing normals
}
const VtValue value = ReadPrimvar(primvars, HdTokens->normals);
if (!value.IsHolding<VtVec3fArray>()) {
TF_WARN("Invalid normals data for %s", GetId().GetText());
return;
}
const auto &normals = value.UncheckedGet<VtVec3fArray>();
if (interpolation == HdInterpolationConstant) {
TF_VERIFY(normals.size() == 1);
const GfVec3f constantNormal = normals[0];
packed_normal *const N =
_geom->attributes.add(ATTR_STD_VERTEX_NORMAL)->data_for_write<packed_normal>();
for (size_t i = 0; i < _geom->num_verts(); ++i) {
N[i] = packed_normal(make_float3(constantNormal[0], constantNormal[1], constantNormal[2]));
}
}
else if (interpolation == HdInterpolationUniform) {
TF_VERIFY(normals.size() == static_cast<size_t>(_topology.GetNumFaces()));
/* Nothing to do, face normals are computed on demand in the kernel. */
}
else if (interpolation == HdInterpolationVertex || interpolation == HdInterpolationVarying) {
TF_VERIFY(normals.size() == static_cast<size_t>(_topology.GetNumPoints()) &&
static_cast<size_t>(_topology.GetNumPoints()) == _geom->num_verts());
packed_normal *const N =
_geom->attributes.add(ATTR_STD_VERTEX_NORMAL)->data_for_write<packed_normal>();
for (size_t i = 0; i < _geom->num_verts(); ++i) {
N[i] = packed_normal(make_float3(normals[i][0], normals[i][1], normals[i][2]));
}
}
else if (interpolation == HdInterpolationFaceVarying) {
TF_VERIFY(normals.size() == static_cast<size_t>(_topology.GetNumFaceVaryings()));
// TODO: Cycles has no per-corner normals, so ignore until supported.
#if 0
if (!_util.ComputeTriangulatedFaceVaryingPrimvar(
normals.data(), normals.size(), HdTypeFloatVec3, &value))
{
return;
}
const auto &normalsTriangulated = value.UncheckedGet<VtVec3fArray>();
#endif
}
}
void HdCyclesMesh::PopulatePrimvars(HdSceneDelegate *sceneDelegate)
{
Scene *const scene = (Scene *)_geom->get_owner();
const bool subdivision = _geom->get_subdivision_type() != Mesh::SUBDIVISION_NONE;
AttributeSet &attributes = subdivision ? _geom->subd_attributes : _geom->attributes;
const HdSceneIndexPrim prim = GetPrim(sceneDelegate, GetId());
const HdPrimvarsSchema primvars = HdPrimvarsSchema::GetFromParent(prim.dataSource);
const std::pair<HdInterpolation, AttributeElement> interpolations[] = {
std::make_pair(HdInterpolationFaceVarying, ATTR_ELEMENT_CORNER),
std::make_pair(HdInterpolationUniform, ATTR_ELEMENT_FACE),
std::make_pair(HdInterpolationVertex, ATTR_ELEMENT_VERTEX),
std::make_pair(HdInterpolationVarying, ATTR_ELEMENT_VERTEX),
std::make_pair(HdInterpolationConstant, ATTR_ELEMENT_OBJECT),
};
for (const auto &interpolation : interpolations) {
for (const TfToken &primvarName : PrimvarNamesAtInterpolation(primvars, interpolation.first)) {
// Skip special primvars that are handled separately
if (primvarName == HdTokens->points || primvarName == HdTokens->normals) {
continue;
}
VtValue value = ReadPrimvar(primvars, primvarName);
if (value.IsEmpty()) {
continue;
}
const TfToken role = ReadPrimvarRole(primvars, primvarName);
const ustring name(primvarName.GetString());
AttributeStandard std = ATTR_STD_NONE;
if (role == HdPrimvarRoleTokens->textureCoordinate) {
std = ATTR_STD_UV;
}
else if (interpolation.first == HdInterpolationVertex) {
if (primvarName == HdTokens->displayColor || role == HdPrimvarRoleTokens->color) {
std = ATTR_STD_VERTEX_COLOR;
}
else if (primvarName == HdTokens->normals) {
std = ATTR_STD_VERTEX_NORMAL;
}
}
else if (primvarName == HdTokens->displayColor &&
interpolation.first == HdInterpolationConstant)
{
if (value.IsHolding<VtVec3fArray>() && value.GetArraySize() == 1) {
const GfVec3f color = value.UncheckedGet<VtVec3fArray>()[0];
_instances[0]->set_color(make_float3(color[0], color[1], color[2]));
}
}
// Skip attributes that are not needed
if ((std != ATTR_STD_NONE && _geom->need_attribute(scene, std)) ||
_geom->need_attribute(scene, name))
{
const HdType valueType = HdGetValueTupleType(value).type;
if (!subdivision) {
// Adjust attributes for polygons that were triangulated
if (interpolation.first == HdInterpolationUniform) {
value = ComputeTriangulatedUniformPrimvar(value, valueType, _primitiveParams);
if (value.IsEmpty()) {
continue;
}
}
else if (interpolation.first == HdInterpolationFaceVarying) {
value = ComputeTriangulatedFaceVaryingPrimvar(value, valueType, _util);
if (value.IsEmpty()) {
continue;
}
}
}
ApplyPrimvars(attributes, name, value, interpolation.second, std);
}
}
}
}
void HdCyclesMesh::PopulateTopology(HdSceneDelegate *sceneDelegate)
{
// Clear geometry before populating it again with updated topology
_geom->clear(true);
const HdSceneIndexPrim prim = GetPrim(sceneDelegate, GetId());
const HdLegacyDisplayStyleSchema displayStyleSchema = HdLegacyDisplayStyleSchema::GetFromParent(
prim.dataSource);
int refineLevel = 0;
if (auto ds = displayStyleSchema.GetRefineLevel()) {
refineLevel = ds->GetTypedValue(0.0f);
}
bool flatShadingEnabled = false;
if (auto ds = displayStyleSchema.GetFlatShadingEnabled()) {
flatShadingEnabled = ds->GetTypedValue(0.0f);
}
const HdMeshSchema meshSchema = HdMeshSchema::GetFromParent(prim.dataSource);
const HdMeshTopologySchema topoSchema = meshSchema.GetTopology();
TfToken scheme = PxOsdOpenSubdivTokens->none;
if (auto ds = meshSchema.GetSubdivisionScheme()) {
scheme = ds->GetTypedValue(0.0f);
}
TfToken orientation = HdTokens->rightHanded;
if (auto ds = topoSchema.GetOrientation()) {
orientation = ds->GetTypedValue(0.0f);
}
VtIntArray faceVertexCounts;
if (auto ds = topoSchema.GetFaceVertexCounts()) {
faceVertexCounts = ds->GetTypedValue(0.0f);
}
VtIntArray faceVertexIndices;
if (auto ds = topoSchema.GetFaceVertexIndices()) {
faceVertexIndices = ds->GetTypedValue(0.0f);
}
VtIntArray holeIndices;
if (auto ds = topoSchema.GetHoleIndices()) {
holeIndices = ds->GetTypedValue(0.0f);
}
_topology = HdMeshTopology(
scheme, orientation, faceVertexCounts, faceVertexIndices, holeIndices, refineLevel);
/* Geom subsets are published as child prims of the mesh in the scene index. */
HdGeomSubsets geomSubsetsList;
if (HdSceneIndexBaseRefPtr si = sceneDelegate->GetRenderIndex().GetTerminalSceneIndex()) {
for (const SdfPath &childPath : si->GetChildPrimPaths(GetId())) {
const HdSceneIndexPrim childPrim = si->GetPrim(childPath);
if (childPrim.primType != HdPrimTypeTokens->geomSubset) {
continue;
}
const HdGeomSubsetSchema subsetSchema = HdGeomSubsetSchema::GetFromParent(
childPrim.dataSource);
/* Only face subsets supported here, not point or curve subsets. */
if (auto typeDs = subsetSchema.GetType()) {
if (typeDs->GetTypedValue(0.0f) != HdGeomSubsetSchemaTokens->typeFaceSet) {
continue;
}
}
HdGeomSubset subset;
subset.type = HdGeomSubset::TypeFaceSet;
if (auto ds = subsetSchema.GetIndices()) {
subset.indices = ds->GetTypedValue(0.0f);
}
if (auto pathDs = HdMaterialBindingsSchema::GetFromParent(childPrim.dataSource)
.GetMaterialBinding()
.GetPath())
{
subset.materialId = pathDs->GetTypedValue(0.0f);
}
geomSubsetsList.push_back(subset);
}
}
_topology.SetGeomSubsets(geomSubsetsList);
const TfToken subdivScheme = _topology.GetScheme();
if (subdivScheme == PxOsdOpenSubdivTokens->bilinear && _topology.GetRefineLevel() > 0) {
_geom->set_subdivision_type(Mesh::SUBDIVISION_LINEAR);
}
else if (subdivScheme == PxOsdOpenSubdivTokens->catmullClark && _topology.GetRefineLevel() > 0) {
_geom->set_subdivision_type(Mesh::SUBDIVISION_CATMULL_CLARK);
}
else {
_geom->set_subdivision_type(Mesh::SUBDIVISION_NONE);
}
const bool smooth = !flatShadingEnabled;
const bool subdivision = _geom->get_subdivision_type() != Mesh::SUBDIVISION_NONE;
// Initialize lookup table from polygon face to material shader index
VtIntArray faceShaders(_topology.GetNumFaces(), 0);
const HdGeomSubsets &geomSubsets = _topology.GetGeomSubsets();
if (!geomSubsets.empty()) {
array<Node *> usedShaders = std::move(_geom->get_used_shaders());
// Remove any previous materials except for the material assigned to the prim
usedShaders.resize(1);
std::unordered_map<SdfPath, int, SdfPath::Hash> materials;
for (const HdGeomSubset &geomSubset : geomSubsets) {
TF_VERIFY(geomSubset.type == HdGeomSubset::TypeFaceSet);
int shader = 0;
const auto it = materials.find(geomSubset.materialId);
if (it != materials.end()) {
shader = it->second;
}
else {
const auto *const material = static_cast<const HdCyclesMaterial *>(
sceneDelegate->GetRenderIndex().GetSprim(HdPrimTypeTokens->material,
geomSubset.materialId));
if (material && material->GetCyclesShader()) {
shader = static_cast<int>(usedShaders.size());
usedShaders.push_back_slow(material->GetCyclesShader());
materials.emplace(geomSubset.materialId, shader);
}
}
for (const int face : geomSubset.indices) {
faceShaders[face] = shader;
}
}
_geom->set_used_shaders(usedShaders);
}
const VtIntArray vertIndx = _topology.GetFaceVertexIndices();
const VtIntArray vertCounts = _topology.GetFaceVertexCounts();
if (!subdivision) {
VtVec3iArray triangles;
_util.ComputeTriangleIndices(&triangles, &_primitiveParams);
_geom->resize_mesh(_topology.GetNumPoints(), triangles.size());
int *geom_indices = _geom->get_triangles().data();
for (size_t i = 0; i < _primitiveParams.size(); ++i) {
const GfVec3i triangle = triangles[i];
geom_indices[i * 3 + 0] = triangle[0];
geom_indices[i * 3 + 1] = triangle[1];
geom_indices[i * 3 + 2] = triangle[2];
}
int *shader = _geom->get_shader().data();
for (size_t i = 0; i < _primitiveParams.size(); ++i) {
const int faceIndex = HdMeshUtil::DecodeFaceIndexFromCoarseFaceParam(_primitiveParams[i]);
shader[i] = faceShaders[faceIndex];
}
std::ranges::fill(_geom->get_smooth(), smooth);
_geom->tag_triangles_modified();
_geom->tag_shader_modified();
_geom->tag_smooth_modified();
}
else {
const HdSubdivisionTagsSchema subdivSchema = HdSubdivisionTagsSchema::GetFromParent(
prim.dataSource);
PxOsdSubdivTags subdivTags;
if (auto ds = subdivSchema.GetInterpolateBoundary()) {
subdivTags.SetVertexInterpolationRule(ds->GetTypedValue(0.0f));
}
if (auto ds = subdivSchema.GetFaceVaryingLinearInterpolation()) {
subdivTags.SetFaceVaryingInterpolationRule(ds->GetTypedValue(0.0f));
}
if (auto ds = subdivSchema.GetTriangleSubdivisionRule()) {
subdivTags.SetTriangleSubdivision(ds->GetTypedValue(0.0f));
}
if (auto ds = subdivSchema.GetCornerIndices()) {
subdivTags.SetCornerIndices(ds->GetTypedValue(0.0f));
}
if (auto ds = subdivSchema.GetCornerSharpnesses()) {
subdivTags.SetCornerWeights(ds->GetTypedValue(0.0f));
}
if (auto ds = subdivSchema.GetCreaseIndices()) {
subdivTags.SetCreaseIndices(ds->GetTypedValue(0.0f));
}
if (auto ds = subdivSchema.GetCreaseLengths()) {
subdivTags.SetCreaseLengths(ds->GetTypedValue(0.0f));
}
if (auto ds = subdivSchema.GetCreaseSharpnesses()) {
subdivTags.SetCreaseWeights(ds->GetTypedValue(0.0f));
}
_topology.SetSubdivTags(subdivTags);
size_t numCorners = 0;
for (const int vertCount : vertCounts) {
numCorners += vertCount;
}
_geom->resize_subd_faces(_topology.GetNumFaces(), numCorners);
_geom->resize_mesh(_topology.GetNumPoints(), 0);
Attribute *subd_attr_P = _geom->subd_attributes.add(ATTR_STD_POSITION);
subd_attr_P->resize(_topology.GetNumPoints());
std::copy_n(vertIndx.data(), vertIndx.size(), _geom->get_subd_face_corners().data());
int *subd_start_corner = _geom->get_subd_start_corner().data();
int *subd_num_corners = _geom->get_subd_num_corners().data();
int *subd_ptex_offset = _geom->get_subd_ptex_offset().data();
// TODO: Handle hole indices
int ptex_offset = 0;
size_t faceIndex = 0;
size_t indexOffset = 0;
for (const int vertCount : vertCounts) {
subd_start_corner[faceIndex] = indexOffset;
subd_num_corners[faceIndex] = vertCount;
subd_ptex_offset[faceIndex] = ptex_offset;
const int num_ptex = (vertCount == 4) ? 1 : vertCount;
ptex_offset += num_ptex;
faceIndex++;
indexOffset += vertCount;
}
std::copy_n(faceShaders.data(), faceShaders.size(), _geom->get_subd_shader().data());
std::ranges::fill(_geom->get_subd_smooth(), smooth);
_geom->tag_subd_face_corners_modified();
_geom->tag_subd_start_corner_modified();
_geom->tag_subd_num_corners_modified();
_geom->tag_subd_shader_modified();
_geom->tag_subd_smooth_modified();
_geom->tag_subd_ptex_offset_modified();
const VtIntArray creaseLengths = subdivTags.GetCreaseLengths();
if (!creaseLengths.empty()) {
size_t numCreases = 0;
for (const int creaseLength : creaseLengths) {
numCreases += creaseLength - 1;
}
_geom->reserve_subd_creases(numCreases);
const VtIntArray creaseIndices = subdivTags.GetCreaseIndices();
const VtFloatArray creaseWeights = subdivTags.GetCreaseWeights();
indexOffset = 0;
size_t creaseLengthOffset = 0;
size_t createWeightOffset = 0;
for (const int creaseLength : creaseLengths) {
for (int j = 0; j < creaseLength - 1; ++j, ++createWeightOffset) {
const int v0 = creaseIndices[indexOffset + j];
const int v1 = creaseIndices[indexOffset + j + 1];
const float weight = creaseWeights.size() == creaseLengths.size() ?
creaseWeights[creaseLengthOffset] :
creaseWeights[createWeightOffset];
_geom->add_edge_crease(v0, v1, weight);
}
indexOffset += creaseLength;
creaseLengthOffset++;
}
const VtIntArray cornerIndices = subdivTags.GetCornerIndices();
const VtFloatArray cornerWeights = subdivTags.GetCornerWeights();
for (size_t i = 0; i < cornerIndices.size(); ++i) {
_geom->add_vertex_crease(cornerIndices[i], cornerWeights[i]);
}
}
_geom->set_subd_dicing_rate(1.0f);
_geom->set_subd_max_level(_topology.GetRefineLevel());
_geom->set_subd_objecttoworld(_instances[0]->get_tfm());
}
}
void HdCyclesMesh::Finalize(PXR_NS::HdRenderParam *renderParam)
{
_topology = HdMeshTopology();
_primitiveParams.clear();
HdCyclesGeometry<PXR_NS::HdMesh, Mesh>::Finalize(renderParam);
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,44 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "hydra/config.h"
#include "hydra/geometry.h"
#include <pxr/imaging/hd/mesh.h>
#include <pxr/imaging/hd/meshUtil.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesMesh final : public HdCyclesGeometry<PXR_NS::HdMesh, CCL_NS::Mesh> {
public:
HdCyclesMesh(const PXR_NS::SdfPath &rprimId);
~HdCyclesMesh() override;
PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override;
void Finalize(PXR_NS::HdRenderParam *renderParam) override;
private:
PXR_NS::HdDirtyBits _PropagateDirtyBits(PXR_NS::HdDirtyBits bits) const override;
void Populate(PXR_NS::HdSceneDelegate *sceneDelegate,
PXR_NS::HdDirtyBits dirtyBits,
bool &rebuild) override;
void PopulatePoints(PXR_NS::HdSceneDelegate *sceneDelegate);
void PopulateNormals(PXR_NS::HdSceneDelegate *sceneDelegate);
void PopulatePrimvars(PXR_NS::HdSceneDelegate *sceneDelegate);
void PopulateTopology(PXR_NS::HdSceneDelegate *sceneDelegate);
PXR_NS::HdMeshUtil _util;
PXR_NS::HdMeshTopology _topology;
PXR_NS::VtIntArray _primitiveParams;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,574 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "hydra/node_util.h"
#include "util/transform.h"
#include <pxr/base/gf/matrix3d.h>
#include <pxr/base/gf/matrix3f.h>
#include <pxr/base/gf/matrix4d.h>
#include <pxr/base/gf/matrix4f.h>
#include <pxr/base/gf/vec2f.h>
#include <pxr/base/gf/vec3f.h>
#include <pxr/base/vt/array.h>
#include <pxr/usd/sdf/assetPath.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
namespace {
template<typename DstType> DstType convertToCycles(const VtValue &value)
{
if (value.IsHolding<DstType>()) {
return value.UncheckedGet<DstType>();
}
const VtValue castedValue = VtValue::Cast<DstType>(value);
if (castedValue.IsHolding<DstType>()) {
return castedValue.UncheckedGet<DstType>();
}
TF_WARN("Could not convert VtValue to Cycles type");
return DstType(0);
}
template<> float2 convertToCycles<float2>(const VtValue &value)
{
const GfVec2f convertedValue = convertToCycles<GfVec2f>(value);
return make_float2(convertedValue[0], convertedValue[1]);
}
template<> float3 convertToCycles<float3>(const VtValue &value)
{
if (value.IsHolding<GfVec3f>()) {
const GfVec3f convertedValue = value.UncheckedGet<GfVec3f>();
return make_float3(convertedValue[0], convertedValue[1], convertedValue[2]);
}
if (value.IsHolding<GfVec4f>()) {
const GfVec4f convertedValue = value.UncheckedGet<GfVec4f>();
return make_float3(convertedValue[0], convertedValue[1], convertedValue[2]);
}
if (value.CanCast<GfVec3f>()) {
const GfVec3f convertedValue = VtValue::Cast<GfVec3f>(value).UncheckedGet<GfVec3f>();
return make_float3(convertedValue[0], convertedValue[1], convertedValue[2]);
}
if (value.CanCast<GfVec4f>()) {
const GfVec4f convertedValue = VtValue::Cast<GfVec4f>(value).UncheckedGet<GfVec4f>();
return make_float3(convertedValue[0], convertedValue[1], convertedValue[2]);
}
TF_WARN("Could not convert VtValue to float3");
return zero_float3();
}
template<> ustring convertToCycles<ustring>(const VtValue &value)
{
if (value.IsHolding<TfToken>()) {
return ustring(value.UncheckedGet<TfToken>().GetString());
}
if (value.IsHolding<std::string>()) {
return ustring(value.UncheckedGet<std::string>());
}
if (value.IsHolding<SdfAssetPath>()) {
const SdfAssetPath &path = value.UncheckedGet<SdfAssetPath>();
return ustring(path.GetResolvedPath());
}
if (value.CanCast<TfToken>()) {
return convertToCycles<ustring>(VtValue::Cast<TfToken>(value));
}
if (value.CanCast<std::string>()) {
return convertToCycles<ustring>(VtValue::Cast<std::string>(value));
}
if (value.CanCast<SdfAssetPath>()) {
return convertToCycles<ustring>(VtValue::Cast<SdfAssetPath>(value));
}
TF_WARN("Could not convert VtValue to ustring");
return ustring();
}
template<typename Matrix>
Transform convertMatrixToCycles(
const typename std::enable_if<Matrix::numRows == 3 && Matrix::numColumns == 3, Matrix>::type
&matrix)
{
return make_transform(matrix[0][0],
matrix[1][0],
matrix[2][0],
0,
matrix[0][1],
matrix[1][1],
matrix[2][1],
0,
matrix[0][2],
matrix[1][2],
matrix[2][2],
0);
}
template<typename Matrix>
Transform convertMatrixToCycles(
const typename std::enable_if<Matrix::numRows == 4 && Matrix::numColumns == 4, Matrix>::type
&matrix)
{
return make_transform(matrix[0][0],
matrix[1][0],
matrix[2][0],
matrix[3][0],
matrix[0][1],
matrix[1][1],
matrix[2][1],
matrix[3][1],
matrix[0][2],
matrix[1][2],
matrix[2][2],
matrix[3][2]);
}
template<> Transform convertToCycles<Transform>(const VtValue &value)
{
if (value.IsHolding<GfMatrix4f>()) {
return convertMatrixToCycles<GfMatrix4f>(value.UncheckedGet<GfMatrix4f>());
}
if (value.IsHolding<GfMatrix3f>()) {
return convertMatrixToCycles<GfMatrix3f>(value.UncheckedGet<GfMatrix3f>());
}
if (value.IsHolding<GfMatrix4d>()) {
return convertMatrixToCycles<GfMatrix4d>(value.UncheckedGet<GfMatrix4d>());
}
if (value.IsHolding<GfMatrix3d>()) {
return convertMatrixToCycles<GfMatrix3d>(value.UncheckedGet<GfMatrix3d>());
}
if (value.CanCast<GfMatrix4f>()) {
return convertToCycles<Transform>(VtValue::Cast<GfMatrix4f>(value));
}
if (value.CanCast<GfMatrix3f>()) {
return convertToCycles<Transform>(VtValue::Cast<GfMatrix3f>(value));
}
if (value.CanCast<GfMatrix4d>()) {
return convertToCycles<Transform>(VtValue::Cast<GfMatrix4d>(value));
}
if (value.CanCast<GfMatrix3d>()) {
return convertToCycles<Transform>(VtValue::Cast<GfMatrix3d>(value));
}
TF_WARN("Could not convert VtValue to Transform");
return transform_identity();
}
template<typename DstType, typename SrcType = DstType>
array<DstType> convertToCyclesArray(const VtValue &value)
{
static_assert(sizeof(DstType) == sizeof(SrcType),
"Size mismatch between VtArray and array base type");
using SrcArray = VtArray<SrcType>;
if (value.IsHolding<SrcArray>()) {
const auto &valueData = value.UncheckedGet<SrcArray>();
array<DstType> cyclesArray;
cyclesArray.resize(valueData.size());
std::memcpy(cyclesArray.data(), valueData.data(), valueData.size() * sizeof(DstType));
return cyclesArray;
}
if (value.CanCast<SrcArray>()) {
const VtValue castedValue = VtValue::Cast<SrcArray>(value);
const auto &valueData = castedValue.UncheckedGet<SrcArray>();
array<DstType> cyclesArray;
cyclesArray.resize(valueData.size());
std::memcpy(cyclesArray.data(), valueData.data(), valueData.size() * sizeof(DstType));
return cyclesArray;
}
return array<DstType>();
}
template<> array<packed_float3> convertToCyclesArray<packed_float3, GfVec3f>(const VtValue &value)
{
if (value.IsHolding<VtVec3fArray>()) {
const auto &valueData = value.UncheckedGet<VtVec3fArray>();
array<packed_float3> cyclesArray;
cyclesArray.reserve(valueData.size());
for (const GfVec3f &vec : valueData) {
cyclesArray.push_back_reserved(make_float3(vec[0], vec[1], vec[2]));
}
return cyclesArray;
}
if (value.IsHolding<VtVec4fArray>()) {
const auto &valueData = value.UncheckedGet<VtVec4fArray>();
array<packed_float3> cyclesArray;
cyclesArray.reserve(valueData.size());
for (const GfVec4f &vec : valueData) {
cyclesArray.push_back_reserved(make_float3(vec[0], vec[1], vec[2]));
}
return cyclesArray;
}
if (value.CanCast<VtVec3fArray>()) {
return convertToCyclesArray<packed_float3, GfVec3f>(VtValue::Cast<VtVec3fArray>(value));
}
if (value.CanCast<VtVec4fArray>()) {
return convertToCyclesArray<packed_float3, GfVec3f>(VtValue::Cast<VtVec4fArray>(value));
}
return array<packed_float3>();
}
template<> array<ustring> convertToCyclesArray<ustring, void>(const VtValue &value)
{
using SdfPathArray = VtArray<SdfAssetPath>;
if (value.IsHolding<VtStringArray>()) {
const auto &valueData = value.UncheckedGet<VtStringArray>();
array<ustring> cyclesArray;
cyclesArray.reserve(valueData.size());
for (const auto &element : valueData) {
cyclesArray.push_back_reserved(ustring(element));
}
return cyclesArray;
}
if (value.IsHolding<VtTokenArray>()) {
const auto &valueData = value.UncheckedGet<VtTokenArray>();
array<ustring> cyclesArray;
cyclesArray.reserve(valueData.size());
for (const auto &element : valueData) {
cyclesArray.push_back_reserved(ustring(element.GetString()));
}
return cyclesArray;
}
if (value.IsHolding<SdfPathArray>()) {
const auto &valueData = value.UncheckedGet<SdfPathArray>();
array<ustring> cyclesArray;
cyclesArray.reserve(valueData.size());
for (const auto &element : valueData) {
cyclesArray.push_back_reserved(ustring(element.GetResolvedPath()));
}
return cyclesArray;
}
if (value.CanCast<VtStringArray>()) {
return convertToCyclesArray<ustring, void>(VtValue::Cast<VtStringArray>(value));
}
if (value.CanCast<VtTokenArray>()) {
return convertToCyclesArray<ustring, void>(VtValue::Cast<VtTokenArray>(value));
}
if (value.CanCast<SdfPathArray>()) {
return convertToCyclesArray<ustring, void>(VtValue::Cast<SdfPathArray>(value));
}
TF_WARN("Could not convert VtValue to array<ustring>");
return array<ustring>();
}
template<typename MatrixArray> array<Transform> convertToCyclesTransformArray(const VtValue &value)
{
assert(value.IsHolding<MatrixArray>());
const auto &valueData = value.UncheckedGet<MatrixArray>();
array<Transform> cyclesArray;
cyclesArray.reserve(valueData.size());
for (const auto &element : valueData) {
cyclesArray.push_back_reserved(
convertMatrixToCycles<typename MatrixArray::value_type>(element));
}
return cyclesArray;
}
template<> array<Transform> convertToCyclesArray<Transform, void>(const VtValue &value)
{
if (value.IsHolding<VtMatrix4fArray>()) {
return convertToCyclesTransformArray<VtMatrix4fArray>(value);
}
if (value.IsHolding<VtMatrix3fArray>()) {
return convertToCyclesTransformArray<VtMatrix3fArray>(value);
}
if (value.IsHolding<VtMatrix4dArray>()) {
return convertToCyclesTransformArray<VtMatrix4dArray>(value);
}
if (value.IsHolding<VtMatrix3dArray>()) {
return convertToCyclesTransformArray<VtMatrix3dArray>(value);
}
if (value.CanCast<VtMatrix4fArray>()) {
return convertToCyclesTransformArray<VtMatrix4fArray>(VtValue::Cast<VtMatrix4fArray>(value));
}
if (value.CanCast<VtMatrix3fArray>()) {
return convertToCyclesTransformArray<VtMatrix3fArray>(VtValue::Cast<VtMatrix3fArray>(value));
}
if (value.CanCast<VtMatrix4dArray>()) {
return convertToCyclesTransformArray<VtMatrix4dArray>(VtValue::Cast<VtMatrix4dArray>(value));
}
if (value.CanCast<VtMatrix3dArray>()) {
return convertToCyclesTransformArray<VtMatrix3dArray>(VtValue::Cast<VtMatrix3dArray>(value));
}
TF_WARN("Could not convert VtValue to array<Transform>");
return array<Transform>();
}
template<typename SrcType> VtValue convertFromCycles(const SrcType &value)
{
return VtValue(value);
}
template<> VtValue convertFromCycles<float2>(const float2 &value)
{
const GfVec2f convertedValue(value.x, value.y);
return VtValue(convertedValue);
}
template<> VtValue convertFromCycles<float3>(const float3 &value)
{
const GfVec3f convertedValue(value.x, value.y, value.z);
return VtValue(convertedValue);
}
template<> VtValue convertFromCycles<ustring>(const ustring &value)
{
return VtValue(value.string());
}
GfMatrix4f convertMatrixFromCycles(const Transform &matrix)
{
return GfMatrix4f(matrix[0][0],
matrix[1][0],
matrix[2][0],
0.0f,
matrix[0][1],
matrix[1][1],
matrix[2][1],
0.0f,
matrix[0][2],
matrix[1][2],
matrix[2][2],
0.0f,
0.0f,
0.0f,
0.0f,
1.0f);
}
template<> VtValue convertFromCycles<Transform>(const Transform &value)
{
return VtValue(convertMatrixFromCycles(value));
}
template<typename SrcType, typename DstType = SrcType>
VtValue convertFromCyclesArray(const array<SrcType> &value)
{
static_assert(sizeof(DstType) == sizeof(SrcType),
"Size mismatch between VtArray and array base type");
VtArray<DstType> convertedValue;
convertedValue.resize(value.size());
std::memcpy(convertedValue.data(), value.data(), value.size() * sizeof(SrcType));
return VtValue(convertedValue);
}
template<> VtValue convertFromCyclesArray<float2, GfVec2f>(const array<float2> &value)
{
VtVec2fArray convertedValue;
convertedValue.reserve(value.size());
for (const auto &element : value) {
convertedValue.push_back(GfVec2f(element.x, element.y));
}
return VtValue(convertedValue);
}
template<>
VtValue convertFromCyclesArray<packed_float3, GfVec3f>(const array<packed_float3> &value)
{
VtVec3fArray convertedValue;
convertedValue.reserve(value.size());
for (const auto &element : value) {
convertedValue.push_back(GfVec3f(element.x, element.y, element.z));
}
return VtValue(convertedValue);
}
template<> VtValue convertFromCyclesArray<ustring, void>(const array<ustring> &value)
{
VtStringArray convertedValue;
convertedValue.reserve(value.size());
for (const auto &element : value) {
convertedValue.push_back(element.string());
}
return VtValue(convertedValue);
}
template<> VtValue convertFromCyclesArray<Transform, void>(const array<Transform> &value)
{
VtMatrix4fArray convertedValue;
convertedValue.reserve(value.size());
for (const auto &element : value) {
convertedValue.push_back(convertMatrixFromCycles(element));
}
return VtValue(convertedValue);
}
} // namespace
void SetNodeValue(Node *node, const SocketType &socket, const VtValue &value)
{
switch (socket.type) {
default:
case SocketType::UNDEFINED:
TF_RUNTIME_ERROR("Unexpected conversion: SocketType::UNDEFINED");
break;
case SocketType::BOOLEAN:
node->set(socket, convertToCycles<bool>(value));
break;
case SocketType::FLOAT:
node->set(socket, convertToCycles<float>(value));
break;
case SocketType::INT:
node->set(socket, convertToCycles<int>(value));
break;
case SocketType::UINT:
node->set(socket, convertToCycles<unsigned int>(value));
break;
case SocketType::COLOR:
case SocketType::VECTOR:
case SocketType::POINT:
case SocketType::NORMAL:
node->set(socket, convertToCycles<float3>(value));
break;
case SocketType::POINT2:
node->set(socket, convertToCycles<float2>(value));
break;
case SocketType::CLOSURE:
// Handled by node connections
break;
case SocketType::STRING:
node->set(socket, convertToCycles<ustring>(value));
break;
case SocketType::ENUM:
// Enum's can accept a string or an int
if (value.IsHolding<TfToken>() || value.IsHolding<std::string>()) {
node->set(socket, convertToCycles<ustring>(value));
}
else {
node->set(socket, convertToCycles<int>(value));
}
break;
case SocketType::TRANSFORM:
node->set(socket, convertToCycles<Transform>(value));
break;
case SocketType::NODE:
// TODO: renderIndex->GetRprim()->cycles_node ?
TF_WARN("Unimplemented conversion: SocketType::NODE");
break;
case SocketType::BOOLEAN_ARRAY: {
auto cyclesArray = convertToCyclesArray<bool>(value);
node->set(socket, cyclesArray);
break;
}
case SocketType::FLOAT_ARRAY: {
auto cyclesArray = convertToCyclesArray<float>(value);
node->set(socket, cyclesArray);
break;
}
case SocketType::INT_ARRAY: {
auto cyclesArray = convertToCyclesArray<int>(value);
node->set(socket, cyclesArray);
break;
}
case SocketType::COLOR_ARRAY:
case SocketType::VECTOR_ARRAY:
case SocketType::POINT_ARRAY:
case SocketType::NORMAL_ARRAY: {
auto cyclesArray = convertToCyclesArray<packed_float3, GfVec3f>(value);
node->set(socket, cyclesArray);
break;
}
case SocketType::POINT2_ARRAY: {
auto cyclesArray = convertToCyclesArray<float2, GfVec2f>(value);
node->set(socket, cyclesArray);
break;
}
case SocketType::STRING_ARRAY: {
auto cyclesArray = convertToCyclesArray<ustring, void>(value);
node->set(socket, cyclesArray);
break;
}
case SocketType::TRANSFORM_ARRAY: {
auto cyclesArray = convertToCyclesArray<Transform, void>(value);
node->set(socket, cyclesArray);
break;
}
case SocketType::NODE_ARRAY: {
// TODO: renderIndex->GetRprim()->cycles_node ?
TF_WARN("Unimplemented conversion: SocketType::NODE_ARRAY");
break;
}
}
}
VtValue GetNodeValue(const Node *node, const SocketType &socket)
{
switch (socket.type) {
default:
case SocketType::UNDEFINED:
TF_RUNTIME_ERROR("Unexpected conversion: SocketType::UNDEFINED");
return VtValue();
case SocketType::BOOLEAN:
return convertFromCycles(node->get_bool(socket));
case SocketType::FLOAT:
return convertFromCycles(node->get_float(socket));
case SocketType::INT:
return convertFromCycles(node->get_int(socket));
case SocketType::UINT:
return convertFromCycles(node->get_uint(socket));
case SocketType::COLOR:
case SocketType::VECTOR:
case SocketType::POINT:
case SocketType::NORMAL:
return convertFromCycles(node->get_float3(socket));
case SocketType::POINT2:
return convertFromCycles(node->get_float2(socket));
case SocketType::CLOSURE:
return VtValue();
case SocketType::STRING:
return convertFromCycles(node->get_string(socket));
case SocketType::ENUM:
return convertFromCycles(node->get_int(socket));
case SocketType::TRANSFORM:
return convertFromCycles(node->get_transform(socket));
case SocketType::NODE:
TF_WARN("Unimplemented conversion: SocketType::NODE");
return VtValue();
case SocketType::BOOLEAN_ARRAY:
return convertFromCyclesArray(node->get_bool_array(socket));
case SocketType::FLOAT_ARRAY:
return convertFromCyclesArray(node->get_float_array(socket));
case SocketType::INT_ARRAY:
return convertFromCyclesArray(node->get_int_array(socket));
case SocketType::COLOR_ARRAY:
case SocketType::VECTOR_ARRAY:
case SocketType::POINT_ARRAY:
case SocketType::NORMAL_ARRAY:
return convertFromCyclesArray<packed_float3, GfVec3f>(node->get_float3_array(socket));
case SocketType::POINT2_ARRAY:
return convertFromCyclesArray<float2, GfVec2f>(node->get_float2_array(socket));
case SocketType::STRING_ARRAY:
return convertFromCyclesArray<ustring, void>(node->get_string_array(socket));
case SocketType::TRANSFORM_ARRAY:
return convertFromCyclesArray<Transform, void>(node->get_transform_array(socket));
case SocketType::NODE_ARRAY: {
TF_WARN("Unimplemented conversion: SocketType::NODE_ARRAY");
return VtValue();
}
}
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,19 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "graph/node.h"
#include "hydra/config.h"
#include <pxr/base/vt/value.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
void SetNodeValue(CCL_NS::Node *node, const CCL_NS::SocketType &socket, const VtValue &value);
VtValue GetNodeValue(const CCL_NS::Node *node, const CCL_NS::SocketType &socket);
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,81 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "hydra/output_driver.h"
#include "hydra/render_buffer.h"
#include "hydra/session.h"
HDCYCLES_NAMESPACE_OPEN_SCOPE
HdCyclesOutputDriver::HdCyclesOutputDriver(HdCyclesSession *renderParam)
: _renderParam(renderParam)
{
}
void HdCyclesOutputDriver::write_render_tile(const Tile &tile)
{
update_render_tile(tile);
// Update convergence state of all render buffers
for (const HdRenderPassAovBinding &aovBinding : _renderParam->GetAovBindings()) {
if (auto *const renderBuffer = static_cast<HdCyclesRenderBuffer *>(aovBinding.renderBuffer)) {
renderBuffer->SetConverged(true);
}
}
}
bool HdCyclesOutputDriver::update_render_tile(const Tile &tile)
{
std::vector<float> pixels;
for (const HdRenderPassAovBinding &aovBinding : _renderParam->GetAovBindings()) {
if (auto *const renderBuffer = static_cast<HdCyclesRenderBuffer *>(aovBinding.renderBuffer)) {
if (aovBinding == _renderParam->GetDisplayAovBinding() && renderBuffer->IsResourceUsed()) {
continue; // Display AOV binding is already updated by Cycles display driver
}
const HdFormat format = renderBuffer->GetFormat();
if (format == HdFormatInvalid) {
continue; // Skip invalid AOV bindings
}
const size_t channels = HdGetComponentCount(format);
// Avoid extra copy by mapping render buffer directly when dimensions/format match the tile
if (tile.offset.x == 0 && tile.offset.y == 0 && tile.size.x == renderBuffer->GetWidth() &&
tile.size.y == renderBuffer->GetHeight() &&
(format >= HdFormatFloat32 && format <= HdFormatFloat32Vec4))
{
float *const data = static_cast<float *>(renderBuffer->Map());
TF_VERIFY(tile.get_pass_pixels(aovBinding.aovName.GetString(), channels, data));
renderBuffer->Unmap();
}
else {
pixels.resize(channels * tile.size.x * tile.size.y);
if (tile.get_pass_pixels(aovBinding.aovName.GetString(), channels, pixels.data())) {
const bool isId = aovBinding.aovName == HdAovTokens->primId ||
aovBinding.aovName == HdAovTokens->elementId ||
aovBinding.aovName == HdAovTokens->instanceId;
renderBuffer->Map();
renderBuffer->WritePixels(pixels.data(),
GfVec2i(tile.offset.x, tile.offset.y),
GfVec2i(tile.size.x, tile.size.y),
channels,
isId);
renderBuffer->Unmap();
}
else {
// Do not warn on missing elementId, which is a standard AOV but is not implememted
if (aovBinding.aovName != HdAovTokens->elementId) {
TF_RUNTIME_ERROR("Could not find pass for AOV '%s'", aovBinding.aovName.GetText());
}
}
}
}
}
return true;
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,24 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "hydra/config.h"
#include "session/output_driver.h"
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesOutputDriver final : public CCL_NS::OutputDriver {
public:
HdCyclesOutputDriver(HdCyclesSession *renderParam);
private:
void write_render_tile(const Tile &tile) override;
bool update_render_tile(const Tile &tile) override;
HdCyclesSession *const _renderParam;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,3 @@
{
"Includes": [ "*/resources/" ]
}

View File

@@ -0,0 +1,79 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "hydra/plugin.h"
#include "hydra/render_delegate.h"
#include "util/log.h"
#include "util/path.h"
#include <pxr/base/arch/fileSystem.h>
#include <pxr/base/plug/plugin.h>
#include <pxr/base/plug/thisPlugin.h>
#include <pxr/base/tf/envSetting.h>
#include <pxr/imaging/hd/rendererPluginRegistry.h>
PXR_NAMESPACE_OPEN_SCOPE
TF_DEFINE_ENV_SETTING(CYCLES_LOGGING, false, "Enable Cycles logging")
TF_DEFINE_ENV_SETTING(CYCLES_LOGGING_LEVEL, "warning", "Cycles logging level")
HdCyclesPlugin::HdCyclesPlugin()
{
const PlugPluginPtr plugin = PLUG_THIS_PLUGIN;
// Initialize Cycles paths relative to the plugin resource path
const std::string rootPath = PXR_NS::ArchAbsPath(plugin->GetResourcePath());
CCL_NS::path_init(std::move(rootPath));
if (TfGetEnvSetting(CYCLES_LOGGING)) {
CCL_NS::log_level_set(TfGetEnvSetting(CYCLES_LOGGING_LEVEL));
}
}
HdCyclesPlugin::~HdCyclesPlugin() {}
#if PXR_VERSION < 2302
bool HdCyclesPlugin::IsSupported() const
{
return true;
}
#else
# if PXR_VERSION >= 2511
bool HdCyclesPlugin::IsSupported(HdRendererCreateArgs const & /*rendererCreateArgs*/,
std::string * /*reasonWhyNot*/) const
{
return true;
}
# endif
bool HdCyclesPlugin::IsSupported(bool /*gpuEnabled*/) const
{
return true;
}
#endif
HdRenderDelegate *HdCyclesPlugin::CreateRenderDelegate()
{
return CreateRenderDelegate({});
}
HdRenderDelegate *HdCyclesPlugin::CreateRenderDelegate(const HdRenderSettingsMap &settingsMap)
{
return new HD_CYCLES_NS::HdCyclesDelegate(settingsMap);
}
void HdCyclesPlugin::DeleteRenderDelegate(HdRenderDelegate *renderDelegate)
{
delete renderDelegate;
}
// USD's type system accounts for namespace, so we'd have to register our name as
// HdCycles::HdCyclesPlugin in plugInfo.json, which isn't all that bad for JSON,
// but those colons may cause issues for any USD specific tooling. So just put our
// plugin class in the pxr namespace (which USD's type system will elide).
TF_REGISTRY_FUNCTION(TfType)
{
HdRendererPluginRegistry::Define<PXR_NS::HdCyclesPlugin>();
}
PXR_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,31 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "hydra/config.h"
#include <pxr/imaging/hd/rendererPlugin.h>
PXR_NAMESPACE_OPEN_SCOPE
class HdCyclesPlugin final : public PXR_NS::HdRendererPlugin {
public:
HdCyclesPlugin();
~HdCyclesPlugin() override;
#if PXR_VERSION >= 2511
bool IsSupported(HdRendererCreateArgs const &rendererCreateArgs,
std::string *reasonWhyNot = nullptr) const override;
#endif
bool IsSupported(bool gpuEnabled) const override;
PXR_NS::HdRenderDelegate *CreateRenderDelegate() override;
PXR_NS::HdRenderDelegate *CreateRenderDelegate(
const PXR_NS::HdRenderSettingsMap & /*settingsMap*/) override;
void DeleteRenderDelegate(PXR_NS::HdRenderDelegate *) override;
};
PXR_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,171 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "hydra/pointcloud.h"
#include "hydra/geometry.inl"
#include "hydra/util.h"
#include "scene/pointcloud.h"
HDCYCLES_NAMESPACE_OPEN_SCOPE
HdCyclesPoints::HdCyclesPoints(const SdfPath &rprimId) : HdCyclesGeometry(rprimId) {}
HdCyclesPoints::~HdCyclesPoints() = default;
HdDirtyBits HdCyclesPoints::GetInitialDirtyBitsMask() const
{
HdDirtyBits bits = HdCyclesGeometry::GetInitialDirtyBitsMask();
bits |= HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyWidths |
HdChangeTracker::DirtyPrimvar;
return bits;
}
HdDirtyBits HdCyclesPoints::_PropagateDirtyBits(HdDirtyBits bits) const
{
// Points and widths always have to be updated together
if (bits & (HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyWidths)) {
bits |= HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyWidths;
}
return bits;
}
void HdCyclesPoints::Populate(HdSceneDelegate *sceneDelegate, HdDirtyBits dirtyBits, bool &rebuild)
{
if (dirtyBits & (HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyWidths)) {
const size_t numPoints = _geom->num_points();
PopulatePoints(sceneDelegate);
PopulateWidths(sceneDelegate);
rebuild = _geom->num_points() != numPoints;
array<int> shaders;
shaders.reserve(_geom->num_points());
for (size_t i = 0; i < _geom->num_points(); ++i) {
shaders.push_back_reserved(0);
}
_geom->set_shader(shaders);
}
if (dirtyBits & HdChangeTracker::DirtyPrimvar) {
PopulatePrimvars(sceneDelegate);
}
}
void HdCyclesPoints::PopulatePoints(HdSceneDelegate *sceneDelegate)
{
const HdSceneIndexPrim prim = GetPrim(sceneDelegate, GetId());
const HdPrimvarsSchema primvars = HdPrimvarsSchema::GetFromParent(prim.dataSource);
const VtValue value = ReadPrimvar(primvars, HdTokens->points);
if (!value.IsHolding<VtVec3fArray>()) {
TF_WARN("Invalid points data for %s", GetId().GetText());
return;
}
const auto &points = value.UncheckedGet<VtVec3fArray>();
static_assert(sizeof(GfVec3f) == sizeof(packed_float3));
_geom->resize(int(points.size()));
std::copy_n(reinterpret_cast<const packed_float3 *>(points.data()),
points.size(),
_geom->get_position_for_write());
}
void HdCyclesPoints::PopulateWidths(HdSceneDelegate *sceneDelegate)
{
const HdSceneIndexPrim prim = GetPrim(sceneDelegate, GetId());
const HdPrimvarsSchema primvars = HdPrimvarsSchema::GetFromParent(prim.dataSource);
const VtValue value = ReadPrimvar(primvars, HdTokens->widths);
const HdInterpolation interpolation = ReadPrimvarInterpolation(primvars, HdTokens->widths);
if (!value.IsHolding<VtFloatArray>()) {
TF_WARN("Invalid widths data for %s", GetId().GetText());
return;
}
const auto &widths = value.UncheckedGet<VtFloatArray>();
float *radius = _geom->get_radius_for_write();
if (interpolation == HdInterpolationConstant) {
TF_VERIFY(widths.size() == 1);
const float constantRadius = widths[0] * 0.5f;
for (size_t i = 0; i < _geom->num_points(); ++i) {
radius[i] = constantRadius;
}
}
else if (interpolation == HdInterpolationVertex) {
TF_VERIFY(widths.size() == _geom->num_points());
for (size_t i = 0; i < _geom->num_points(); ++i) {
radius[i] = widths[i] * 0.5f;
}
}
}
void HdCyclesPoints::PopulatePrimvars(HdSceneDelegate *sceneDelegate)
{
Scene *const scene = (Scene *)_geom->get_owner();
const HdSceneIndexPrim prim = GetPrim(sceneDelegate, GetId());
const HdPrimvarsSchema primvars = HdPrimvarsSchema::GetFromParent(prim.dataSource);
const std::pair<HdInterpolation, AttributeElement> interpolations[] = {
std::make_pair(HdInterpolationVertex, ATTR_ELEMENT_VERTEX),
std::make_pair(HdInterpolationConstant, ATTR_ELEMENT_OBJECT),
};
for (const auto &interpolation : interpolations) {
for (const TfToken &primvarName : PrimvarNamesAtInterpolation(primvars, interpolation.first)) {
// Skip special primvars that are handled separately
if (primvarName == HdTokens->points || primvarName == HdTokens->widths) {
continue;
}
const VtValue value = ReadPrimvar(primvars, primvarName);
if (value.IsEmpty()) {
continue;
}
const TfToken role = ReadPrimvarRole(primvars, primvarName);
const ustring name(primvarName.GetString());
AttributeStandard std = ATTR_STD_NONE;
if (role == HdPrimvarRoleTokens->textureCoordinate) {
std = ATTR_STD_UV;
}
else if (interpolation.first == HdInterpolationVertex) {
if (primvarName == HdTokens->displayColor || role == HdPrimvarRoleTokens->color) {
std = ATTR_STD_VERTEX_COLOR;
}
else if (primvarName == HdTokens->normals) {
std = ATTR_STD_VERTEX_NORMAL;
}
}
else if (primvarName == HdTokens->displayColor &&
interpolation.first == HdInterpolationConstant)
{
if (value.IsHolding<VtVec3fArray>() && value.GetArraySize() == 1) {
const GfVec3f color = value.UncheckedGet<VtVec3fArray>()[0];
_instances[0]->set_color(make_float3(color[0], color[1], color[2]));
}
}
// Skip attributes that are not needed
if ((std != ATTR_STD_NONE && _geom->need_attribute(scene, std)) ||
_geom->need_attribute(scene, name))
{
ApplyPrimvars(_geom->attributes, name, value, interpolation.second, std);
}
}
}
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,35 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "hydra/config.h"
#include "hydra/geometry.h"
#include <pxr/imaging/hd/points.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesPoints final : public HdCyclesGeometry<PXR_NS::HdPoints, CCL_NS::PointCloud> {
public:
HdCyclesPoints(const PXR_NS::SdfPath &rprimId);
~HdCyclesPoints() override;
PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override;
private:
PXR_NS::HdDirtyBits _PropagateDirtyBits(PXR_NS::HdDirtyBits bits) const override;
void Populate(PXR_NS::HdSceneDelegate *sceneDelegate,
PXR_NS::HdDirtyBits dirtyBits,
bool &rebuild) override;
void PopulatePoints(PXR_NS::HdSceneDelegate *sceneDelegate);
void PopulateWidths(PXR_NS::HdSceneDelegate *sceneDelegate);
void PopulatePrimvars(PXR_NS::HdSceneDelegate *sceneDelegate);
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,285 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "hydra/render_buffer.h"
#include "hydra/session.h"
#include "util/half.h"
#include <pxr/base/gf/vec3i.h>
#include <pxr/base/gf/vec4f.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
HdCyclesRenderBuffer::HdCyclesRenderBuffer(const SdfPath &bprimId) : HdRenderBuffer(bprimId) {}
HdCyclesRenderBuffer::~HdCyclesRenderBuffer() = default;
void HdCyclesRenderBuffer::Finalize(HdRenderParam *renderParam)
{
// Remove this render buffer from AOV bindings
// This ensures that 'OutputDriver' does not attempt to write to it anymore
static_cast<HdCyclesSession *>(renderParam)->RemoveAovBinding(this);
HdRenderBuffer::Finalize(renderParam);
}
bool HdCyclesRenderBuffer::Allocate(const GfVec3i &dimensions,
HdFormat format,
bool /*multiSampled*/)
{
if (dimensions[2] != 1) {
TF_RUNTIME_ERROR("HdCyclesRenderBuffer::Allocate called with dimensions that are not 2D.");
return false;
}
const size_t oldSize = _dataSize;
const size_t newSize = dimensions[0] * dimensions[1] * HdDataSizeOfFormat(format);
if (oldSize == newSize) {
return true;
}
if (IsMapped()) {
TF_RUNTIME_ERROR("HdCyclesRenderBuffer::Allocate called while buffer is mapped.");
return false;
}
_width = dimensions[0];
_height = dimensions[1];
_format = format;
_dataSize = newSize;
_resourceUsed = false;
return true;
}
void HdCyclesRenderBuffer::_Deallocate()
{
_width = 0u;
_height = 0u;
_format = HdFormatInvalid;
_data.clear();
_data.shrink_to_fit();
_dataSize = 0;
_resource = VtValue();
}
void *HdCyclesRenderBuffer::Map()
{
// Mapping is not implemented when a resource is set
if (!_resource.IsEmpty()) {
return nullptr;
}
if (_data.size() != _dataSize) {
_data.resize(_dataSize);
}
++_mapped;
return _data.data();
}
void HdCyclesRenderBuffer::Unmap()
{
--_mapped;
}
bool HdCyclesRenderBuffer::IsMapped() const
{
return _mapped != 0;
}
void HdCyclesRenderBuffer::Resolve() {}
bool HdCyclesRenderBuffer::IsConverged() const
{
return _converged;
}
void HdCyclesRenderBuffer::SetConverged(bool converged)
{
_converged = converged;
}
bool HdCyclesRenderBuffer::IsResourceUsed() const
{
return _resourceUsed;
}
VtValue HdCyclesRenderBuffer::GetResource(bool multiSampled) const
{
TF_UNUSED(multiSampled);
_resourceUsed = true;
return _resource;
}
void HdCyclesRenderBuffer::SetResource(const VtValue &resource)
{
_resource = resource;
}
namespace {
struct SimpleConversion {
static float convert(const float value)
{
return value;
}
};
struct IdConversion {
static int32_t convert(const float value)
{
return static_cast<int32_t>(value) - 1;
}
};
struct UInt8Conversion {
static uint8_t convert(const float value)
{
return static_cast<uint8_t>(value * 255.f);
}
};
struct SInt8Conversion {
static int8_t convert(const float value)
{
return static_cast<int8_t>(value * 127.f);
}
};
struct HalfConversion {
static half convert(const float value)
{
return float_to_half_image(value);
}
};
template<typename SrcT, typename DstT, typename Convertor = SimpleConversion>
void writePixels(const SrcT *srcPtr,
const GfVec2i &srcSize,
const int srcChannelCount,
DstT *dstPtr,
const GfVec2i &dstSize,
const int dstChannelCount,
const Convertor &convertor = {})
{
const auto writeSize = GfVec2i(GfMin(srcSize[0], dstSize[0]), GfMin(srcSize[1], dstSize[1]));
const auto writeChannelCount = GfMin(srcChannelCount, dstChannelCount);
for (int y = 0; y < writeSize[1]; ++y) {
for (int x = 0; x < writeSize[0]; ++x) {
for (int c = 0; c < writeChannelCount; ++c) {
dstPtr[x * dstChannelCount + c] = convertor.convert(srcPtr[x * srcChannelCount + c]);
}
}
srcPtr += srcSize[0] * srcChannelCount;
dstPtr += dstSize[0] * dstChannelCount;
}
}
} // namespace
void HdCyclesRenderBuffer::WritePixels(const float *srcPixels,
const PXR_NS::GfVec2i &srcOffset,
const GfVec2i &srcDims,
const int srcChannels,
bool isId)
{
uint8_t *dstPixels = _data.data();
const size_t formatSize = HdDataSizeOfFormat(_format);
dstPixels += srcOffset[1] * (formatSize * _width) + srcOffset[0] * formatSize;
switch (_format) {
case HdFormatUNorm8:
case HdFormatUNorm8Vec2:
case HdFormatUNorm8Vec3:
case HdFormatUNorm8Vec4:
writePixels(srcPixels,
srcDims,
srcChannels,
dstPixels,
GfVec2i(_width, _height),
1 + (_format - HdFormatUNorm8),
UInt8Conversion());
break;
case HdFormatSNorm8:
case HdFormatSNorm8Vec2:
case HdFormatSNorm8Vec3:
case HdFormatSNorm8Vec4:
writePixels(srcPixels,
srcDims,
srcChannels,
dstPixels,
GfVec2i(_width, _height),
1 + (_format - HdFormatSNorm8),
SInt8Conversion());
break;
case HdFormatFloat16:
case HdFormatFloat16Vec2:
case HdFormatFloat16Vec3:
case HdFormatFloat16Vec4:
writePixels(srcPixels,
srcDims,
srcChannels,
reinterpret_cast<half *>(dstPixels),
GfVec2i(_width, _height),
1 + (_format - HdFormatFloat16),
HalfConversion());
break;
case HdFormatFloat32:
case HdFormatFloat32Vec2:
case HdFormatFloat32Vec3:
case HdFormatFloat32Vec4:
writePixels(srcPixels,
srcDims,
srcChannels,
reinterpret_cast<float *>(dstPixels),
GfVec2i(_width, _height),
1 + (_format - HdFormatFloat32));
break;
case HdFormatInt32:
// Special case for ID AOVs (see 'HdCyclesMesh::Sync')
if (isId) {
writePixels(srcPixels,
srcDims,
srcChannels,
reinterpret_cast<int *>(dstPixels),
GfVec2i(_width, _height),
1,
IdConversion());
}
else {
writePixels(srcPixels,
srcDims,
srcChannels,
reinterpret_cast<int *>(dstPixels),
GfVec2i(_width, _height),
1);
}
break;
case HdFormatInt32Vec2:
case HdFormatInt32Vec3:
case HdFormatInt32Vec4:
writePixels(srcPixels,
srcDims,
srcChannels,
reinterpret_cast<int *>(dstPixels),
GfVec2i(_width, _height),
1 + (_format - HdFormatInt32));
break;
default:
TF_RUNTIME_ERROR("HdCyclesRenderBuffer::WritePixels called with unsupported format.");
break;
}
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,90 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "hydra/config.h"
#include <pxr/imaging/hd/renderBuffer.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesRenderBuffer final : public PXR_NS::HdRenderBuffer {
public:
HdCyclesRenderBuffer(const PXR_NS::SdfPath &bprimId);
~HdCyclesRenderBuffer() override;
void Finalize(PXR_NS::HdRenderParam *renderParam) override;
bool Allocate(const PXR_NS::GfVec3i &dimensions,
PXR_NS::HdFormat format,
bool multiSampled) override;
unsigned int GetWidth() const override
{
return _width;
}
unsigned int GetHeight() const override
{
return _height;
}
unsigned int GetDepth() const override
{
return 1u;
}
PXR_NS::HdFormat GetFormat() const override
{
return _format;
}
bool IsMultiSampled() const override
{
return false;
}
void *Map() override;
void Unmap() override;
bool IsMapped() const override;
void Resolve() override;
bool IsConverged() const override;
void SetConverged(bool converged);
bool IsResourceUsed() const;
PXR_NS::VtValue GetResource(bool multiSampled = false) const override;
void SetResource(const PXR_NS::VtValue &resource);
void WritePixels(const float *pixels,
const PXR_NS::GfVec2i &offset,
const PXR_NS::GfVec2i &dims,
const int channels,
bool isId = false);
private:
void _Deallocate() override;
unsigned int _width = 0u;
unsigned int _height = 0u;
PXR_NS::HdFormat _format = PXR_NS::HdFormatInvalid;
size_t _dataSize = 0;
std::vector<uint8_t> _data;
PXR_NS::VtValue _resource;
mutable std::atomic_bool _resourceUsed = false;
std::atomic_int _mapped = 0;
std::atomic_bool _converged = false;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,474 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "hydra/render_delegate.h"
#include "hydra/camera.h"
#include "hydra/curves.h"
#include "hydra/field.h"
#include "hydra/instancer.h"
#include "hydra/light.h"
#include "hydra/material.h"
#include "hydra/mesh.h"
#include "hydra/node_util.h"
#include "hydra/pointcloud.h"
#include "hydra/render_buffer.h"
#include "hydra/render_pass.h"
#include "hydra/session.h"
#include "hydra/volume.h"
#include "scene/integrator.h"
#include "scene/scene.h"
#include "session/session.h"
#include <pxr/base/tf/getenv.h>
#include <pxr/imaging/hgi/tokens.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
TF_DEFINE_PUBLIC_TOKENS(HdCyclesRenderSettingsTokens, HD_CYCLES_RENDER_SETTINGS_TOKENS);
// clang-format off
TF_DEFINE_PRIVATE_TOKENS(_tokens,
(cycles)
(openvdbAsset)
);
// clang-format on
namespace {
const TfTokenVector kSupportedRPrimTypes = {
HdPrimTypeTokens->basisCurves,
HdPrimTypeTokens->mesh,
HdPrimTypeTokens->points,
#ifdef WITH_OPENVDB
HdPrimTypeTokens->volume,
#endif
};
const TfTokenVector kSupportedSPrimTypes = {
HdPrimTypeTokens->camera,
HdPrimTypeTokens->material,
HdPrimTypeTokens->diskLight,
HdPrimTypeTokens->distantLight,
HdPrimTypeTokens->domeLight,
HdPrimTypeTokens->rectLight,
HdPrimTypeTokens->sphereLight,
};
const TfTokenVector kSupportedBPrimTypes = {
HdPrimTypeTokens->renderBuffer,
#ifdef WITH_OPENVDB
_tokens->openvdbAsset,
#endif
};
SessionParams GetSessionParams(const HdRenderSettingsMap &settings)
{
SessionParams params;
params.threads = 0;
params.background = false;
params.use_resolution_divider = false;
HdRenderSettingsMap::const_iterator it;
// Pull all setting that contribute to device creation first
it = settings.find(HdCyclesRenderSettingsTokens->threads);
if (it != settings.end()) {
params.threads = VtValue::Cast<int>(it->second).GetWithDefault(params.threads);
}
// Get the Cycles device from settings or environment, falling back to CPU
std::string deviceType = Device::string_from_type(DEVICE_CPU);
it = settings.find(HdCyclesRenderSettingsTokens->device);
if (it != settings.end()) {
deviceType = VtValue::Cast<std::string>(it->second).GetWithDefault(deviceType);
}
else {
const std::string deviceTypeEnv = TfGetenv("CYCLES_DEVICE");
if (!deviceTypeEnv.empty()) {
deviceType = deviceTypeEnv;
}
}
// Move to all uppercase for Device::type_from_string
std::transform(deviceType.begin(), deviceType.end(), deviceType.begin(), ::toupper);
vector<DeviceInfo> devices = Device::available_devices(
DEVICE_MASK(Device::type_from_string(deviceType.c_str())));
if (devices.empty()) {
devices = Device::available_devices(DEVICE_MASK_CPU);
if (!devices.empty()) {
params.device = devices.front();
}
}
else {
params.device = Device::get_multi_device(devices, params.threads, params.background);
}
/* Set same device for denoising for now. */
params.denoise_device = params.device;
return params;
}
} // namespace
HdCyclesDelegate::HdCyclesDelegate(const HdRenderSettingsMap &settingsMap,
Session *session_,
const bool keep_nodes)
: HdRenderDelegate()
{
_renderParam = session_ ? std::make_unique<HdCyclesSession>(session_, keep_nodes) :
std::make_unique<HdCyclesSession>(GetSessionParams(settingsMap));
for (const auto &setting : settingsMap) {
// Skip over the settings known to be used for initialization only
if (setting.first == HdCyclesRenderSettingsTokens->device ||
setting.first == HdCyclesRenderSettingsTokens->threads)
{
continue;
}
SetRenderSetting(setting.first, setting.second);
}
}
HdCyclesDelegate::~HdCyclesDelegate() = default;
void HdCyclesDelegate::SetDrivers(const HdDriverVector &drivers)
{
for (HdDriver *hdDriver : drivers) {
if (hdDriver->name == HgiTokens->renderDriver && hdDriver->driver.IsHolding<Hgi *>()) {
_hgi = hdDriver->driver.UncheckedGet<Hgi *>();
break;
}
}
}
bool HdCyclesDelegate::IsDisplaySupported() const
{
#if defined(_WIN32) && defined(WITH_HYDRA_DISPLAY_DRIVER)
return _hgi && _hgi->GetAPIName() == HgiTokens->OpenGL;
#else
return false;
#endif
}
const TfTokenVector &HdCyclesDelegate::GetSupportedRprimTypes() const
{
return kSupportedRPrimTypes;
}
const TfTokenVector &HdCyclesDelegate::GetSupportedSprimTypes() const
{
return kSupportedSPrimTypes;
}
const TfTokenVector &HdCyclesDelegate::GetSupportedBprimTypes() const
{
return kSupportedBPrimTypes;
}
HdRenderParam *HdCyclesDelegate::GetRenderParam() const
{
return _renderParam.get();
}
HdResourceRegistrySharedPtr HdCyclesDelegate::GetResourceRegistry() const
{
return HdResourceRegistrySharedPtr();
}
bool HdCyclesDelegate::IsPauseSupported() const
{
return true;
}
bool HdCyclesDelegate::Pause()
{
_renderParam->session->set_pause(true);
return true;
}
bool HdCyclesDelegate::Resume()
{
_renderParam->session->set_pause(false);
return true;
}
HdRenderPassSharedPtr HdCyclesDelegate::CreateRenderPass(HdRenderIndex *index,
const HdRprimCollection &collection)
{
return HdRenderPassSharedPtr(new HdCyclesRenderPass(index, collection, _renderParam.get()));
}
HdInstancer *HdCyclesDelegate::CreateInstancer(HdSceneDelegate *delegate,
const SdfPath &instancerId)
{
return new HdCyclesInstancer(delegate, instancerId);
}
void HdCyclesDelegate::DestroyInstancer(HdInstancer *instancer)
{
delete instancer;
}
HdRprim *HdCyclesDelegate::CreateRprim(const TfToken &typeId, const SdfPath &rprimId)
{
if (typeId == HdPrimTypeTokens->mesh) {
return new HdCyclesMesh(rprimId);
}
if (typeId == HdPrimTypeTokens->basisCurves) {
return new HdCyclesCurves(rprimId);
}
if (typeId == HdPrimTypeTokens->points) {
return new HdCyclesPoints(rprimId);
}
#ifdef WITH_OPENVDB
if (typeId == HdPrimTypeTokens->volume) {
return new HdCyclesVolume(rprimId);
}
#endif
TF_CODING_ERROR("Unknown Rprim type %s", typeId.GetText());
return nullptr;
}
void HdCyclesDelegate::DestroyRprim(HdRprim *rPrim)
{
delete rPrim;
}
HdSprim *HdCyclesDelegate::CreateSprim(const TfToken &typeId, const SdfPath &sprimId)
{
if (typeId == HdPrimTypeTokens->camera) {
return new HdCyclesCamera(sprimId);
}
if (typeId == HdPrimTypeTokens->material) {
return new HdCyclesMaterial(sprimId);
}
if (typeId == HdPrimTypeTokens->diskLight || typeId == HdPrimTypeTokens->distantLight ||
typeId == HdPrimTypeTokens->domeLight || typeId == HdPrimTypeTokens->rectLight ||
typeId == HdPrimTypeTokens->sphereLight)
{
return new HdCyclesLight(sprimId, typeId);
}
TF_CODING_ERROR("Unknown Sprim type %s", typeId.GetText());
return nullptr;
}
HdSprim *HdCyclesDelegate::CreateFallbackSprim(const TfToken &typeId)
{
return CreateSprim(typeId, SdfPath::EmptyPath());
}
void HdCyclesDelegate::DestroySprim(HdSprim *sPrim)
{
delete sPrim;
}
HdBprim *HdCyclesDelegate::CreateBprim(const TfToken &typeId, const SdfPath &bprimId)
{
if (typeId == HdPrimTypeTokens->renderBuffer) {
return new HdCyclesRenderBuffer(bprimId);
}
#ifdef WITH_OPENVDB
if (typeId == _tokens->openvdbAsset) {
return new HdCyclesField(bprimId, typeId);
}
#endif
TF_CODING_ERROR("Unknown Bprim type %s", typeId.GetText());
return nullptr;
}
HdBprim *HdCyclesDelegate::CreateFallbackBprim(const TfToken &typeId)
{
return CreateBprim(typeId, SdfPath::EmptyPath());
}
void HdCyclesDelegate::DestroyBprim(HdBprim *bPrim)
{
delete bPrim;
}
void HdCyclesDelegate::CommitResources(HdChangeTracker *tracker)
{
TF_UNUSED(tracker);
const SceneLock lock(_renderParam.get());
_renderParam->UpdateScene();
}
TfToken HdCyclesDelegate::GetMaterialBindingPurpose() const
{
return HdTokens->full;
}
TfTokenVector HdCyclesDelegate::GetMaterialRenderContexts() const
{
return {_tokens->cycles};
}
VtDictionary HdCyclesDelegate::GetRenderStats() const
{
const Stats &stats = _renderParam->session->stats;
const Progress &progress = _renderParam->session->progress;
double totalTime;
double renderTime;
progress.get_time(totalTime, renderTime);
const double fractionDone = progress.get_progress();
std::string status;
std::string substatus;
progress.get_status(status, substatus);
if (!substatus.empty()) {
status += " | " + substatus;
}
return {{"rendererName", VtValue("Cycles")},
{"rendererVersion", VtValue(GfVec3i(0, 0, 0))},
{"percentDone", VtValue(floor_to_int(fractionDone * 100))},
{"fractionDone", VtValue(fractionDone)},
{"loadClockTime", VtValue(totalTime - renderTime)},
{"peakMemory", VtValue(stats.mem_peak)},
{"totalClockTime", VtValue(totalTime)},
{"totalMemory", VtValue(stats.mem_used)},
{"renderProgressAnnotation", VtValue(status)}};
}
HdAovDescriptor HdCyclesDelegate::GetDefaultAovDescriptor(const TfToken &name) const
{
if (name == HdAovTokens->color) {
HdFormat colorFormat = HdFormatFloat32Vec4;
if (IsDisplaySupported()) {
// Can use Cycles 'DisplayDriver' in OpenGL, but it only supports 'half4' format
colorFormat = HdFormatFloat16Vec4;
}
return HdAovDescriptor(colorFormat, false, VtValue(GfVec4f(0.0f)));
}
if (name == HdAovTokens->depth) {
return HdAovDescriptor(HdFormatFloat32, false, VtValue(1.0f));
}
if (name == HdAovTokens->normal) {
return HdAovDescriptor(HdFormatFloat32Vec3, false, VtValue(GfVec3f(0.0f)));
}
if (name == HdAovTokens->primId || name == HdAovTokens->instanceId ||
name == HdAovTokens->elementId)
{
return HdAovDescriptor(HdFormatInt32, false, VtValue(-1));
}
return HdAovDescriptor();
}
HdRenderSettingDescriptorList HdCyclesDelegate::GetRenderSettingDescriptors() const
{
Scene *const scene = _renderParam->session->scene.get();
HdRenderSettingDescriptorList descriptors;
descriptors.push_back({
"Time Limit",
HdCyclesRenderSettingsTokens->timeLimit,
VtValue(0.0),
});
descriptors.push_back({
"Sample Count",
HdCyclesRenderSettingsTokens->samples,
VtValue(1024),
});
descriptors.push_back({
"Sample Offset",
HdCyclesRenderSettingsTokens->sampleOffset,
VtValue(0),
});
for (const SocketType &socket : scene->integrator->type->inputs) {
descriptors.push_back({socket.ui_name.string(),
TfToken("cycles:integrator:" + socket.name.string()),
GetNodeValue(scene->integrator, socket)});
}
return descriptors;
}
void HdCyclesDelegate::SetRenderSetting(const PXR_NS::TfToken &key, const PXR_NS::VtValue &value)
{
Scene *const scene = _renderParam->session->scene.get();
Session *const session = _renderParam->session;
if (key == HdCyclesRenderSettingsTokens->stageMetersPerUnit) {
_renderParam->SetStageMetersPerUnit(
VtValue::Cast<double>(value).GetWithDefault(_renderParam->GetStageMetersPerUnit()));
}
else if (key == HdCyclesRenderSettingsTokens->timeLimit) {
session->set_time_limit(
VtValue::Cast<double>(value).GetWithDefault(session->params.time_limit));
}
else if (key == HdCyclesRenderSettingsTokens->samples) {
static const int max_samples = Integrator::MAX_SAMPLES;
int samples = VtValue::Cast<int>(value).GetWithDefault(session->params.samples);
samples = std::min(std::max(1, samples), max_samples);
session->set_samples(samples);
}
else if (key == HdCyclesRenderSettingsTokens->sampleOffset) {
session->params.sample_subset_offset = VtValue::Cast<int>(value).GetWithDefault(
session->params.sample_subset_offset);
session->params.sample_subset_length = Integrator::MAX_SAMPLES;
session->params.use_sample_subset = session->params.sample_subset_offset > 0;
++_settingsVersion;
}
else {
const std::string &keyString = key.GetString();
if (keyString.rfind("cycles:integrator:", 0) == 0) {
const ustring socketName(keyString, sizeof("cycles:integrator:") - 1);
if (const SocketType *socket = scene->integrator->type->find_input(socketName)) {
SetNodeValue(scene->integrator, *socket, value);
++_settingsVersion;
}
}
}
}
VtValue HdCyclesDelegate::GetRenderSetting(const TfToken &key) const
{
Scene *const scene = _renderParam->session->scene.get();
Session *const session = _renderParam->session;
if (key == HdCyclesRenderSettingsTokens->stageMetersPerUnit) {
return VtValue(_renderParam->GetStageMetersPerUnit());
}
if (key == HdCyclesRenderSettingsTokens->device) {
return VtValue(TfToken(Device::string_from_type(session->params.device.type)));
}
if (key == HdCyclesRenderSettingsTokens->threads) {
return VtValue(session->params.threads);
}
if (key == HdCyclesRenderSettingsTokens->timeLimit) {
return VtValue(session->params.time_limit);
}
if (key == HdCyclesRenderSettingsTokens->samples) {
return VtValue(session->params.samples);
}
if (key == HdCyclesRenderSettingsTokens->sampleOffset) {
return VtValue((session->params.use_sample_subset) ? session->params.sample_subset_offset : 0);
}
const std::string &keyString = key.GetString();
if (keyString.rfind("cycles:integrator:", 0) == 0) {
const ustring socketName(keyString, sizeof("cycles:integrator:") - 1);
if (const SocketType *socket = scene->integrator->type->find_input(socketName)) {
return GetNodeValue(scene->integrator, *socket);
}
}
return VtValue();
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,98 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "hydra/config.h"
#include <pxr/imaging/hd/renderDelegate.h>
#include <pxr/imaging/hgi/hgi.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
// clang-format off
#define HD_CYCLES_RENDER_SETTINGS_TOKENS \
(stageMetersPerUnit) \
((device, "cycles:device")) \
((threads, "cycles:threads")) \
((timeLimit, "cycles:time_limit")) \
((samples, "cycles:samples")) \
((sampleOffset, "cycles:sample_offset"))
// clang-format on
TF_DECLARE_PUBLIC_TOKENS(HdCyclesRenderSettingsTokens, HD_CYCLES_RENDER_SETTINGS_TOKENS);
class HdCyclesDelegate final : public PXR_NS::HdRenderDelegate {
public:
HdCyclesDelegate(const PXR_NS::HdRenderSettingsMap &settingsMap,
CCL_NS::Session *session_ = nullptr,
const bool keep_nodes = false);
~HdCyclesDelegate() override;
void SetDrivers(const PXR_NS::HdDriverVector &drivers) override;
bool IsDisplaySupported() const;
PXR_NS::Hgi *GetHgi() const
{
return _hgi;
}
const PXR_NS::TfTokenVector &GetSupportedRprimTypes() const override;
const PXR_NS::TfTokenVector &GetSupportedSprimTypes() const override;
const PXR_NS::TfTokenVector &GetSupportedBprimTypes() const override;
PXR_NS::HdRenderParam *GetRenderParam() const override;
PXR_NS::HdResourceRegistrySharedPtr GetResourceRegistry() const override;
PXR_NS::HdRenderSettingDescriptorList GetRenderSettingDescriptors() const override;
bool IsPauseSupported() const override;
bool Pause() override;
bool Resume() override;
PXR_NS::HdRenderPassSharedPtr CreateRenderPass(
PXR_NS::HdRenderIndex *index, const PXR_NS::HdRprimCollection &collection) override;
PXR_NS::HdInstancer *CreateInstancer(PXR_NS::HdSceneDelegate *delegate,
const PXR_NS::SdfPath &id) override;
void DestroyInstancer(PXR_NS::HdInstancer *instancer) override;
PXR_NS::HdRprim *CreateRprim(const PXR_NS::TfToken &typeId,
const PXR_NS::SdfPath &rprimId) override;
void DestroyRprim(PXR_NS::HdRprim *rPrim) override;
PXR_NS::HdSprim *CreateSprim(const PXR_NS::TfToken &typeId,
const PXR_NS::SdfPath &sprimId) override;
PXR_NS::HdSprim *CreateFallbackSprim(const PXR_NS::TfToken &typeId) override;
void DestroySprim(PXR_NS::HdSprim *sPrim) override;
PXR_NS::HdBprim *CreateBprim(const PXR_NS::TfToken &typeId,
const PXR_NS::SdfPath &bprimId) override;
PXR_NS::HdBprim *CreateFallbackBprim(const PXR_NS::TfToken &typeId) override;
void DestroyBprim(PXR_NS::HdBprim *bPrim) override;
void CommitResources(PXR_NS::HdChangeTracker *tracker) override;
PXR_NS::TfToken GetMaterialBindingPurpose() const override;
PXR_NS::TfTokenVector GetMaterialRenderContexts() const override;
PXR_NS::VtDictionary GetRenderStats() const override;
PXR_NS::HdAovDescriptor GetDefaultAovDescriptor(const PXR_NS::TfToken &name) const override;
void SetRenderSetting(const PXR_NS::TfToken &key, const PXR_NS::VtValue &value) override;
PXR_NS::VtValue GetRenderSetting(const PXR_NS::TfToken &key) const override;
private:
PXR_NS::Hgi *_hgi = nullptr;
std::unique_ptr<HdCyclesSession> _renderParam;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,170 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "hydra/render_pass.h"
#include "hydra/camera.h"
#include "hydra/output_driver.h"
#include "hydra/render_buffer.h"
#include "hydra/render_delegate.h"
#include "hydra/session.h"
#ifdef WITH_HYDRA_DISPLAY_DRIVER
# include "hydra/display_driver.h"
#endif
#include "scene/camera.h"
#include "scene/integrator.h"
#include "scene/scene.h"
#include "session/session.h"
#include <pxr/imaging/hd/renderPassState.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
HdCyclesRenderPass::HdCyclesRenderPass(HdRenderIndex *index,
const HdRprimCollection &collection,
HdCyclesSession *renderParam)
: HdRenderPass(index, collection), _renderParam(renderParam)
{
Session *const session = _renderParam->session;
// Reset cancel state so session thread can continue rendering
session->progress.reset();
session->set_output_driver(make_unique<HdCyclesOutputDriver>(renderParam));
const auto *const renderDelegate = static_cast<const HdCyclesDelegate *>(
GetRenderIndex()->GetRenderDelegate());
if (renderDelegate->IsDisplaySupported()) {
#ifdef WITH_HYDRA_DISPLAY_DRIVER
session->set_display_driver(
make_unique<HdCyclesDisplayDriver>(renderParam, renderDelegate->GetHgi()));
#endif
}
}
HdCyclesRenderPass::~HdCyclesRenderPass()
{
Session *const session = _renderParam->session;
session->cancel(true);
}
bool HdCyclesRenderPass::IsConverged() const
{
for (const HdRenderPassAovBinding &aovBinding : _renderParam->GetAovBindings()) {
if (aovBinding.renderBuffer && !aovBinding.renderBuffer->IsConverged()) {
return false;
}
}
return true;
}
void HdCyclesRenderPass::ResetConverged()
{
for (const HdRenderPassAovBinding &aovBinding : _renderParam->GetAovBindings()) {
if (auto *const renderBuffer = static_cast<HdCyclesRenderBuffer *>(aovBinding.renderBuffer)) {
renderBuffer->SetConverged(false);
}
}
}
void HdCyclesRenderPass::_Execute(const HdRenderPassStateSharedPtr &renderPassState,
const TfTokenVector & /*renderTags*/)
{
Scene *const scene = _renderParam->session->scene.get();
Session *const session = _renderParam->session;
if (session->progress.get_cancel()) {
return; // Something went wrong and cannot continue without recreating the session
}
if (scene->mutex.try_lock()) {
auto *const renderDelegate = static_cast<HdCyclesDelegate *>(
GetRenderIndex()->GetRenderDelegate());
const unsigned int settingsVersion = renderDelegate->GetRenderSettingsVersion();
// Update requested AOV bindings
const HdRenderPassAovBindingVector &aovBindings = renderPassState->GetAovBindings();
if (_renderParam->GetAovBindings() != aovBindings ||
// Need to resync passes when denoising is enabled or disabled to update the pass mode
(settingsVersion != _lastSettingsVersion && scene->integrator->use_denoise_is_modified()))
{
_renderParam->SyncAovBindings(aovBindings);
if (renderDelegate->IsDisplaySupported()) {
// Update display pass to the first requested color AOV
const HdRenderPassAovBinding displayAovBinding = !aovBindings.empty() ?
aovBindings.front() :
HdRenderPassAovBinding();
if (displayAovBinding.aovName == HdAovTokens->color && displayAovBinding.renderBuffer) {
_renderParam->SetDisplayAovBinding(displayAovBinding);
}
else {
_renderParam->SetDisplayAovBinding(HdRenderPassAovBinding());
}
}
}
// Update camera dimensions to the viewport size
CameraUtilFraming framing = renderPassState->GetFraming();
if (!framing.IsValid()) {
const GfVec4f vp = renderPassState->GetViewport();
framing = CameraUtilFraming(GfRect2i(GfVec2i(0), int(vp[2]), int(vp[3])));
}
scene->camera->set_full_width(framing.dataWindow.GetWidth());
scene->camera->set_full_height(framing.dataWindow.GetHeight());
if (const auto *const camera = static_cast<const HdCyclesCamera *>(
renderPassState->GetCamera()))
{
camera->ApplyCameraSettings(_renderParam, scene->camera);
}
else {
HdCyclesCamera::ApplyCameraSettings(_renderParam,
renderPassState->GetWorldToViewMatrix(),
renderPassState->GetProjectionMatrix(),
renderPassState->GetClipPlanes(),
scene->camera);
}
// Reset session if the session, scene, camera or AOV bindings changed
if (scene->need_reset() || settingsVersion != _lastSettingsVersion) {
_lastSettingsVersion = settingsVersion;
// Reset convergence state of all render buffers
ResetConverged();
BufferParams buffer_params;
buffer_params.full_x = static_cast<int>(framing.displayWindow.GetMin()[0]);
buffer_params.full_y = static_cast<int>(framing.displayWindow.GetMin()[1]);
buffer_params.full_width = static_cast<int>(framing.displayWindow.GetSize()[0]);
buffer_params.full_height = static_cast<int>(framing.displayWindow.GetSize()[1]);
buffer_params.window_x = framing.dataWindow.GetMinX() - buffer_params.full_x;
buffer_params.window_y = framing.dataWindow.GetMinY() - buffer_params.full_y;
buffer_params.window_width = framing.dataWindow.GetWidth();
buffer_params.window_height = framing.dataWindow.GetHeight();
buffer_params.width = buffer_params.window_width;
buffer_params.height = buffer_params.window_height;
session->reset(session->params, buffer_params);
}
scene->mutex.unlock();
// Start Cycles render thread if not already running
session->start();
}
session->draw();
}
void HdCyclesRenderPass::_MarkCollectionDirty() {}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,35 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "hydra/config.h"
#include <pxr/imaging/hd/renderPass.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesRenderPass final : public PXR_NS::HdRenderPass {
public:
HdCyclesRenderPass(PXR_NS::HdRenderIndex *index,
const PXR_NS::HdRprimCollection &collection,
HdCyclesSession *renderParam);
~HdCyclesRenderPass() override;
bool IsConverged() const override;
private:
void ResetConverged();
void _Execute(const PXR_NS::HdRenderPassStateSharedPtr &renderPassState,
const PXR_NS::TfTokenVector &renderTags) override;
void _MarkCollectionDirty() override;
HdCyclesSession *_renderParam;
unsigned int _lastSettingsVersion = 0;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1 @@
*PXR*

View File

@@ -0,0 +1,8 @@
/* Hide everything except USD / Hydra symbols, to avoid conflicts with other
* application using different library versions. */
{
global:
PXR*;
local:
*;
};

View File

@@ -0,0 +1,22 @@
{
"Plugins": [
{
"Info": {
"Types": {
"HdCyclesPlugin": {
"bases": [
"HdRendererPlugin"
],
"displayName": "Cycles",
"priority": 0
}
}
},
"LibraryPath": "@PLUG_INFO_LIBRARY_PATH@",
"Name": "hdCycles",
"ResourcePath": "@PLUG_INFO_RESOURCE_PATH@",
"Root": "@PLUG_INFO_ROOT@",
"Type": "library"
}
]
}

View File

@@ -0,0 +1,182 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "hydra/session.h"
#include "scene/object.h"
#include "scene/shader.h"
// Have to include shader.h before background.h so that 'set_shader' uses the correct 'set'
// overload taking a 'Node *', rather than the one taking a 'bool'
#include "scene/background.h"
#include "scene/light.h"
#include "scene/shader_graph.h"
#include "scene/shader_nodes.h"
#include "session/session.h"
HDCYCLES_NAMESPACE_OPEN_SCOPE
namespace {
const std::unordered_map<TfToken, PassType, TfToken::HashFunctor> kAovToPass = {
{HdAovTokens->color, PASS_COMBINED},
{HdAovTokens->depth, PASS_DEPTH},
{HdAovTokens->normal, PASS_NORMAL},
{HdAovTokens->primId, PASS_OBJECT_ID},
{HdAovTokens->instanceId, PASS_AOV_VALUE},
};
} // namespace
SceneLock::SceneLock(const HdRenderParam *renderParam)
: scene(static_cast<const HdCyclesSession *>(renderParam)->session->scene.get()),
sceneLock(scene->mutex)
{
}
SceneLock::~SceneLock() = default;
HdCyclesSession::HdCyclesSession(Session *session_, const bool keep_nodes)
: session(session_), keep_nodes(keep_nodes), _ownCyclesSession(false)
{
}
HdCyclesSession::HdCyclesSession(const SessionParams &params)
: session(new Session(params, SceneParams())), keep_nodes(false), _ownCyclesSession(true)
{
Scene *const scene = session->scene.get();
// Create background with ambient light
{
unique_ptr<ShaderGraph> graph = make_unique<ShaderGraph>();
BackgroundNode *bgNode = graph->create_node<BackgroundNode>();
bgNode->set_color(one_float3());
graph->connect(bgNode->output("Background"), graph->output()->input("Surface"));
scene->default_background->set_graph(std::move(graph));
scene->default_background->tag_update(scene);
}
// Wire up object color in default surface material
{
unique_ptr<ShaderGraph> graph = make_unique<ShaderGraph>();
ObjectInfoNode *objectNode = graph->create_node<ObjectInfoNode>();
DiffuseBsdfNode *diffuseNode = graph->create_node<DiffuseBsdfNode>();
graph->connect(objectNode->output("Color"), diffuseNode->input("Color"));
graph->connect(diffuseNode->output("BSDF"), graph->output()->input("Surface"));
// Create the instanceId AOV output
const ustring instanceId(HdAovTokens->instanceId.GetString());
OutputAOVNode *aovNode = graph->create_node<OutputAOVNode>();
aovNode->set_name(instanceId);
AttributeNode *instanceIdNode = graph->create_node<AttributeNode>();
instanceIdNode->set_attribute(instanceId);
graph->connect(instanceIdNode->output("Fac"), aovNode->input("Value"));
scene->default_surface->set_graph(std::move(graph));
scene->default_surface->tag_update(scene);
}
}
HdCyclesSession::~HdCyclesSession()
{
if (_ownCyclesSession) {
delete session;
}
}
void HdCyclesSession::UpdateScene()
{
Scene *const scene = session->scene.get();
// Update background depending on presence of a background light
if (scene->light_manager->need_update()) {
Light *background_light = nullptr;
bool have_lights = false;
for (Object *object : scene->objects) {
if (!object->get_geometry()->is_light()) {
continue;
}
have_lights = true;
Light *light = static_cast<Light *>(object->get_geometry());
if (light->is_background_light()) {
background_light = light;
break;
}
}
if (!background_light) {
scene->background->set_shader(scene->default_background);
scene->background->set_transparent(true);
/* Set background color depending to non-zero value if there are no
* lights in the scene, to match behavior of other renderers. */
for (ShaderNode *node : scene->default_background->graph->nodes) {
if (node->is_a(BackgroundNode::get_node_type())) {
BackgroundNode *bgNode = static_cast<BackgroundNode *>(node);
bgNode->set_color((have_lights) ? zero_float3() : make_float3(0.5f));
}
}
}
else {
scene->background->set_shader(background_light->get_shader());
scene->background->set_transparent(false);
}
scene->background->tag_update(scene);
}
}
void HdCyclesSession::SyncAovBindings(const HdRenderPassAovBindingVector &aovBindings)
{
Scene *const scene = session->scene.get();
// Delete all existing passes
const vector<Pass *> &scene_passes = scene->passes;
scene->delete_nodes(set<Pass *>(scene_passes.begin(), scene_passes.end()));
// Update passes with requested AOV bindings
_aovBindings = aovBindings;
for (const HdRenderPassAovBinding &aovBinding : aovBindings) {
const auto cyclesAov = kAovToPass.find(aovBinding.aovName);
if (cyclesAov == kAovToPass.end()) {
// TODO: Use PASS_AOV_COLOR and PASS_AOV_VALUE for these?
TF_WARN("Unknown pass %s", aovBinding.aovName.GetText());
continue;
}
const PassType type = cyclesAov->second;
const PassMode mode = PassMode::DENOISED;
Pass *pass = scene->create_node<Pass>();
pass->set_type(type);
pass->set_mode(mode);
pass->set_name(ustring(aovBinding.aovName.GetString()));
}
}
void HdCyclesSession::RemoveAovBinding(HdRenderBuffer *renderBuffer)
{
for (HdRenderPassAovBinding &aovBinding : _aovBindings) {
if (renderBuffer == aovBinding.renderBuffer) {
aovBinding.renderBuffer = nullptr;
break;
}
}
if (renderBuffer == _displayAovBinding.renderBuffer) {
_displayAovBinding.renderBuffer = nullptr;
}
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,72 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "hydra/config.h"
#include "util/thread.h"
#include <pxr/imaging/hd/renderDelegate.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
struct SceneLock {
SceneLock(const PXR_NS::HdRenderParam *renderParam);
~SceneLock();
CCL_NS::Scene *scene;
private:
CCL_NS::thread_scoped_lock sceneLock;
};
class HdCyclesSession final : public PXR_NS::HdRenderParam {
public:
HdCyclesSession(CCL_NS::Session *session_, const bool keep_nodes);
HdCyclesSession(const CCL_NS::SessionParams &params);
~HdCyclesSession() override;
void UpdateScene();
double GetStageMetersPerUnit() const
{
return _stageMetersPerUnit;
}
void SetStageMetersPerUnit(const double stageMetersPerUnit)
{
_stageMetersPerUnit = stageMetersPerUnit;
}
PXR_NS::HdRenderPassAovBinding GetDisplayAovBinding() const
{
return _displayAovBinding;
}
void SetDisplayAovBinding(const PXR_NS::HdRenderPassAovBinding &aovBinding)
{
_displayAovBinding = aovBinding;
}
const PXR_NS::HdRenderPassAovBindingVector &GetAovBindings() const
{
return _aovBindings;
}
void SyncAovBindings(const PXR_NS::HdRenderPassAovBindingVector &aovBindings);
void RemoveAovBinding(PXR_NS::HdRenderBuffer *renderBuffer);
CCL_NS::Session *session;
bool keep_nodes;
private:
const bool _ownCyclesSession;
double _stageMetersPerUnit = 0.01;
PXR_NS::HdRenderPassAovBindingVector _aovBindings;
PXR_NS::HdRenderPassAovBinding _displayAovBinding;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,164 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "hydra/util.h"
#include "scene/attribute.h"
#include <pxr/base/gf/vec2f.h>
#include <pxr/base/gf/vec3f.h>
#include <pxr/base/gf/vec4f.h>
#include <pxr/base/vt/array.h>
#include <pxr/imaging/hd/primvarSchema.h>
#include <pxr/imaging/hd/renderIndex.h>
#include <pxr/imaging/hd/sceneDelegate.h>
#include <pxr/imaging/hd/tokens.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
HdSceneIndexPrim GetPrim(HdSceneDelegate *delegate, const SdfPath &id)
{
if (!delegate) {
return {};
}
HdSceneIndexBaseRefPtr si = delegate->GetRenderIndex().GetTerminalSceneIndex();
if (!si) {
return {};
}
return si->GetPrim(id);
}
VtValue ReadPrimvar(const HdPrimvarsSchema &primvars, const TfToken &name)
{
if (!primvars) {
return {};
}
HdSampledDataSourceHandle ds = primvars.GetPrimvar(name).GetPrimvarValue();
return ds ? ds->GetValue(0.0f) : VtValue();
}
HdInterpolation ReadPrimvarInterpolation(const HdPrimvarsSchema &primvars, const TfToken &name)
{
if (!primvars) {
return HdInterpolationCount;
}
HdTokenDataSourceHandle ds = primvars.GetPrimvar(name).GetInterpolation();
if (!ds) {
return HdInterpolationCount;
}
const TfToken token = ds->GetTypedValue(0.0f);
if (token == HdPrimvarSchemaTokens->constant) {
return HdInterpolationConstant;
}
if (token == HdPrimvarSchemaTokens->uniform) {
return HdInterpolationUniform;
}
if (token == HdPrimvarSchemaTokens->varying) {
return HdInterpolationVarying;
}
if (token == HdPrimvarSchemaTokens->vertex) {
return HdInterpolationVertex;
}
if (token == HdPrimvarSchemaTokens->faceVarying) {
return HdInterpolationFaceVarying;
}
if (token == HdPrimvarSchemaTokens->instance) {
return HdInterpolationInstance;
}
return HdInterpolationCount;
}
TfToken ReadPrimvarRole(const HdPrimvarsSchema &primvars, const TfToken &name)
{
if (!primvars) {
return {};
}
HdTokenDataSourceHandle ds = primvars.GetPrimvar(name).GetRole();
return ds ? ds->GetTypedValue(0.0f) : TfToken();
}
TfTokenVector PrimvarNamesAtInterpolation(const HdPrimvarsSchema &primvars,
HdInterpolation interpolation)
{
TfTokenVector result;
if (!primvars) {
return result;
}
for (const TfToken &name : primvars.GetPrimvarNames()) {
if (ReadPrimvarInterpolation(primvars, name) == interpolation) {
result.push_back(name);
}
}
return result;
}
void ApplyPrimvars(AttributeSet &attributes,
const ustring &name,
VtValue value,
AttributeElement elem,
AttributeStandard std)
{
const void *data = HdGetValueData(value);
size_t size = value.GetArraySize();
const HdType valueType = HdGetValueTupleType(value).type;
TypeDesc attrType = CCL_NS::TypeUnknown;
switch (valueType) {
case HdTypeFloat:
attrType = CCL_NS::TypeFloat;
size *= sizeof(float);
break;
case HdTypeFloatVec2:
attrType = CCL_NS::TypeFloat2;
size *= sizeof(float2);
static_assert(sizeof(GfVec2f) == sizeof(float2));
break;
case HdTypeFloatVec3: {
const auto &valueData = value.Get<VtVec3fArray>();
if (elem & ATTR_ELEMENT_IS_NORMAL) {
attrType = CCL_NS::TypeNormal;
size = valueData.size() * sizeof(packed_normal);
VtArray<packed_normal> valueConverted;
valueConverted.reserve(valueData.size());
for (const GfVec3f &vec : valueData) {
valueConverted.push_back(packed_normal(make_float3(vec[0], vec[1], vec[2])));
}
data = valueConverted.data();
value = std::move(valueConverted);
}
else {
attrType = CCL_NS::TypeVector;
size = valueData.size() * sizeof(float3);
// The Cycles "float3" data type is padded to "float4", so need to convert the array
VtArray<float3> valueConverted;
valueConverted.reserve(valueData.size());
for (const GfVec3f &vec : valueData) {
valueConverted.push_back(make_float3(vec[0], vec[1], vec[2]));
}
data = valueConverted.data();
value = std::move(valueConverted);
}
break;
}
case HdTypeFloatVec4:
attrType = CCL_NS::TypeFloat4;
size *= sizeof(float4);
static_assert(sizeof(GfVec4f) == sizeof(float4));
break;
default:
TF_WARN("Unsupported attribute type %d", static_cast<int>(valueType));
return;
}
Attribute *const attr = attributes.add(name, attrType, elem);
attr->std = std;
assert(size == attr->data_sizeof() * attr->size);
std::memcpy(attr->data_for_write(), data, size);
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,112 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "hydra/config.h"
#include "scene/attribute.h"
#include <pxr/base/vt/value.h>
#include <pxr/imaging/hd/dataSource.h>
#include <pxr/imaging/hd/enums.h>
#include <pxr/imaging/hd/primvarsSchema.h>
#include <pxr/imaging/hd/sceneIndex.h>
#include <pxr/imaging/hd/timeSampleArray.h>
#include <pxr/imaging/hd/types.h>
PXR_NAMESPACE_OPEN_SCOPE
class HdSceneDelegate;
class SdfPath;
class TfToken;
PXR_NAMESPACE_CLOSE_SCOPE
HDCYCLES_NAMESPACE_OPEN_SCOPE
/* Prim data source from the scene index, or empty if none is reachable. */
PXR_NS::HdSceneIndexPrim GetPrim(PXR_NS::HdSceneDelegate *delegate, const PXR_NS::SdfPath &id);
/* Typed child data source, or null if missing or of a different type. */
template<typename T>
typename PXR_NS::HdTypedSampledDataSource<T>::Handle GetTypedDataSource(
const PXR_NS::HdContainerDataSourceHandle &container, const PXR_NS::TfToken &name)
{
if (!container) {
return nullptr;
}
return PXR_NS::HdTypedSampledDataSource<T>::Cast(container->Get(name));
}
/* Typed value at shutter offset 0, or `fallback` if missing or of a different type. */
template<typename T>
T GetTypedValue(const PXR_NS::HdContainerDataSourceHandle &container,
const PXR_NS::TfToken &name,
const T &fallback = T())
{
if (!container) {
return fallback;
}
const auto base = container->Get(name);
if (const auto typed = PXR_NS::HdTypedSampledDataSource<T>::Cast(base)) {
return typed->GetTypedValue(0.0f);
}
if (const auto sampled = PXR_NS::HdSampledDataSource::Cast(base)) {
PXR_NS::VtValue v = sampled->GetValue(0.0f);
if (v.IsHolding<T>()) {
return v.UncheckedGet<T>();
}
}
return fallback;
}
/* Sample a typed data source over the shutter into `out`. */
template<typename T, unsigned int Capacity>
void SampleTyped(const typename PXR_NS::HdTypedSampledDataSource<T>::Handle &ds,
float shutterOpen,
float shutterClose,
PXR_NS::HdTimeSampleArray<T, Capacity> *out)
{
if (!ds) {
out->Resize(0);
return;
}
std::vector<float> times;
const bool hasSamples = ds->GetContributingSampleTimesForInterval(
shutterOpen, shutterClose, &times);
if (!hasSamples || times.empty()) {
out->Resize(1);
out->times[0] = 0.0f;
out->values[0] = ds->GetTypedValue(0.0f);
return;
}
out->Resize(times.size());
for (size_t i = 0; i < times.size(); ++i) {
out->times[i] = times[i];
out->values[i] = ds->GetTypedValue(times[i]);
}
}
/* Read a primvar's value at shutter offset 0. */
PXR_NS::VtValue ReadPrimvar(const PXR_NS::HdPrimvarsSchema &primvars, const PXR_NS::TfToken &name);
/* Read a primvar's interpolation. */
PXR_NS::HdInterpolation ReadPrimvarInterpolation(const PXR_NS::HdPrimvarsSchema &primvars,
const PXR_NS::TfToken &name);
/* Read a primvar's role token. */
PXR_NS::TfToken ReadPrimvarRole(const PXR_NS::HdPrimvarsSchema &primvars,
const PXR_NS::TfToken &name);
/* Enumerate primvar names whose interpolation matches. */
PXR_NS::TfTokenVector PrimvarNamesAtInterpolation(const PXR_NS::HdPrimvarsSchema &primvars,
PXR_NS::HdInterpolation interpolation);
/* Convert a Hydra primvar value to a Cycles attribute. */
void ApplyPrimvars(CCL_NS::AttributeSet &attributes,
const CCL_NS::ustring &name,
PXR_NS::VtValue value,
CCL_NS::AttributeElement elem,
CCL_NS::AttributeStandard std);
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,92 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "hydra/volume.h"
#include "hydra/field.h"
#include "hydra/geometry.inl"
#include "hydra/util.h"
#include "scene/volume.h"
#include <pxr/imaging/hd/volumeFieldBindingSchema.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
// clang-format off
TF_DEFINE_PRIVATE_TOKENS(_tokens,
(openvdbAsset)
);
// clang-format on
HdCyclesVolume::HdCyclesVolume(const SdfPath &rprimId) : HdCyclesGeometry(rprimId) {}
HdCyclesVolume::~HdCyclesVolume() = default;
HdDirtyBits HdCyclesVolume::GetInitialDirtyBitsMask() const
{
HdDirtyBits bits = HdCyclesGeometry::GetInitialDirtyBitsMask();
bits |= HdChangeTracker::DirtyVolumeField;
return bits;
}
void HdCyclesVolume::Populate(HdSceneDelegate *sceneDelegate, HdDirtyBits dirtyBits, bool &rebuild)
{
Scene *const scene = (Scene *)_geom->get_owner();
if (dirtyBits & HdChangeTracker::DirtyVolumeField) {
const HdSceneIndexPrim prim = GetPrim(sceneDelegate, GetId());
HdVolumeFieldBindingSchema bindings = HdVolumeFieldBindingSchema::GetFromParent(
prim.dataSource);
for (const TfToken &fieldName : bindings.GetVolumeFieldBindingNames()) {
auto pathDs = bindings.GetVolumeFieldBinding(fieldName);
if (!pathDs) {
continue;
}
const SdfPath fieldId = pathDs->GetTypedValue(0.0f);
if (auto *const openvdbAsset = static_cast<HdCyclesField *>(
sceneDelegate->GetRenderIndex().GetBprim(_tokens->openvdbAsset, fieldId)))
{
const ustring name(fieldName.GetString());
AttributeStandard std = ATTR_STD_NONE;
if (name == Attribute::standard_name(ATTR_STD_VOLUME_DENSITY)) {
std = ATTR_STD_VOLUME_DENSITY;
}
else if (name == Attribute::standard_name(ATTR_STD_VOLUME_COLOR)) {
std = ATTR_STD_VOLUME_COLOR;
}
else if (name == Attribute::standard_name(ATTR_STD_VOLUME_FLAME)) {
std = ATTR_STD_VOLUME_FLAME;
}
else if (name == Attribute::standard_name(ATTR_STD_VOLUME_HEAT)) {
std = ATTR_STD_VOLUME_HEAT;
}
else if (name == Attribute::standard_name(ATTR_STD_VOLUME_TEMPERATURE)) {
std = ATTR_STD_VOLUME_TEMPERATURE;
}
else if (name == Attribute::standard_name(ATTR_STD_VOLUME_VELOCITY)) {
std = ATTR_STD_VOLUME_VELOCITY;
}
// Skip attributes that are not needed
if ((std != ATTR_STD_NONE && _geom->need_attribute(scene, std)) ||
_geom->need_attribute(scene, name))
{
Attribute *const attr = (std != ATTR_STD_NONE) ?
_geom->attributes.add(std) :
_geom->attributes.add(name, TypeFloat, ATTR_ELEMENT_VOXEL);
attr->data_voxel_for_write() = openvdbAsset->GetImageHandle();
}
}
}
_geom->merge_grids(scene);
rebuild = true;
}
}
HDCYCLES_NAMESPACE_CLOSE_SCOPE

View File

@@ -0,0 +1,28 @@
/* SPDX-FileCopyrightText: 2022 NVIDIA Corporation
* SPDX-FileCopyrightText: 2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "hydra/config.h"
#include "hydra/geometry.h"
#include <pxr/imaging/hd/volume.h>
HDCYCLES_NAMESPACE_OPEN_SCOPE
class HdCyclesVolume final : public HdCyclesGeometry<PXR_NS::HdVolume, CCL_NS::Volume> {
public:
HdCyclesVolume(const PXR_NS::SdfPath &rprimId);
~HdCyclesVolume() override;
PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override;
private:
void Populate(PXR_NS::HdSceneDelegate *sceneDelegate,
PXR_NS::HdDirtyBits dirtyBits,
bool &rebuild) override;
};
HDCYCLES_NAMESPACE_CLOSE_SCOPE