Add Chromium-only Blender WebEngine parity work
This commit is contained in:
205
blender-5.2.0/intern/cycles/hydra/CMakeLists.txt
Normal file
205
blender-5.2.0/intern/cycles/hydra/CMakeLists.txt
Normal 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()
|
||||
87
blender-5.2.0/intern/cycles/hydra/addon/__init__.py
Normal file
87
blender-5.2.0/intern/cycles/hydra/addon/__init__.py
Normal 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)
|
||||
252
blender-5.2.0/intern/cycles/hydra/camera.cpp
Normal file
252
blender-5.2.0/intern/cycles/hydra/camera.cpp
Normal 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
|
||||
46
blender-5.2.0/intern/cycles/hydra/camera.h
Normal file
46
blender-5.2.0/intern/cycles/hydra/camera.h
Normal 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
|
||||
46
blender-5.2.0/intern/cycles/hydra/config.h
Normal file
46
blender-5.2.0/intern/cycles/hydra/config.h
Normal 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
|
||||
226
blender-5.2.0/intern/cycles/hydra/curves.cpp
Normal file
226
blender-5.2.0/intern/cycles/hydra/curves.cpp
Normal 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
|
||||
37
blender-5.2.0/intern/cycles/hydra/curves.h
Normal file
37
blender-5.2.0/intern/cycles/hydra/curves.h
Normal 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
|
||||
292
blender-5.2.0/intern/cycles/hydra/display_driver.cpp
Normal file
292
blender-5.2.0/intern/cycles/hydra/display_driver.cpp
Normal 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 ¶ms,
|
||||
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 ¶ms)
|
||||
{
|
||||
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
|
||||
70
blender-5.2.0/intern/cycles/hydra/display_driver.h
Normal file
70
blender-5.2.0/intern/cycles/hydra/display_driver.h
Normal 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 ¶ms,
|
||||
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 ¶ms) 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
|
||||
109
blender-5.2.0/intern/cycles/hydra/field.cpp
Normal file
109
blender-5.2.0/intern/cycles/hydra/field.cpp
Normal 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
|
||||
35
blender-5.2.0/intern/cycles/hydra/field.h
Normal file
35
blender-5.2.0/intern/cycles/hydra/field.h
Normal 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
|
||||
160
blender-5.2.0/intern/cycles/hydra/file_reader.cpp
Normal file
160
blender-5.2.0/intern/cycles/hydra/file_reader.cpp
Normal 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
|
||||
18
blender-5.2.0/intern/cycles/hydra/file_reader.h
Normal file
18
blender-5.2.0/intern/cycles/hydra/file_reader.h
Normal 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
|
||||
9
blender-5.2.0/intern/cycles/hydra/geometry.cpp
Normal file
9
blender-5.2.0/intern/cycles/hydra/geometry.cpp
Normal 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"
|
||||
47
blender-5.2.0/intern/cycles/hydra/geometry.h
Normal file
47
blender-5.2.0/intern/cycles/hydra/geometry.h
Normal 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
|
||||
239
blender-5.2.0/intern/cycles/hydra/geometry.inl
Normal file
239
blender-5.2.0/intern/cycles/hydra/geometry.inl
Normal 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
|
||||
172
blender-5.2.0/intern/cycles/hydra/instancer.cpp
Normal file
172
blender-5.2.0/intern/cycles/hydra/instancer.cpp
Normal 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
|
||||
38
blender-5.2.0/intern/cycles/hydra/instancer.h
Normal file
38
blender-5.2.0/intern/cycles/hydra/instancer.h
Normal 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
|
||||
409
blender-5.2.0/intern/cycles/hydra/light.cpp
Normal file
409
blender-5.2.0/intern/cycles/hydra/light.cpp
Normal 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
|
||||
38
blender-5.2.0/intern/cycles/hydra/light.h
Normal file
38
blender-5.2.0/intern/cycles/hydra/light.h
Normal 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
|
||||
602
blender-5.2.0/intern/cycles/hydra/material.cpp
Normal file
602
blender-5.2.0/intern/cycles/hydra/material.cpp
Normal 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 ¶mName : 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
|
||||
60
blender-5.2.0/intern/cycles/hydra/material.h
Normal file
60
blender-5.2.0/intern/cycles/hydra/material.h
Normal 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
|
||||
617
blender-5.2.0/intern/cycles/hydra/mesh.cpp
Normal file
617
blender-5.2.0/intern/cycles/hydra/mesh.cpp
Normal 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
|
||||
44
blender-5.2.0/intern/cycles/hydra/mesh.h
Normal file
44
blender-5.2.0/intern/cycles/hydra/mesh.h
Normal 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
|
||||
574
blender-5.2.0/intern/cycles/hydra/node_util.cpp
Normal file
574
blender-5.2.0/intern/cycles/hydra/node_util.cpp
Normal 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
|
||||
19
blender-5.2.0/intern/cycles/hydra/node_util.h
Normal file
19
blender-5.2.0/intern/cycles/hydra/node_util.h
Normal 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
|
||||
81
blender-5.2.0/intern/cycles/hydra/output_driver.cpp
Normal file
81
blender-5.2.0/intern/cycles/hydra/output_driver.cpp
Normal 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
|
||||
24
blender-5.2.0/intern/cycles/hydra/output_driver.h
Normal file
24
blender-5.2.0/intern/cycles/hydra/output_driver.h
Normal 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
|
||||
3
blender-5.2.0/intern/cycles/hydra/plugInfo.json
Normal file
3
blender-5.2.0/intern/cycles/hydra/plugInfo.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"Includes": [ "*/resources/" ]
|
||||
}
|
||||
79
blender-5.2.0/intern/cycles/hydra/plugin.cpp
Normal file
79
blender-5.2.0/intern/cycles/hydra/plugin.cpp
Normal 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
|
||||
31
blender-5.2.0/intern/cycles/hydra/plugin.h
Normal file
31
blender-5.2.0/intern/cycles/hydra/plugin.h
Normal 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
|
||||
171
blender-5.2.0/intern/cycles/hydra/pointcloud.cpp
Normal file
171
blender-5.2.0/intern/cycles/hydra/pointcloud.cpp
Normal 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
|
||||
35
blender-5.2.0/intern/cycles/hydra/pointcloud.h
Normal file
35
blender-5.2.0/intern/cycles/hydra/pointcloud.h
Normal 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
|
||||
285
blender-5.2.0/intern/cycles/hydra/render_buffer.cpp
Normal file
285
blender-5.2.0/intern/cycles/hydra/render_buffer.cpp
Normal 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
|
||||
90
blender-5.2.0/intern/cycles/hydra/render_buffer.h
Normal file
90
blender-5.2.0/intern/cycles/hydra/render_buffer.h
Normal 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
|
||||
474
blender-5.2.0/intern/cycles/hydra/render_delegate.cpp
Normal file
474
blender-5.2.0/intern/cycles/hydra/render_delegate.cpp
Normal 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
|
||||
98
blender-5.2.0/intern/cycles/hydra/render_delegate.h
Normal file
98
blender-5.2.0/intern/cycles/hydra/render_delegate.h
Normal 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
|
||||
170
blender-5.2.0/intern/cycles/hydra/render_pass.cpp
Normal file
170
blender-5.2.0/intern/cycles/hydra/render_pass.cpp
Normal 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
|
||||
35
blender-5.2.0/intern/cycles/hydra/render_pass.h
Normal file
35
blender-5.2.0/intern/cycles/hydra/render_pass.h
Normal 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
|
||||
@@ -0,0 +1 @@
|
||||
*PXR*
|
||||
@@ -0,0 +1,8 @@
|
||||
/* Hide everything except USD / Hydra symbols, to avoid conflicts with other
|
||||
* application using different library versions. */
|
||||
{
|
||||
global:
|
||||
PXR*;
|
||||
local:
|
||||
*;
|
||||
};
|
||||
22
blender-5.2.0/intern/cycles/hydra/resources/plugInfo.json
Normal file
22
blender-5.2.0/intern/cycles/hydra/resources/plugInfo.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
182
blender-5.2.0/intern/cycles/hydra/session.cpp
Normal file
182
blender-5.2.0/intern/cycles/hydra/session.cpp
Normal 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 ¶ms)
|
||||
: 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
|
||||
72
blender-5.2.0/intern/cycles/hydra/session.h
Normal file
72
blender-5.2.0/intern/cycles/hydra/session.h
Normal 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 ¶ms);
|
||||
~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
|
||||
164
blender-5.2.0/intern/cycles/hydra/util.cpp
Normal file
164
blender-5.2.0/intern/cycles/hydra/util.cpp
Normal 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
|
||||
112
blender-5.2.0/intern/cycles/hydra/util.h
Normal file
112
blender-5.2.0/intern/cycles/hydra/util.h
Normal 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, ×);
|
||||
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
|
||||
92
blender-5.2.0/intern/cycles/hydra/volume.cpp
Normal file
92
blender-5.2.0/intern/cycles/hydra/volume.cpp
Normal 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
|
||||
28
blender-5.2.0/intern/cycles/hydra/volume.h
Normal file
28
blender-5.2.0/intern/cycles/hydra/volume.h
Normal 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
|
||||
Reference in New Issue
Block a user