Render: support USD Hydra render delegates
Hydra is a rendering architecture part of USD, designed to abstract the host application from the renderer. A renderer implementing a Hydra render delegate can run in any host application supporting Hydra, which now includes Blender. For external renderers this means less code to be written, and improved performance due to a using a C++ API instead of a Python API. Add-ons need to subclass bpy.types.HydraRenderEngine. See the example in the Python API docs for details. An add-on for Hydra Storm will be included as well. This is USD's rasterizing renderer, used in other applications like usdview. For users it can provide a preview of USD file export, and for developers it serves a reference. There are still limitations and missing features, especially around materials. The remaining to do items are tracked in #110765. This feature was contributed by AMD. Ref #110765 Co-authored-by: Georgiy Markelov <georgiy.m.markelov@gmail.com> Co-authored-by: Vasyl-Pidhirskyi <vpidhirskyi@gmail.com> Co-authored-by: Brian Savery <brian.savery@gmail.com> Co-authored-by: Brecht Van Lommel <brecht@blender.org> Pull Request: #104712
This commit is contained in:
parent
61f407d427
commit
04bb5f9995
Notes:
blender-bot
2025-02-14 01:30:47 +00:00
Referenced by issue #108013, USD Targets for 4.0
@ -367,6 +367,9 @@ option(WITH_USD "Enable Universal Scene Description (USD) Suppor
|
||||
# MaterialX
|
||||
option(WITH_MATERIALX "Enable MaterialX Support" ON)
|
||||
|
||||
# Hydra render engine
|
||||
option(WITH_HYDRA "Enable Hydra render engine" ON)
|
||||
|
||||
# 3D format support
|
||||
# Disable opencollada when we don't have precompiled libs
|
||||
option(WITH_OPENCOLLADA "Enable OpenCollada Support (http://www.opencollada.org)" ON)
|
||||
@ -932,6 +935,9 @@ set_and_warn_dependency(WITH_IMAGE_OPENEXR WITH_OPENVDB OFF)
|
||||
set_and_warn_dependency(WITH_IMAGE_OPENEXR WITH_ALEMBIC OFF)
|
||||
set_and_warn_dependency(WITH_IMAGE_OPENEXR WITH_CYCLES_OSL OFF)
|
||||
|
||||
# Hydra requires USD.
|
||||
set_and_warn_dependency(WITH_USD WITH_HYDRA OFF)
|
||||
|
||||
# auto enable openimageio for cycles
|
||||
if(WITH_CYCLES)
|
||||
# auto enable llvm for cycles_osl
|
||||
|
@ -58,6 +58,7 @@ set(WITH_SDL ON CACHE BOOL "" FORCE)
|
||||
set(WITH_TBB ON CACHE BOOL "" FORCE)
|
||||
set(WITH_USD ON CACHE BOOL "" FORCE)
|
||||
set(WITH_MATERIALX ON CACHE BOOL "" FORCE)
|
||||
set(WITH_HYDRA ON CACHE BOOL "" FORCE)
|
||||
|
||||
set(WITH_MEM_JEMALLOC ON CACHE BOOL "" FORCE)
|
||||
|
||||
|
@ -59,6 +59,7 @@ set(WITH_SDL ON CACHE BOOL "" FORCE)
|
||||
set(WITH_TBB ON CACHE BOOL "" FORCE)
|
||||
set(WITH_USD ON CACHE BOOL "" FORCE)
|
||||
set(WITH_MATERIALX ON CACHE BOOL "" FORCE)
|
||||
set(WITH_HYDRA ON CACHE BOOL "" FORCE)
|
||||
|
||||
set(WITH_MEM_JEMALLOC ON CACHE BOOL "" FORCE)
|
||||
|
||||
|
57
doc/python_api/examples/bpy.types.HydraRenderEngine.py
Normal file
57
doc/python_api/examples/bpy.types.HydraRenderEngine.py
Normal file
@ -0,0 +1,57 @@
|
||||
"""
|
||||
Base class for integrating USD Hydra based renderers.
|
||||
|
||||
USD Hydra Based Renderer
|
||||
++++++++++++++++++++++++
|
||||
"""
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
class CustomHydraRenderEngine(bpy.types.HydraRenderEngine):
|
||||
# Identifier and name in the user interface.
|
||||
bl_idname = "CUSTOM_HYDRA_RENDERER"
|
||||
bl_label = "Custom Hydra Renderer"
|
||||
|
||||
# Name of the render plugin.
|
||||
bl_delegate_id = "HdCustomRendererPlugin"
|
||||
|
||||
# Register path to plugin.
|
||||
@classmethod
|
||||
def register(cls):
|
||||
super().register()
|
||||
|
||||
import pxr
|
||||
pxr.Plug.Registry().RegisterPlugins(['/path/to/plugin'])
|
||||
|
||||
# Render settings that will be passed to the delegate.
|
||||
def get_render_settings(self, engine_type):
|
||||
return {
|
||||
'myBoolean': True,
|
||||
'myValue': 8,
|
||||
'aovToken:Depth': "depth",
|
||||
}
|
||||
|
||||
# RenderEngine methods for update, render and draw are implemented in
|
||||
# HydraRenderEngine. Optionally extra work can be done before or after
|
||||
# by implementing the methods like this.
|
||||
def update(self, data, depsgraph):
|
||||
super().update(data, depsgraph)
|
||||
# Do extra work here
|
||||
|
||||
def update_render_passes(self, scene, render_layer):
|
||||
if render_layer.use_pass_z:
|
||||
self.register_pass(scene, render_layer, 'Depth', 1, 'Z', 'VALUE')
|
||||
|
||||
|
||||
# Registration
|
||||
def register():
|
||||
bpy.utils.register_class(CustomHydraRenderEngine)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(CustomHydraRenderEngine)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
@ -938,10 +938,6 @@ class PropertyGroup(StructRNA, metaclass=RNAMetaPropGroup):
|
||||
__slots__ = ()
|
||||
|
||||
|
||||
class RenderEngine(StructRNA, metaclass=RNAMeta):
|
||||
__slots__ = ()
|
||||
|
||||
|
||||
class KeyingSetInfo(StructRNA, metaclass=RNAMeta):
|
||||
__slots__ = ()
|
||||
|
||||
@ -1256,3 +1252,71 @@ class GeometryNode(NodeInternal):
|
||||
@classmethod
|
||||
def poll(cls, ntree):
|
||||
return ntree.bl_idname == 'GeometryNodeTree'
|
||||
|
||||
|
||||
class RenderEngine(StructRNA, metaclass=RNAMeta):
|
||||
__slots__ = ()
|
||||
|
||||
|
||||
class HydraRenderEngine(RenderEngine):
|
||||
__slots__ = ()
|
||||
|
||||
bl_use_shading_nodes_custom = False
|
||||
bl_delegate_id = 'HdStormRendererPlugin'
|
||||
|
||||
def __init__(self):
|
||||
self.engine_ptr = None
|
||||
|
||||
def __del__(self):
|
||||
if hasattr(self, 'engine_ptr'):
|
||||
if self.engine_ptr:
|
||||
import _bpy_hydra
|
||||
_bpy_hydra.engine_free(self.engine_ptr)
|
||||
|
||||
def get_render_settings(self, engine_type: str):
|
||||
"""
|
||||
Provide render settings for `HdRenderDelegate`.
|
||||
"""
|
||||
return {}
|
||||
|
||||
# Final render.
|
||||
def update(self, data, depsgraph):
|
||||
import _bpy_hydra
|
||||
|
||||
engine_type = 'PREVIEW' if self.is_preview else 'FINAL'
|
||||
if not self.engine_ptr:
|
||||
self.engine_ptr = _bpy_hydra.engine_create(self, engine_type, self.bl_delegate_id)
|
||||
if not self.engine_ptr:
|
||||
return
|
||||
|
||||
_bpy_hydra.engine_update(self.engine_ptr, depsgraph, None)
|
||||
|
||||
for key, val in self.get_render_settings('PREVIEW' if self.is_preview else 'FINAL').items():
|
||||
_bpy_hydra.engine_set_render_setting(self.engine_ptr, key, val)
|
||||
|
||||
def render(self, depsgraph):
|
||||
if not self.engine_ptr:
|
||||
return
|
||||
|
||||
import _bpy_hydra
|
||||
_bpy_hydra.engine_render(self.engine_ptr)
|
||||
|
||||
# Viewport render.
|
||||
def view_update(self, context, depsgraph):
|
||||
import _bpy_hydra
|
||||
if not self.engine_ptr:
|
||||
self.engine_ptr = _bpy_hydra.engine_create(self, 'VIEWPORT', self.bl_delegate_id)
|
||||
if not self.engine_ptr:
|
||||
return
|
||||
|
||||
_bpy_hydra.engine_update(self.engine_ptr, depsgraph, context)
|
||||
|
||||
for key, val in self.get_render_settings('VIEWPORT').items():
|
||||
_bpy_hydra.engine_set_render_setting(self.engine_ptr, key, val)
|
||||
|
||||
def view_draw(self, context, depsgraph):
|
||||
if not self.engine_ptr:
|
||||
return
|
||||
|
||||
import _bpy_hydra
|
||||
_bpy_hydra.engine_view_draw(self.engine_ptr, context)
|
||||
|
@ -14,6 +14,17 @@ if(WIN32)
|
||||
endif()
|
||||
add_definitions(-DBOOST_ALL_NO_LIB)
|
||||
|
||||
# Precompiled Linux libs are made with GCC, and USD uses some extensions
|
||||
# which lead to an incompatible ABI for Clang. Using those extensions with
|
||||
# Clang as well works around the issue.
|
||||
if(UNIX AND NOT APPLE)
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
if(EXISTS ${LIBDIR})
|
||||
add_definitions(-DARCH_HAS_GNU_STL_EXTENSIONS)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# USD headers use deprecated TBB headers, silence warning.
|
||||
add_definitions(-DTBB_SUPPRESS_DEPRECATED_MESSAGES=1)
|
||||
|
||||
@ -54,8 +65,13 @@ set(INC
|
||||
../../editors/include
|
||||
../../imbuf
|
||||
../../makesrna
|
||||
../../nodes
|
||||
../../python/intern
|
||||
../../windowmanager
|
||||
../../../../intern/utfconv
|
||||
../../../../intern/clog
|
||||
# RNA_prototypes.h
|
||||
${CMAKE_BINARY_DIR}/source/blender/makesrna
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
@ -94,6 +110,7 @@ set(SRC
|
||||
intern/usd_reader_volume.cc
|
||||
intern/usd_reader_xform.cc
|
||||
|
||||
usd.hh
|
||||
usd.h
|
||||
|
||||
intern/usd_asset_utils.h
|
||||
@ -124,6 +141,38 @@ set(SRC
|
||||
intern/usd_reader_xform.h
|
||||
)
|
||||
|
||||
if(WITH_HYDRA)
|
||||
list(APPEND SRC
|
||||
hydra/camera.cc
|
||||
hydra/curves.cc
|
||||
hydra/hydra_scene_delegate.cc
|
||||
hydra/id.cc
|
||||
hydra/image.cc
|
||||
hydra/instancer.cc
|
||||
hydra/light.cc
|
||||
hydra/material.cc
|
||||
hydra/mesh.cc
|
||||
hydra/object.cc
|
||||
hydra/volume.cc
|
||||
hydra/volume_modifier.cc
|
||||
hydra/world.cc
|
||||
|
||||
hydra/camera.h
|
||||
hydra/curves.h
|
||||
hydra/hydra_scene_delegate.h
|
||||
hydra/id.h
|
||||
hydra/image.h
|
||||
hydra/instancer.h
|
||||
hydra/light.h
|
||||
hydra/material.h
|
||||
hydra/mesh.h
|
||||
hydra/object.h
|
||||
hydra/volume.h
|
||||
hydra/volume_modifier.h
|
||||
hydra/world.h
|
||||
)
|
||||
endif()
|
||||
|
||||
set(LIB
|
||||
bf_blenkernel
|
||||
PRIVATE bf::blenlib
|
||||
@ -153,6 +202,9 @@ endif()
|
||||
|
||||
blender_add_lib(bf_usd "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
|
||||
# RNA_prototypes.h
|
||||
add_dependencies(bf_usd bf_rna)
|
||||
|
||||
if(COMMAND target_precompile_headers)
|
||||
target_precompile_headers(bf_usd PRIVATE intern/usd_precomp.h)
|
||||
endif()
|
||||
|
283
source/blender/io/usd/hydra/camera.cc
Normal file
283
source/blender/io/usd/hydra/camera.cc
Normal file
@ -0,0 +1,283 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "camera.h"
|
||||
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_view3d_types.h"
|
||||
|
||||
#include "hydra/object.h"
|
||||
|
||||
namespace blender::io::hydra {
|
||||
|
||||
CameraData::CameraData(View3D *v3d, ARegion *region)
|
||||
{
|
||||
RegionView3D *region_data = (RegionView3D *)region->regiondata;
|
||||
|
||||
/* TODO: refactor use BKE_camera_params API. */
|
||||
float VIEWPORT_SENSOR_SIZE = DEFAULT_SENSOR_WIDTH * 2.0f;
|
||||
|
||||
pxr::GfVec2i res(region->winx, region->winy);
|
||||
float ratio = (float)res[0] / res[1];
|
||||
transform_ = gf_matrix_from_transform(region_data->viewmat).GetInverse();
|
||||
|
||||
switch (region_data->persp) {
|
||||
case RV3D_PERSP: {
|
||||
mode_ = CAM_PERSP;
|
||||
clip_range_ = pxr::GfRange1f(v3d->clip_start, v3d->clip_end);
|
||||
lens_shift_ = pxr::GfVec2f(0.0, 0.0);
|
||||
focal_length_ = v3d->lens;
|
||||
|
||||
if (ratio > 1.0) {
|
||||
sensor_size_ = pxr::GfVec2f(VIEWPORT_SENSOR_SIZE, VIEWPORT_SENSOR_SIZE / ratio);
|
||||
}
|
||||
else {
|
||||
sensor_size_ = pxr::GfVec2f(VIEWPORT_SENSOR_SIZE * ratio, VIEWPORT_SENSOR_SIZE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case RV3D_ORTHO: {
|
||||
mode_ = CAM_ORTHO;
|
||||
lens_shift_ = pxr::GfVec2f(0.0f, 0.0f);
|
||||
|
||||
float o_size = region_data->dist * VIEWPORT_SENSOR_SIZE / v3d->lens;
|
||||
float o_depth = v3d->clip_end;
|
||||
|
||||
clip_range_ = pxr::GfRange1f(-o_depth * 0.5, o_depth * 0.5);
|
||||
|
||||
if (ratio > 1.0f) {
|
||||
ortho_size_ = pxr::GfVec2f(o_size, o_size / ratio);
|
||||
}
|
||||
else {
|
||||
ortho_size_ = pxr::GfVec2f(o_size * ratio, o_size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case RV3D_CAMOB: {
|
||||
pxr::GfMatrix4d mat = transform_;
|
||||
*this = CameraData(v3d->camera, res, pxr::GfVec4f(0, 0, 1, 1));
|
||||
transform_ = mat;
|
||||
|
||||
/* This formula was taken from previous plugin with corresponded comment.
|
||||
* See blender/intern/cycles/blender/blender_camera.cpp:blender_camera_from_view (look
|
||||
* for 1.41421f). */
|
||||
float zoom = 4.0 / pow((pow(2.0, 0.5) + region_data->camzoom / 50.0), 2);
|
||||
|
||||
/* Updating l_shift due to viewport zoom and view_camera_offset
|
||||
* view_camera_offset should be multiplied by 2. */
|
||||
lens_shift_ = pxr::GfVec2f((lens_shift_[0] + region_data->camdx * 2) / zoom,
|
||||
(lens_shift_[1] + region_data->camdy * 2) / zoom);
|
||||
|
||||
if (mode_ == CAM_ORTHO) {
|
||||
ortho_size_ *= zoom;
|
||||
}
|
||||
else {
|
||||
sensor_size_ *= zoom;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CameraData::CameraData(Object *camera_obj, pxr::GfVec2i res, pxr::GfVec4f tile)
|
||||
{
|
||||
Camera *camera = (Camera *)camera_obj->data;
|
||||
|
||||
float t_pos[2] = {tile[0], tile[1]};
|
||||
float t_size[2] = {tile[2], tile[3]};
|
||||
transform_ = gf_matrix_from_transform(camera_obj->object_to_world);
|
||||
clip_range_ = pxr::GfRange1f(camera->clip_start, camera->clip_end);
|
||||
mode_ = camera->type;
|
||||
|
||||
if (camera->dof.flag & CAM_DOF_ENABLED) {
|
||||
float focus_distance;
|
||||
if (!camera->dof.focus_object) {
|
||||
focus_distance = camera->dof.focus_distance;
|
||||
}
|
||||
else {
|
||||
pxr::GfVec3f obj_pos(camera->dof.focus_object->object_to_world[0][3],
|
||||
camera->dof.focus_object->object_to_world[1][3],
|
||||
camera->dof.focus_object->object_to_world[2][3]);
|
||||
pxr::GfVec3f cam_pos(transform_[0][3], transform_[1][3], transform_[2][3]);
|
||||
focus_distance = (obj_pos - cam_pos).GetLength();
|
||||
}
|
||||
|
||||
dof_data_ = std::tuple(
|
||||
std::max(focus_distance, 0.001f), camera->dof.aperture_fstop, camera->dof.aperture_blades);
|
||||
}
|
||||
|
||||
float ratio = (float)res[0] / res[1];
|
||||
|
||||
switch (camera->sensor_fit) {
|
||||
case CAMERA_SENSOR_FIT_VERT:
|
||||
lens_shift_ = pxr::GfVec2f(camera->shiftx / ratio, camera->shifty);
|
||||
break;
|
||||
case CAMERA_SENSOR_FIT_HOR:
|
||||
lens_shift_ = pxr::GfVec2f(camera->shiftx, camera->shifty * ratio);
|
||||
break;
|
||||
case CAMERA_SENSOR_FIT_AUTO:
|
||||
if (ratio > 1.0f) {
|
||||
lens_shift_ = pxr::GfVec2f(camera->shiftx, camera->shifty * ratio);
|
||||
}
|
||||
else {
|
||||
lens_shift_ = pxr::GfVec2f(camera->shiftx / ratio, camera->shifty);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
lens_shift_ = pxr::GfVec2f(camera->shiftx, camera->shifty);
|
||||
break;
|
||||
}
|
||||
|
||||
lens_shift_ = pxr::GfVec2f(
|
||||
lens_shift_[0] / t_size[0] + (t_pos[0] + t_size[0] * 0.5 - 0.5) / t_size[0],
|
||||
lens_shift_[1] / t_size[1] + (t_pos[1] + t_size[1] * 0.5 - 0.5) / t_size[1]);
|
||||
|
||||
switch (camera->type) {
|
||||
case CAM_PERSP: {
|
||||
focal_length_ = camera->lens;
|
||||
|
||||
switch (camera->sensor_fit) {
|
||||
case CAMERA_SENSOR_FIT_VERT:
|
||||
sensor_size_ = pxr::GfVec2f(camera->sensor_y * ratio, camera->sensor_y);
|
||||
break;
|
||||
case CAMERA_SENSOR_FIT_HOR:
|
||||
sensor_size_ = pxr::GfVec2f(camera->sensor_x, camera->sensor_x / ratio);
|
||||
break;
|
||||
case CAMERA_SENSOR_FIT_AUTO:
|
||||
if (ratio > 1.0f) {
|
||||
sensor_size_ = pxr::GfVec2f(camera->sensor_x, camera->sensor_x / ratio);
|
||||
}
|
||||
else {
|
||||
sensor_size_ = pxr::GfVec2f(camera->sensor_x * ratio, camera->sensor_x);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
sensor_size_ = pxr::GfVec2f(camera->sensor_x, camera->sensor_y);
|
||||
break;
|
||||
}
|
||||
sensor_size_ = pxr::GfVec2f(sensor_size_[0] * t_size[0], sensor_size_[1] * t_size[1]);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAM_ORTHO: {
|
||||
focal_length_ = 0.0f;
|
||||
switch (camera->sensor_fit) {
|
||||
case CAMERA_SENSOR_FIT_VERT:
|
||||
ortho_size_ = pxr::GfVec2f(camera->ortho_scale * ratio, camera->ortho_scale);
|
||||
break;
|
||||
case CAMERA_SENSOR_FIT_HOR:
|
||||
ortho_size_ = pxr::GfVec2f(camera->ortho_scale, camera->ortho_scale / ratio);
|
||||
break;
|
||||
case CAMERA_SENSOR_FIT_AUTO:
|
||||
if (ratio > 1.0f) {
|
||||
ortho_size_ = pxr::GfVec2f(camera->ortho_scale, camera->ortho_scale / ratio);
|
||||
}
|
||||
else {
|
||||
ortho_size_ = pxr::GfVec2f(camera->ortho_scale * ratio, camera->ortho_scale);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ortho_size_ = pxr::GfVec2f(camera->ortho_scale, camera->ortho_scale);
|
||||
break;
|
||||
}
|
||||
ortho_size_ = pxr::GfVec2f(ortho_size_[0] * t_size[0], ortho_size_[1] * t_size[1]);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAM_PANO: {
|
||||
/* TODO: Recheck parameters for PANO camera */
|
||||
focal_length_ = camera->lens;
|
||||
|
||||
switch (camera->sensor_fit) {
|
||||
case CAMERA_SENSOR_FIT_VERT:
|
||||
sensor_size_ = pxr::GfVec2f(camera->sensor_y * ratio, camera->sensor_y);
|
||||
break;
|
||||
case CAMERA_SENSOR_FIT_HOR:
|
||||
sensor_size_ = pxr::GfVec2f(camera->sensor_x, camera->sensor_x / ratio);
|
||||
break;
|
||||
case CAMERA_SENSOR_FIT_AUTO:
|
||||
if (ratio > 1.0f) {
|
||||
sensor_size_ = pxr::GfVec2f(camera->sensor_x, camera->sensor_x / ratio);
|
||||
}
|
||||
else {
|
||||
sensor_size_ = pxr::GfVec2f(camera->sensor_x * ratio, camera->sensor_x);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
sensor_size_ = pxr::GfVec2f(camera->sensor_x, camera->sensor_y);
|
||||
break;
|
||||
}
|
||||
sensor_size_ = pxr::GfVec2f(sensor_size_[0] * t_size[0], sensor_size_[1] * t_size[1]);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
focal_length_ = camera->lens;
|
||||
sensor_size_ = pxr::GfVec2f(camera->sensor_y * ratio, camera->sensor_y);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pxr::GfCamera CameraData::gf_camera()
|
||||
{
|
||||
return gf_camera(pxr::GfVec4f(0, 0, 1, 1));
|
||||
}
|
||||
|
||||
pxr::GfCamera CameraData::gf_camera(pxr::GfVec4f tile)
|
||||
{
|
||||
float t_pos[2] = {tile[0], tile[1]}, t_size[2] = {tile[2], tile[3]};
|
||||
|
||||
pxr::GfCamera gf_camera = pxr::GfCamera();
|
||||
|
||||
gf_camera.SetClippingRange(clip_range_);
|
||||
|
||||
float l_shift[2] = {(lens_shift_[0] + t_pos[0] + t_size[0] * 0.5f - 0.5f) / t_size[0],
|
||||
(lens_shift_[1] + t_pos[1] + t_size[1] * 0.5f - 0.5f) / t_size[1]};
|
||||
|
||||
switch (mode_) {
|
||||
case CAM_PERSP:
|
||||
case CAM_PANO: {
|
||||
/* TODO: store panoramic camera settings */
|
||||
gf_camera.SetProjection(pxr::GfCamera::Projection::Perspective);
|
||||
gf_camera.SetFocalLength(focal_length_);
|
||||
|
||||
float s_size[2] = {sensor_size_[0] * t_size[0], sensor_size_[1] * t_size[1]};
|
||||
|
||||
gf_camera.SetHorizontalAperture(s_size[0]);
|
||||
gf_camera.SetVerticalAperture(s_size[1]);
|
||||
|
||||
gf_camera.SetHorizontalApertureOffset(l_shift[0] * s_size[0]);
|
||||
gf_camera.SetVerticalApertureOffset(l_shift[1] * s_size[1]);
|
||||
break;
|
||||
}
|
||||
case CAM_ORTHO: {
|
||||
gf_camera.SetProjection(pxr::GfCamera::Projection::Orthographic);
|
||||
|
||||
/* Use tenths of a world unit accorging to USD docs
|
||||
* https://graphics.pixar.com/usd/docs/api/class_gf_camera.html */
|
||||
float o_size[2] = {ortho_size_[0] * t_size[0] * 10, ortho_size_[1] * t_size[1] * 10};
|
||||
|
||||
gf_camera.SetHorizontalAperture(o_size[0]);
|
||||
gf_camera.SetVerticalAperture(o_size[1]);
|
||||
|
||||
gf_camera.SetHorizontalApertureOffset(l_shift[0] * o_size[0]);
|
||||
gf_camera.SetVerticalApertureOffset(l_shift[1] * o_size[1]);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
gf_camera.SetTransform(transform_);
|
||||
return gf_camera;
|
||||
}
|
||||
|
||||
} // namespace blender::io::hydra
|
36
source/blender/io/usd/hydra/camera.h
Normal file
36
source/blender/io/usd/hydra/camera.h
Normal file
@ -0,0 +1,36 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
|
||||
#include <pxr/base/gf/camera.h>
|
||||
#include <pxr/base/gf/vec2f.h>
|
||||
|
||||
struct ARegion;
|
||||
struct Object;
|
||||
struct View3D;
|
||||
|
||||
namespace blender::io::hydra {
|
||||
|
||||
class CameraData {
|
||||
private:
|
||||
int mode_;
|
||||
pxr::GfRange1f clip_range_;
|
||||
float focal_length_;
|
||||
pxr::GfVec2f sensor_size_;
|
||||
pxr::GfMatrix4d transform_;
|
||||
pxr::GfVec2f lens_shift_;
|
||||
pxr::GfVec2f ortho_size_;
|
||||
std::tuple<float, float, int> dof_data_;
|
||||
|
||||
public:
|
||||
CameraData(View3D *v3d, ARegion *region);
|
||||
CameraData(Object *camera_obj, pxr::GfVec2i res, pxr::GfVec4f tile);
|
||||
|
||||
pxr::GfCamera gf_camera();
|
||||
pxr::GfCamera gf_camera(pxr::GfVec4f tile);
|
||||
};
|
||||
|
||||
} // namespace blender::io::hydra
|
183
source/blender/io/usd/hydra/curves.cc
Normal file
183
source/blender/io/usd/hydra/curves.cc
Normal file
@ -0,0 +1,183 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#include "curves.h"
|
||||
|
||||
#include <pxr/base/gf/vec2f.h>
|
||||
#include <pxr/imaging/hd/tokens.h>
|
||||
|
||||
#include "BKE_customdata.h"
|
||||
#include "BKE_material.h"
|
||||
|
||||
#include "hydra_scene_delegate.h"
|
||||
|
||||
namespace blender::io::hydra {
|
||||
|
||||
CurvesData::CurvesData(HydraSceneDelegate *scene_delegate,
|
||||
Object *object,
|
||||
pxr::SdfPath const &prim_id)
|
||||
: ObjectData(scene_delegate, object, prim_id)
|
||||
{
|
||||
}
|
||||
|
||||
void CurvesData::init()
|
||||
{
|
||||
ID_LOGN(1, "");
|
||||
|
||||
Object *object = (Object *)id;
|
||||
write_curves((Curves *)object->data);
|
||||
write_transform();
|
||||
write_materials();
|
||||
}
|
||||
|
||||
void CurvesData::insert()
|
||||
{
|
||||
ID_LOGN(1, "");
|
||||
scene_delegate_->GetRenderIndex().InsertRprim(
|
||||
pxr::HdPrimTypeTokens->basisCurves, scene_delegate_, prim_id);
|
||||
}
|
||||
|
||||
void CurvesData::remove()
|
||||
{
|
||||
ID_LOG(1, "");
|
||||
scene_delegate_->GetRenderIndex().RemoveRprim(prim_id);
|
||||
}
|
||||
|
||||
void CurvesData::update()
|
||||
{
|
||||
Object *object = (Object *)id;
|
||||
pxr::HdDirtyBits bits = pxr::HdChangeTracker::Clean;
|
||||
if ((id->recalc & ID_RECALC_GEOMETRY) || (((ID *)object->data)->recalc & ID_RECALC_GEOMETRY)) {
|
||||
init();
|
||||
bits = pxr::HdChangeTracker::AllDirty;
|
||||
}
|
||||
if (id->recalc & ID_RECALC_SHADING) {
|
||||
write_materials();
|
||||
bits |= pxr::HdChangeTracker::DirtyMaterialId | pxr::HdChangeTracker::DirtyDoubleSided;
|
||||
}
|
||||
if (id->recalc & ID_RECALC_TRANSFORM) {
|
||||
write_transform();
|
||||
bits |= pxr::HdChangeTracker::DirtyTransform;
|
||||
}
|
||||
|
||||
if (bits == pxr::HdChangeTracker::Clean) {
|
||||
return;
|
||||
}
|
||||
|
||||
scene_delegate_->GetRenderIndex().GetChangeTracker().MarkRprimDirty(prim_id, bits);
|
||||
ID_LOGN(1, "");
|
||||
}
|
||||
|
||||
pxr::VtValue CurvesData::get_data(pxr::TfToken const &key) const
|
||||
{
|
||||
if (key == pxr::HdTokens->points) {
|
||||
return pxr::VtValue(vertices_);
|
||||
}
|
||||
else if (key == pxr::HdPrimvarRoleTokens->textureCoordinate) {
|
||||
return pxr::VtValue(uvs_);
|
||||
}
|
||||
else if (key == pxr::HdTokens->widths) {
|
||||
return pxr::VtValue(widths_);
|
||||
}
|
||||
return pxr::VtValue();
|
||||
}
|
||||
|
||||
pxr::SdfPath CurvesData::material_id() const
|
||||
{
|
||||
if (!mat_data_) {
|
||||
return pxr::SdfPath();
|
||||
}
|
||||
return mat_data_->prim_id;
|
||||
}
|
||||
|
||||
void CurvesData::available_materials(Set<pxr::SdfPath> &paths) const
|
||||
{
|
||||
if (mat_data_ && !mat_data_->prim_id.IsEmpty()) {
|
||||
paths.add(mat_data_->prim_id);
|
||||
}
|
||||
}
|
||||
|
||||
pxr::HdBasisCurvesTopology CurvesData::topology() const
|
||||
{
|
||||
return pxr::HdBasisCurvesTopology(pxr::HdTokens->linear,
|
||||
pxr::TfToken(),
|
||||
pxr::HdTokens->nonperiodic,
|
||||
curve_vertex_counts_,
|
||||
pxr::VtIntArray());
|
||||
}
|
||||
|
||||
pxr::HdPrimvarDescriptorVector CurvesData::primvar_descriptors(
|
||||
pxr::HdInterpolation interpolation) const
|
||||
{
|
||||
pxr::HdPrimvarDescriptorVector primvars;
|
||||
if (interpolation == pxr::HdInterpolationVertex) {
|
||||
if (!vertices_.empty()) {
|
||||
primvars.emplace_back(pxr::HdTokens->points, interpolation, pxr::HdPrimvarRoleTokens->point);
|
||||
}
|
||||
if (!widths_.empty()) {
|
||||
primvars.emplace_back(pxr::HdTokens->widths, interpolation, pxr::HdPrimvarRoleTokens->none);
|
||||
}
|
||||
}
|
||||
else if (interpolation == pxr::HdInterpolationConstant) {
|
||||
if (!uvs_.empty()) {
|
||||
primvars.emplace_back(pxr::HdPrimvarRoleTokens->textureCoordinate,
|
||||
interpolation,
|
||||
pxr::HdPrimvarRoleTokens->textureCoordinate);
|
||||
}
|
||||
}
|
||||
return primvars;
|
||||
}
|
||||
|
||||
void CurvesData::write_materials()
|
||||
{
|
||||
Object *object = (Object *)id;
|
||||
Material *mat = nullptr;
|
||||
/* TODO: Using only first material. Add support for multimaterial. */
|
||||
if (BKE_object_material_count_eval(object) > 0) {
|
||||
mat = BKE_object_material_get_eval(object, 0);
|
||||
}
|
||||
mat_data_ = get_or_create_material(mat);
|
||||
}
|
||||
|
||||
void CurvesData::write_curves(Curves *curves)
|
||||
{
|
||||
curve_vertex_counts_.clear();
|
||||
widths_.clear();
|
||||
vertices_.clear();
|
||||
|
||||
const float *radii = (const float *)CustomData_get_layer_named(
|
||||
&curves->geometry.point_data, CD_PROP_FLOAT, "radius");
|
||||
const float(*positions)[3] = (const float(*)[3])CustomData_get_layer_named(
|
||||
&curves->geometry.point_data, CD_PROP_FLOAT3, "position");
|
||||
|
||||
vertices_.reserve(curves->geometry.curve_num);
|
||||
|
||||
for (int i = 0; i < curves->geometry.curve_num; i++) {
|
||||
int first_point_index = *(curves->geometry.curve_offsets + i);
|
||||
int num_points = *(curves->geometry.curve_offsets + i + 1) - first_point_index;
|
||||
curve_vertex_counts_.push_back(num_points);
|
||||
|
||||
/* Set radius similar to Cycles if isn't set */
|
||||
for (int j = 0; j < num_points; j++) {
|
||||
int ind = first_point_index + j;
|
||||
widths_.push_back(radii ? radii[ind] * 2 : 0.01f);
|
||||
vertices_.push_back(pxr::GfVec3f(positions[ind][0], positions[ind][1], positions[ind][2]));
|
||||
}
|
||||
}
|
||||
write_uv_maps(curves);
|
||||
}
|
||||
|
||||
void CurvesData::write_uv_maps(Curves *curves)
|
||||
{
|
||||
uvs_.clear();
|
||||
|
||||
const float(*uvs)[2] = (const float(*)[2])CustomData_get_layer_named(
|
||||
&curves->geometry.curve_data, CD_PROP_FLOAT2, "surface_uv_coordinate");
|
||||
if (uvs) {
|
||||
for (int i = 0; i < curves->geometry.curve_num; i++) {
|
||||
uvs_.push_back(pxr::GfVec2f(uvs[i][0], uvs[i][1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::io::hydra
|
52
source/blender/io/usd/hydra/curves.h
Normal file
52
source/blender/io/usd/hydra/curves.h
Normal file
@ -0,0 +1,52 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pxr/base/vt/array.h>
|
||||
#include <pxr/imaging/hd/sceneDelegate.h>
|
||||
|
||||
#include "DNA_curves_types.h"
|
||||
|
||||
#include "BLI_set.hh"
|
||||
|
||||
#include "BKE_duplilist.h"
|
||||
|
||||
#include "material.h"
|
||||
#include "object.h"
|
||||
|
||||
namespace blender::io::hydra {
|
||||
|
||||
class CurvesData : public ObjectData {
|
||||
private:
|
||||
pxr::VtIntArray curve_vertex_counts_;
|
||||
pxr::VtVec3fArray vertices_;
|
||||
pxr::VtVec2fArray uvs_;
|
||||
pxr::VtFloatArray widths_;
|
||||
|
||||
MaterialData *mat_data_ = nullptr;
|
||||
|
||||
public:
|
||||
CurvesData(HydraSceneDelegate *scene_delegate, Object *object, pxr::SdfPath const &prim_id);
|
||||
|
||||
void init() override;
|
||||
void insert() override;
|
||||
void remove() override;
|
||||
void update() override;
|
||||
|
||||
pxr::VtValue get_data(pxr::TfToken const &key) const override;
|
||||
pxr::SdfPath material_id() const override;
|
||||
void available_materials(Set<pxr::SdfPath> &paths) const override;
|
||||
|
||||
pxr::HdBasisCurvesTopology topology() const;
|
||||
pxr::HdPrimvarDescriptorVector primvar_descriptors(pxr::HdInterpolation interpolation) const;
|
||||
|
||||
protected:
|
||||
void write_materials() override;
|
||||
|
||||
private:
|
||||
void write_curves(Curves *curves);
|
||||
void write_uv_maps(Curves *curves);
|
||||
};
|
||||
|
||||
} // namespace blender::io::hydra
|
543
source/blender/io/usd/hydra/hydra_scene_delegate.cc
Normal file
543
source/blender/io/usd/hydra/hydra_scene_delegate.cc
Normal file
@ -0,0 +1,543 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#include "hydra_scene_delegate.h"
|
||||
|
||||
#include <bitset>
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BLI_set.hh"
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
namespace blender::io::hydra {
|
||||
|
||||
CLG_LOGREF_DECLARE_GLOBAL(LOG_HYDRA_SCENE, "hydra.scene");
|
||||
|
||||
bool HydraSceneDelegate::ShadingSettings::operator==(const ShadingSettings &other)
|
||||
{
|
||||
bool ret = use_scene_lights == other.use_scene_lights &&
|
||||
use_scene_world == other.use_scene_world;
|
||||
if (ret && !use_scene_world) {
|
||||
/* compare studiolight settings when studiolight is using */
|
||||
ret = studiolight_name == other.studiolight_name &&
|
||||
studiolight_rotation == other.studiolight_rotation &&
|
||||
studiolight_intensity == other.studiolight_intensity;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
HydraSceneDelegate::HydraSceneDelegate(pxr::HdRenderIndex *parent_index,
|
||||
pxr::SdfPath const &delegate_id)
|
||||
: HdSceneDelegate(parent_index, delegate_id)
|
||||
{
|
||||
instancer_data_ = std::make_unique<InstancerData>(this, instancer_prim_id());
|
||||
}
|
||||
|
||||
pxr::HdMeshTopology HydraSceneDelegate::GetMeshTopology(pxr::SdfPath const &id)
|
||||
{
|
||||
CLOG_INFO(LOG_HYDRA_SCENE, 3, "%s", id.GetText());
|
||||
MeshData *m_data = mesh_data(id);
|
||||
return m_data->topology(id);
|
||||
}
|
||||
|
||||
pxr::HdBasisCurvesTopology HydraSceneDelegate::GetBasisCurvesTopology(pxr::SdfPath const &id)
|
||||
{
|
||||
CLOG_INFO(LOG_HYDRA_SCENE, 3, "%s", id.GetText());
|
||||
CurvesData *c_data = curves_data(id);
|
||||
return c_data->topology();
|
||||
};
|
||||
|
||||
pxr::GfMatrix4d HydraSceneDelegate::GetTransform(pxr::SdfPath const &id)
|
||||
{
|
||||
CLOG_INFO(LOG_HYDRA_SCENE, 3, "%s", id.GetText());
|
||||
InstancerData *i_data = instancer_data(id, true);
|
||||
if (i_data) {
|
||||
return i_data->transform(id);
|
||||
}
|
||||
ObjectData *obj_data = object_data(id);
|
||||
if (obj_data) {
|
||||
return obj_data->transform;
|
||||
}
|
||||
return pxr::GfMatrix4d();
|
||||
}
|
||||
|
||||
pxr::VtValue HydraSceneDelegate::Get(pxr::SdfPath const &id, pxr::TfToken const &key)
|
||||
{
|
||||
CLOG_INFO(LOG_HYDRA_SCENE, 3, "%s, %s", id.GetText(), key.GetText());
|
||||
ObjectData *obj_data = object_data(id);
|
||||
if (obj_data) {
|
||||
return obj_data->get_data(id, key);
|
||||
}
|
||||
MaterialData *mat_data = material_data(id);
|
||||
if (mat_data) {
|
||||
return mat_data->get_data(key);
|
||||
}
|
||||
InstancerData *i_data = instancer_data(id);
|
||||
if (i_data) {
|
||||
return i_data->get_data(key);
|
||||
}
|
||||
return pxr::VtValue();
|
||||
}
|
||||
|
||||
pxr::VtValue HydraSceneDelegate::GetLightParamValue(pxr::SdfPath const &id,
|
||||
pxr::TfToken const &key)
|
||||
{
|
||||
CLOG_INFO(LOG_HYDRA_SCENE, 3, "%s, %s", id.GetText(), key.GetText());
|
||||
LightData *l_data = light_data(id);
|
||||
if (l_data) {
|
||||
return l_data->get_data(key);
|
||||
}
|
||||
return pxr::VtValue();
|
||||
}
|
||||
|
||||
pxr::HdPrimvarDescriptorVector HydraSceneDelegate::GetPrimvarDescriptors(
|
||||
pxr::SdfPath const &id, pxr::HdInterpolation interpolation)
|
||||
{
|
||||
CLOG_INFO(LOG_HYDRA_SCENE, 3, "%s, %d", id.GetText(), interpolation);
|
||||
MeshData *m_data = mesh_data(id);
|
||||
if (m_data) {
|
||||
return m_data->primvar_descriptors(interpolation);
|
||||
}
|
||||
CurvesData *c_data = curves_data(id);
|
||||
if (c_data) {
|
||||
return c_data->primvar_descriptors(interpolation);
|
||||
}
|
||||
InstancerData *i_data = instancer_data(id);
|
||||
if (i_data) {
|
||||
return i_data->primvar_descriptors(interpolation);
|
||||
}
|
||||
return pxr::HdPrimvarDescriptorVector();
|
||||
}
|
||||
|
||||
pxr::SdfPath HydraSceneDelegate::GetMaterialId(pxr::SdfPath const &rprim_id)
|
||||
{
|
||||
CLOG_INFO(LOG_HYDRA_SCENE, 3, "%s", rprim_id.GetText());
|
||||
ObjectData *obj_data = object_data(rprim_id);
|
||||
if (obj_data) {
|
||||
return obj_data->material_id(rprim_id);
|
||||
}
|
||||
return pxr::SdfPath();
|
||||
}
|
||||
|
||||
pxr::VtValue HydraSceneDelegate::GetMaterialResource(pxr::SdfPath const &id)
|
||||
{
|
||||
CLOG_INFO(LOG_HYDRA_SCENE, 3, "%s", id.GetText());
|
||||
MaterialData *mat_data = material_data(id);
|
||||
if (mat_data) {
|
||||
return mat_data->get_material_resource();
|
||||
}
|
||||
return pxr::VtValue();
|
||||
}
|
||||
|
||||
bool HydraSceneDelegate::GetVisible(pxr::SdfPath const &id)
|
||||
{
|
||||
CLOG_INFO(LOG_HYDRA_SCENE, 3, "%s", id.GetText());
|
||||
if (id == world_prim_id()) {
|
||||
return true;
|
||||
}
|
||||
InstancerData *i_data = instancer_data(id, true);
|
||||
if (i_data) {
|
||||
return true;
|
||||
}
|
||||
return object_data(id)->visible;
|
||||
}
|
||||
|
||||
bool HydraSceneDelegate::GetDoubleSided(pxr::SdfPath const &id)
|
||||
{
|
||||
CLOG_INFO(LOG_HYDRA_SCENE, 3, "%s", id.GetText());
|
||||
return mesh_data(id)->double_sided(id);
|
||||
}
|
||||
|
||||
pxr::HdCullStyle HydraSceneDelegate::GetCullStyle(pxr::SdfPath const &id)
|
||||
{
|
||||
CLOG_INFO(LOG_HYDRA_SCENE, 3, "%s", id.GetText());
|
||||
return mesh_data(id)->cull_style(id);
|
||||
}
|
||||
|
||||
pxr::SdfPath HydraSceneDelegate::GetInstancerId(pxr::SdfPath const &prim_id)
|
||||
{
|
||||
CLOG_INFO(LOG_HYDRA_SCENE, 3, "%s", prim_id.GetText());
|
||||
InstancerData *i_data = instancer_data(prim_id, true);
|
||||
if (i_data && mesh_data(prim_id)) {
|
||||
return i_data->prim_id;
|
||||
}
|
||||
return pxr::SdfPath();
|
||||
}
|
||||
|
||||
pxr::SdfPathVector HydraSceneDelegate::GetInstancerPrototypes(pxr::SdfPath const &instancer_id)
|
||||
{
|
||||
CLOG_INFO(LOG_HYDRA_SCENE, 3, "%s", instancer_id.GetText());
|
||||
InstancerData *i_data = instancer_data(instancer_id);
|
||||
return i_data->prototypes();
|
||||
}
|
||||
|
||||
pxr::VtIntArray HydraSceneDelegate::GetInstanceIndices(pxr::SdfPath const &instancer_id,
|
||||
pxr::SdfPath const &prototype_id)
|
||||
{
|
||||
CLOG_INFO(LOG_HYDRA_SCENE, 3, "%s, %s", instancer_id.GetText(), prototype_id.GetText());
|
||||
InstancerData *i_data = instancer_data(instancer_id);
|
||||
return i_data->indices(prototype_id);
|
||||
}
|
||||
|
||||
pxr::GfMatrix4d HydraSceneDelegate::GetInstancerTransform(pxr::SdfPath const &instancer_id)
|
||||
{
|
||||
CLOG_INFO(LOG_HYDRA_SCENE, 3, "%s", instancer_id.GetText());
|
||||
InstancerData *i_data = instancer_data(instancer_id);
|
||||
return i_data->transform(instancer_id);
|
||||
}
|
||||
|
||||
pxr::HdVolumeFieldDescriptorVector HydraSceneDelegate::GetVolumeFieldDescriptors(
|
||||
pxr::SdfPath const &volume_id)
|
||||
{
|
||||
CLOG_INFO(LOG_HYDRA_SCENE, 3, "%s", volume_id.GetText());
|
||||
VolumeData *v_data = volume_data(volume_id);
|
||||
return v_data->field_descriptors();
|
||||
}
|
||||
|
||||
void HydraSceneDelegate::populate(Depsgraph *deps, View3D *v3d)
|
||||
{
|
||||
bool is_populated = depsgraph != nullptr;
|
||||
|
||||
depsgraph = deps;
|
||||
bmain = DEG_get_bmain(deps);
|
||||
scene = DEG_get_input_scene(depsgraph);
|
||||
view3d = v3d;
|
||||
|
||||
if (is_populated) {
|
||||
check_updates();
|
||||
}
|
||||
else {
|
||||
set_light_shading_settings();
|
||||
set_world_shading_settings();
|
||||
update_collection();
|
||||
update_world();
|
||||
}
|
||||
}
|
||||
|
||||
void HydraSceneDelegate::clear()
|
||||
{
|
||||
for (auto &obj_data : objects_.values()) {
|
||||
obj_data->remove();
|
||||
}
|
||||
objects_.clear();
|
||||
instancer_data_->remove();
|
||||
for (auto &mat_data : materials_.values()) {
|
||||
mat_data->remove();
|
||||
}
|
||||
materials_.clear();
|
||||
|
||||
depsgraph = nullptr;
|
||||
bmain = nullptr;
|
||||
scene = nullptr;
|
||||
view3d = nullptr;
|
||||
}
|
||||
|
||||
pxr::SdfPath HydraSceneDelegate::prim_id(ID *id, const char *prefix) const
|
||||
{
|
||||
/* Making id of object in form like <prefix>_<pointer in 16 hex digits format> */
|
||||
char name[32];
|
||||
snprintf(name, sizeof(name), "%s_%p", prefix, id);
|
||||
return GetDelegateID().AppendElementString(name);
|
||||
}
|
||||
|
||||
pxr::SdfPath HydraSceneDelegate::object_prim_id(Object *object) const
|
||||
{
|
||||
return prim_id((ID *)object, "O");
|
||||
}
|
||||
|
||||
pxr::SdfPath HydraSceneDelegate::material_prim_id(Material *mat) const
|
||||
{
|
||||
return prim_id((ID *)mat, "M");
|
||||
}
|
||||
|
||||
pxr::SdfPath HydraSceneDelegate::instancer_prim_id() const
|
||||
{
|
||||
return GetDelegateID().AppendElementString("Instancer");
|
||||
}
|
||||
|
||||
pxr::SdfPath HydraSceneDelegate::world_prim_id() const
|
||||
{
|
||||
return GetDelegateID().AppendElementString("World");
|
||||
}
|
||||
|
||||
ObjectData *HydraSceneDelegate::object_data(pxr::SdfPath const &id) const
|
||||
{
|
||||
if (id == world_prim_id()) {
|
||||
return world_data_.get();
|
||||
}
|
||||
auto name = id.GetName();
|
||||
pxr::SdfPath p_id = (STRPREFIX(name.c_str(), "SM_") || STRPREFIX(name.c_str(), "VF_")) ?
|
||||
id.GetParentPath() :
|
||||
id;
|
||||
auto obj_data = objects_.lookup_ptr(p_id);
|
||||
if (obj_data) {
|
||||
return obj_data->get();
|
||||
}
|
||||
|
||||
InstancerData *i_data = instancer_data(p_id, true);
|
||||
if (i_data) {
|
||||
return i_data->object_data(id);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MeshData *HydraSceneDelegate::mesh_data(pxr::SdfPath const &id) const
|
||||
{
|
||||
return dynamic_cast<MeshData *>(object_data(id));
|
||||
}
|
||||
|
||||
CurvesData *HydraSceneDelegate::curves_data(pxr::SdfPath const &id) const
|
||||
{
|
||||
return dynamic_cast<CurvesData *>(object_data(id));
|
||||
}
|
||||
|
||||
VolumeData *HydraSceneDelegate::volume_data(pxr::SdfPath const &id) const
|
||||
{
|
||||
return dynamic_cast<VolumeData *>(object_data(id));
|
||||
}
|
||||
|
||||
LightData *HydraSceneDelegate::light_data(pxr::SdfPath const &id) const
|
||||
{
|
||||
return dynamic_cast<LightData *>(object_data(id));
|
||||
}
|
||||
|
||||
MaterialData *HydraSceneDelegate::material_data(pxr::SdfPath const &id) const
|
||||
{
|
||||
auto mat_data = materials_.lookup_ptr(id);
|
||||
if (!mat_data) {
|
||||
return nullptr;
|
||||
}
|
||||
return mat_data->get();
|
||||
}
|
||||
|
||||
InstancerData *HydraSceneDelegate::instancer_data(pxr::SdfPath const &id, bool child_id) const
|
||||
{
|
||||
pxr::SdfPath p_id;
|
||||
if (child_id) {
|
||||
/* Getting instancer path id from child Mesh instance (consist with 3 path elements) and
|
||||
* Light instance (consist with 4 path elements) */
|
||||
int n = id.GetPathElementCount();
|
||||
if (n == 3) {
|
||||
p_id = id.GetParentPath();
|
||||
}
|
||||
else if (n == 4) {
|
||||
p_id = id.GetParentPath().GetParentPath();
|
||||
}
|
||||
}
|
||||
else {
|
||||
p_id = id;
|
||||
}
|
||||
|
||||
if (instancer_data_ && p_id == instancer_data_->prim_id) {
|
||||
return instancer_data_.get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void HydraSceneDelegate::update_world()
|
||||
{
|
||||
if (!world_data_) {
|
||||
if (!shading_settings.use_scene_world || (shading_settings.use_scene_world && scene->world)) {
|
||||
world_data_ = std::make_unique<WorldData>(this, world_prim_id());
|
||||
world_data_->init();
|
||||
world_data_->insert();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!shading_settings.use_scene_world || (shading_settings.use_scene_world && scene->world)) {
|
||||
world_data_->update();
|
||||
}
|
||||
else {
|
||||
world_data_->remove();
|
||||
world_data_ = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HydraSceneDelegate::check_updates()
|
||||
{
|
||||
bool do_update_collection = false;
|
||||
bool do_update_world = false;
|
||||
|
||||
if (set_world_shading_settings()) {
|
||||
do_update_world = true;
|
||||
}
|
||||
|
||||
if (set_light_shading_settings()) {
|
||||
do_update_collection = true;
|
||||
}
|
||||
|
||||
DEGIDIterData data = {0};
|
||||
data.graph = depsgraph;
|
||||
data.only_updated = true;
|
||||
ITER_BEGIN (DEG_iterator_ids_begin, DEG_iterator_ids_next, DEG_iterator_ids_end, &data, ID *, id)
|
||||
{
|
||||
CLOG_INFO(LOG_HYDRA_SCENE,
|
||||
0,
|
||||
"Update: %s [%s]",
|
||||
id->name,
|
||||
std::bitset<32>(id->recalc).to_string().c_str());
|
||||
|
||||
switch (GS(id->name)) {
|
||||
case ID_OB: {
|
||||
do_update_collection = true;
|
||||
} break;
|
||||
|
||||
case ID_MA: {
|
||||
MaterialData *mat_data = material_data(material_prim_id((Material *)id));
|
||||
if (mat_data) {
|
||||
mat_data->update();
|
||||
}
|
||||
} break;
|
||||
|
||||
case ID_WO: {
|
||||
if (shading_settings.use_scene_world && id->recalc & ID_RECALC_SHADING) {
|
||||
do_update_world = true;
|
||||
}
|
||||
} break;
|
||||
|
||||
case ID_SCE: {
|
||||
if ((id->recalc & ID_RECALC_COPY_ON_WRITE && !(id->recalc & ID_RECALC_SELECT)) ||
|
||||
id->recalc & (ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_BASE_FLAGS))
|
||||
{
|
||||
do_update_collection = true;
|
||||
}
|
||||
if (id->recalc & ID_RECALC_AUDIO_VOLUME &&
|
||||
((scene->world && !world_data_) || (!scene->world && world_data_)))
|
||||
{
|
||||
do_update_world = true;
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
ITER_END;
|
||||
|
||||
if (do_update_world) {
|
||||
update_world();
|
||||
}
|
||||
if (do_update_collection) {
|
||||
update_collection();
|
||||
}
|
||||
}
|
||||
|
||||
void HydraSceneDelegate::update_collection()
|
||||
{
|
||||
Set<std::string> available_objects;
|
||||
|
||||
DEGObjectIterSettings settings = {0};
|
||||
settings.depsgraph = depsgraph;
|
||||
settings.flags = DEG_OBJECT_ITER_FOR_RENDER_ENGINE_FLAGS;
|
||||
DEGObjectIterData data = {0};
|
||||
data.settings = &settings;
|
||||
data.graph = settings.depsgraph;
|
||||
data.flag = settings.flags;
|
||||
|
||||
instancer_data_->pre_update();
|
||||
|
||||
ITER_BEGIN (DEG_iterator_objects_begin,
|
||||
DEG_iterator_objects_next,
|
||||
DEG_iterator_objects_end,
|
||||
&data,
|
||||
Object *,
|
||||
object)
|
||||
{
|
||||
if (data.dupli_object_current) {
|
||||
DupliObject *dupli = data.dupli_object_current;
|
||||
if (!ObjectData::is_supported(dupli->ob) ||
|
||||
!ObjectData::is_visible(this, data.dupli_parent, OB_VISIBLE_INSTANCES) ||
|
||||
(!shading_settings.use_scene_lights && object->type == OB_LAMP))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
instancer_data_->update_instance(dupli);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ObjectData::is_supported(object) || !ObjectData::is_visible(this, object) ||
|
||||
(!shading_settings.use_scene_lights && object->type == OB_LAMP))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
available_objects.add(object_prim_id(object).GetName());
|
||||
|
||||
pxr::SdfPath id = object_prim_id(object);
|
||||
ObjectData *obj_data = object_data(id);
|
||||
if (obj_data) {
|
||||
obj_data->update();
|
||||
}
|
||||
else {
|
||||
obj_data = objects_.lookup_or_add(id, ObjectData::create(this, object, id)).get();
|
||||
obj_data->insert();
|
||||
}
|
||||
}
|
||||
ITER_END;
|
||||
|
||||
instancer_data_->post_update();
|
||||
|
||||
/* Remove unused objects */
|
||||
objects_.remove_if([&](auto item) {
|
||||
bool ret = !available_objects.contains(item.key.GetName());
|
||||
if (ret) {
|
||||
item.value->remove();
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
|
||||
/* Remove unused materials */
|
||||
Set<pxr::SdfPath> available_materials;
|
||||
for (auto &val : objects_.values()) {
|
||||
MeshData *m_data = dynamic_cast<MeshData *>(val.get());
|
||||
if (m_data) {
|
||||
m_data->available_materials(available_materials);
|
||||
}
|
||||
CurvesData *c_data = dynamic_cast<CurvesData *>(val.get());
|
||||
if (c_data) {
|
||||
c_data->available_materials(available_materials);
|
||||
}
|
||||
VolumeData *v_data = dynamic_cast<VolumeData *>(val.get());
|
||||
if (v_data) {
|
||||
v_data->available_materials(available_materials);
|
||||
}
|
||||
}
|
||||
instancer_data_->available_materials(available_materials);
|
||||
|
||||
materials_.remove_if([&](auto item) {
|
||||
bool ret = !available_materials.contains(item.key);
|
||||
if (ret) {
|
||||
item.value->remove();
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
}
|
||||
|
||||
bool HydraSceneDelegate::set_light_shading_settings()
|
||||
{
|
||||
if (!view3d) {
|
||||
return false;
|
||||
}
|
||||
ShadingSettings prev_settings(shading_settings);
|
||||
shading_settings.use_scene_lights = V3D_USES_SCENE_LIGHTS(view3d);
|
||||
return !(shading_settings == prev_settings);
|
||||
}
|
||||
|
||||
bool HydraSceneDelegate::set_world_shading_settings()
|
||||
{
|
||||
if (!view3d) {
|
||||
return false;
|
||||
}
|
||||
ShadingSettings prev_settings(shading_settings);
|
||||
shading_settings.use_scene_world = V3D_USES_SCENE_WORLD(view3d);
|
||||
shading_settings.studiolight_name = view3d->shading.lookdev_light;
|
||||
shading_settings.studiolight_rotation = view3d->shading.studiolight_rot_z;
|
||||
shading_settings.studiolight_intensity = view3d->shading.studiolight_intensity;
|
||||
return !(shading_settings == prev_settings);
|
||||
}
|
||||
|
||||
} // namespace blender::io::hydra
|
112
source/blender/io/usd/hydra/hydra_scene_delegate.h
Normal file
112
source/blender/io/usd/hydra/hydra_scene_delegate.h
Normal file
@ -0,0 +1,112 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pxr/base/gf/vec2f.h>
|
||||
#include <pxr/imaging/hd/sceneDelegate.h>
|
||||
|
||||
#include "BLI_map.hh"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "curves.h"
|
||||
#include "instancer.h"
|
||||
#include "light.h"
|
||||
#include "mesh.h"
|
||||
#include "object.h"
|
||||
#include "volume.h"
|
||||
#include "volume_modifier.h"
|
||||
#include "world.h"
|
||||
|
||||
struct Depsgraph;
|
||||
struct Main;
|
||||
struct Scene;
|
||||
struct View3D;
|
||||
|
||||
namespace blender::io::hydra {
|
||||
|
||||
extern struct CLG_LogRef *LOG_HYDRA_SCENE;
|
||||
|
||||
class Engine;
|
||||
|
||||
class HydraSceneDelegate : public pxr::HdSceneDelegate {
|
||||
friend ObjectData; /* has access to materials */
|
||||
friend MaterialData; /* has access to objects and instancers */
|
||||
|
||||
public:
|
||||
struct ShadingSettings {
|
||||
bool use_scene_lights = true;
|
||||
bool use_scene_world = true;
|
||||
std::string studiolight_name;
|
||||
float studiolight_rotation;
|
||||
float studiolight_intensity;
|
||||
|
||||
bool operator==(const ShadingSettings &other);
|
||||
};
|
||||
|
||||
Depsgraph *depsgraph = nullptr;
|
||||
View3D *view3d = nullptr;
|
||||
Main *bmain = nullptr;
|
||||
Scene *scene = nullptr;
|
||||
ShadingSettings shading_settings;
|
||||
|
||||
private:
|
||||
ObjectDataMap objects_;
|
||||
MaterialDataMap materials_;
|
||||
std::unique_ptr<InstancerData> instancer_data_;
|
||||
std::unique_ptr<WorldData> world_data_;
|
||||
|
||||
public:
|
||||
HydraSceneDelegate(pxr::HdRenderIndex *parent_index, pxr::SdfPath const &delegate_id);
|
||||
~HydraSceneDelegate() override = default;
|
||||
|
||||
/* Delegate methods */
|
||||
pxr::HdMeshTopology GetMeshTopology(pxr::SdfPath const &id) override;
|
||||
pxr::HdBasisCurvesTopology GetBasisCurvesTopology(pxr::SdfPath const &id) override;
|
||||
pxr::GfMatrix4d GetTransform(pxr::SdfPath const &id) override;
|
||||
pxr::VtValue Get(pxr::SdfPath const &id, pxr::TfToken const &key) override;
|
||||
pxr::VtValue GetLightParamValue(pxr::SdfPath const &id, pxr::TfToken const &key) override;
|
||||
pxr::HdPrimvarDescriptorVector GetPrimvarDescriptors(
|
||||
pxr::SdfPath const &id, pxr::HdInterpolation interpolation) override;
|
||||
pxr::SdfPath GetMaterialId(pxr::SdfPath const &rprim_id) override;
|
||||
pxr::VtValue GetMaterialResource(pxr::SdfPath const &material_id) override;
|
||||
bool GetVisible(pxr::SdfPath const &id) override;
|
||||
bool GetDoubleSided(pxr::SdfPath const &id) override;
|
||||
pxr::HdCullStyle GetCullStyle(pxr::SdfPath const &id) override;
|
||||
pxr::SdfPath GetInstancerId(pxr::SdfPath const &prim_id) override;
|
||||
pxr::SdfPathVector GetInstancerPrototypes(pxr::SdfPath const &instancer_id) override;
|
||||
pxr::VtIntArray GetInstanceIndices(pxr::SdfPath const &instancer_id,
|
||||
pxr::SdfPath const &prototype_id) override;
|
||||
pxr::GfMatrix4d GetInstancerTransform(pxr::SdfPath const &instancer_id) override;
|
||||
pxr::HdVolumeFieldDescriptorVector GetVolumeFieldDescriptors(
|
||||
pxr::SdfPath const &volume_id) override;
|
||||
|
||||
void populate(Depsgraph *depsgraph, View3D *v3d);
|
||||
void clear();
|
||||
|
||||
private:
|
||||
pxr::SdfPath prim_id(ID *id, const char *prefix) const;
|
||||
pxr::SdfPath object_prim_id(Object *object) const;
|
||||
pxr::SdfPath material_prim_id(Material *mat) const;
|
||||
pxr::SdfPath instancer_prim_id() const;
|
||||
pxr::SdfPath world_prim_id() const;
|
||||
|
||||
ObjectData *object_data(pxr::SdfPath const &id) const;
|
||||
MeshData *mesh_data(pxr::SdfPath const &id) const;
|
||||
CurvesData *curves_data(pxr::SdfPath const &id) const;
|
||||
VolumeData *volume_data(pxr::SdfPath const &id) const;
|
||||
LightData *light_data(pxr::SdfPath const &id) const;
|
||||
MaterialData *material_data(pxr::SdfPath const &id) const;
|
||||
InstancerData *instancer_data(pxr::SdfPath const &id, bool child_id = false) const;
|
||||
|
||||
void update_world();
|
||||
void check_updates();
|
||||
void update_collection();
|
||||
bool set_light_shading_settings();
|
||||
bool set_world_shading_settings();
|
||||
};
|
||||
|
||||
} // namespace blender::io::hydra
|
15
source/blender/io/usd/hydra/id.cc
Normal file
15
source/blender/io/usd/hydra/id.cc
Normal file
@ -0,0 +1,15 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#include "id.h"
|
||||
|
||||
#include "BKE_lib_id.h"
|
||||
|
||||
namespace blender::io::hydra {
|
||||
|
||||
IdData::IdData(HydraSceneDelegate *scene_delegate, ID *id, pxr::SdfPath const &prim_id)
|
||||
: id(id), prim_id(prim_id), scene_delegate_(scene_delegate)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace blender::io::hydra
|
63
source/blender/io/usd/hydra/id.h
Normal file
63
source/blender/io/usd/hydra/id.h
Normal file
@ -0,0 +1,63 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pxr/base/tf/token.h>
|
||||
#include <pxr/base/vt/value.h>
|
||||
#include <pxr/usd/sdf/path.h>
|
||||
|
||||
#include "DNA_ID.h"
|
||||
|
||||
#include "BLI_hash.hh"
|
||||
|
||||
template<> struct blender::DefaultHash<pxr::SdfPath> {
|
||||
uint64_t operator()(const pxr::SdfPath &value) const
|
||||
{
|
||||
return (uint64_t)value.GetHash();
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct blender::DefaultHash<pxr::TfToken> {
|
||||
uint64_t operator()(const pxr::TfToken &value) const
|
||||
{
|
||||
return (uint64_t)value.Hash();
|
||||
}
|
||||
};
|
||||
|
||||
namespace blender::io::hydra {
|
||||
|
||||
class HydraSceneDelegate;
|
||||
|
||||
class IdData {
|
||||
public:
|
||||
ID *id;
|
||||
pxr::SdfPath prim_id;
|
||||
|
||||
protected:
|
||||
HydraSceneDelegate *scene_delegate_;
|
||||
|
||||
public:
|
||||
IdData(HydraSceneDelegate *scene_delegate, ID *id, pxr::SdfPath const &prim_id);
|
||||
virtual ~IdData() = default;
|
||||
|
||||
virtual void init() = 0;
|
||||
virtual void insert() = 0;
|
||||
virtual void remove() = 0;
|
||||
virtual void update() = 0;
|
||||
|
||||
virtual pxr::VtValue get_data(pxr::TfToken const &key) const = 0;
|
||||
};
|
||||
|
||||
#define ID_LOG(level, msg, ...) \
|
||||
CLOG_INFO(LOG_HYDRA_SCENE, level, "%s: " msg, prim_id.GetText(), ##__VA_ARGS__);
|
||||
|
||||
#define ID_LOGN(level, msg, ...) \
|
||||
CLOG_INFO(LOG_HYDRA_SCENE, \
|
||||
level, \
|
||||
"%s (%s): " msg, \
|
||||
prim_id.GetText(), \
|
||||
id ? id->name : "", \
|
||||
##__VA_ARGS__);
|
||||
|
||||
} // namespace blender::io::hydra
|
123
source/blender/io/usd/hydra/image.cc
Normal file
123
source/blender/io/usd/hydra/image.cc
Normal file
@ -0,0 +1,123 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#include "image.h"
|
||||
|
||||
#include <pxr/imaging/hio/imageRegistry.h>
|
||||
|
||||
#include "BLI_fileops.h"
|
||||
#include "BLI_path_util.h"
|
||||
|
||||
#include "BKE_appdir.h"
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_image_format.h"
|
||||
#include "BKE_image_save.h"
|
||||
|
||||
#include "IMB_imbuf.h"
|
||||
#include "IMB_imbuf_types.h"
|
||||
|
||||
#include "hydra_scene_delegate.h"
|
||||
|
||||
namespace blender::io::hydra {
|
||||
|
||||
static std::string get_cache_file(const std::string &file_name, bool mkdir = true)
|
||||
{
|
||||
char dir_path[FILE_MAX];
|
||||
BLI_path_join(dir_path, sizeof(dir_path), BKE_tempdir_session(), "hydra", "image_cache");
|
||||
if (mkdir) {
|
||||
BLI_dir_create_recursive(dir_path);
|
||||
}
|
||||
|
||||
char file_path[FILE_MAX];
|
||||
BLI_path_join(file_path, sizeof(file_path), dir_path, file_name.c_str());
|
||||
return file_path;
|
||||
}
|
||||
|
||||
static std::string cache_image_file(
|
||||
Main *bmain, Scene *scene, Image *image, ImageUser *iuser, bool check_exist)
|
||||
{
|
||||
std::string file_path;
|
||||
ImageSaveOptions opts;
|
||||
if (BKE_image_save_options_init(&opts, bmain, scene, image, iuser, false, false)) {
|
||||
char file_name[32];
|
||||
const char *r_ext = BLI_path_extension_or_end(image->id.name);
|
||||
if (!pxr::HioImageRegistry::GetInstance().IsSupportedImageFile(image->id.name)) {
|
||||
BKE_image_path_ext_from_imformat(&scene->r.im_format, &r_ext);
|
||||
opts.im_format = scene->r.im_format;
|
||||
}
|
||||
|
||||
snprintf(file_name, sizeof(file_name), "img_%p%s", image, r_ext);
|
||||
|
||||
file_path = get_cache_file(file_name);
|
||||
if (check_exist && BLI_exists(file_path.c_str())) {
|
||||
return file_path;
|
||||
}
|
||||
|
||||
opts.save_copy = true;
|
||||
STRNCPY(opts.filepath, file_path.c_str());
|
||||
if (BKE_image_save(nullptr, bmain, image, iuser, &opts)) {
|
||||
CLOG_INFO(LOG_HYDRA_SCENE, 1, "%s -> %s", image->id.name, file_path.c_str());
|
||||
}
|
||||
else {
|
||||
CLOG_ERROR(LOG_HYDRA_SCENE, "Can't save %s", file_path.c_str());
|
||||
file_path = "";
|
||||
}
|
||||
}
|
||||
BKE_image_save_options_free(&opts);
|
||||
return file_path;
|
||||
}
|
||||
|
||||
std::string cache_or_get_image_file(Main *bmain, Scene *scene, Image *image, ImageUser *iuser)
|
||||
{
|
||||
std::string file_path;
|
||||
if (image->source == IMA_SRC_GENERATED) {
|
||||
file_path = cache_image_file(bmain, scene, image, iuser, false);
|
||||
}
|
||||
else if (BKE_image_has_packedfile(image)) {
|
||||
file_path = cache_image_file(bmain, scene, image, iuser, true);
|
||||
}
|
||||
else {
|
||||
char str[FILE_MAX];
|
||||
BKE_image_user_file_path_ex(bmain, iuser, image, str, false, true);
|
||||
file_path = str;
|
||||
|
||||
if (!pxr::HioImageRegistry::GetInstance().IsSupportedImageFile(file_path)) {
|
||||
file_path = cache_image_file(bmain, scene, image, iuser, true);
|
||||
}
|
||||
}
|
||||
|
||||
CLOG_INFO(LOG_HYDRA_SCENE, 1, "%s -> %s", image->id.name, file_path.c_str());
|
||||
return file_path;
|
||||
}
|
||||
|
||||
std::string cache_image_color(float color[4])
|
||||
{
|
||||
char name[128];
|
||||
snprintf(name,
|
||||
sizeof(name),
|
||||
"color_%02d%02d%02d.hdr",
|
||||
int(color[0] * 255),
|
||||
int(color[1] * 255),
|
||||
int(color[2] * 255));
|
||||
std::string file_path = get_cache_file(name);
|
||||
if (BLI_exists(file_path.c_str())) {
|
||||
return file_path;
|
||||
}
|
||||
|
||||
ImBuf *ibuf = IMB_allocImBuf(4, 4, 32, IB_rectfloat);
|
||||
IMB_rectfill(ibuf, color);
|
||||
ibuf->ftype = IMB_FTYPE_RADHDR;
|
||||
|
||||
if (IMB_saveiff(ibuf, file_path.c_str(), IB_rectfloat)) {
|
||||
CLOG_INFO(LOG_HYDRA_SCENE, 1, "%s", file_path.c_str());
|
||||
}
|
||||
else {
|
||||
CLOG_ERROR(LOG_HYDRA_SCENE, "Can't save %s", file_path.c_str());
|
||||
file_path = "";
|
||||
}
|
||||
IMB_freeImBuf(ibuf);
|
||||
|
||||
return file_path;
|
||||
}
|
||||
|
||||
} // namespace blender::io::hydra
|
18
source/blender/io/usd/hydra/image.h
Normal file
18
source/blender/io/usd/hydra/image.h
Normal file
@ -0,0 +1,18 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
struct Main;
|
||||
struct Scene;
|
||||
struct Image;
|
||||
struct ImageUser;
|
||||
|
||||
namespace blender::io::hydra {
|
||||
|
||||
std::string cache_or_get_image_file(Main *bmain, Scene *Scene, Image *image, ImageUser *iuser);
|
||||
std::string cache_image_color(float color[4]);
|
||||
|
||||
} // namespace blender::io::hydra
|
286
source/blender/io/usd/hydra/instancer.cc
Normal file
286
source/blender/io/usd/hydra/instancer.cc
Normal file
@ -0,0 +1,286 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#include "instancer.h"
|
||||
|
||||
#include <pxr/base/gf/vec2f.h>
|
||||
#include <pxr/imaging/hd/light.h>
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "hydra_scene_delegate.h"
|
||||
|
||||
namespace blender::io::hydra {
|
||||
|
||||
InstancerData::InstancerData(HydraSceneDelegate *scene_delegate, pxr::SdfPath const &prim_id)
|
||||
: IdData(scene_delegate, nullptr, prim_id)
|
||||
{
|
||||
}
|
||||
|
||||
void InstancerData::init() {}
|
||||
|
||||
void InstancerData::insert() {}
|
||||
|
||||
void InstancerData::remove()
|
||||
{
|
||||
CLOG_INFO(LOG_HYDRA_SCENE, 1, "%s", prim_id.GetText());
|
||||
for (auto &m_inst : mesh_instances_.values()) {
|
||||
m_inst.data->remove();
|
||||
}
|
||||
if (!mesh_instances_.is_empty()) {
|
||||
scene_delegate_->GetRenderIndex().RemoveInstancer(prim_id);
|
||||
}
|
||||
mesh_instances_.clear();
|
||||
|
||||
for (auto &l_inst : nonmesh_instances_.values()) {
|
||||
l_inst.transforms.clear();
|
||||
update_nonmesh_instance(l_inst);
|
||||
}
|
||||
nonmesh_instances_.clear();
|
||||
}
|
||||
|
||||
void InstancerData::update() {}
|
||||
|
||||
pxr::VtValue InstancerData::get_data(pxr::TfToken const &key) const
|
||||
{
|
||||
ID_LOG(3, "%s", key.GetText());
|
||||
if (key == pxr::HdInstancerTokens->instanceTransform) {
|
||||
return pxr::VtValue(mesh_transforms_);
|
||||
}
|
||||
return pxr::VtValue();
|
||||
}
|
||||
|
||||
pxr::GfMatrix4d InstancerData::transform(pxr::SdfPath const &id) const
|
||||
{
|
||||
NonmeshInstance *nm_inst = nonmesh_instance(id);
|
||||
if (nm_inst) {
|
||||
return nm_inst->transforms[nonmesh_prim_id_index(id)];
|
||||
}
|
||||
|
||||
/* Mesh instance transform must be identity */
|
||||
return pxr::GfMatrix4d(1.0);
|
||||
}
|
||||
|
||||
pxr::HdPrimvarDescriptorVector InstancerData::primvar_descriptors(
|
||||
pxr::HdInterpolation interpolation) const
|
||||
{
|
||||
pxr::HdPrimvarDescriptorVector primvars;
|
||||
if (interpolation == pxr::HdInterpolationInstance) {
|
||||
primvars.emplace_back(
|
||||
pxr::HdInstancerTokens->instanceTransform, interpolation, pxr::HdPrimvarRoleTokens->none);
|
||||
}
|
||||
return primvars;
|
||||
}
|
||||
|
||||
pxr::VtIntArray InstancerData::indices(pxr::SdfPath const &id) const
|
||||
{
|
||||
return mesh_instance(id)->indices;
|
||||
}
|
||||
|
||||
ObjectData *InstancerData::object_data(pxr::SdfPath const &id) const
|
||||
{
|
||||
MeshInstance *m_inst = mesh_instance(id);
|
||||
if (m_inst) {
|
||||
return m_inst->data.get();
|
||||
}
|
||||
NonmeshInstance *nm_inst = nonmesh_instance(id);
|
||||
if (nm_inst) {
|
||||
return nm_inst->data.get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
pxr::SdfPathVector InstancerData::prototypes() const
|
||||
{
|
||||
pxr::SdfPathVector paths;
|
||||
for (auto &m_inst : mesh_instances_.values()) {
|
||||
for (auto &p : m_inst.data->submesh_paths()) {
|
||||
paths.push_back(p);
|
||||
}
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
void InstancerData::available_materials(Set<pxr::SdfPath> &paths) const
|
||||
{
|
||||
for (auto &m_inst : mesh_instances_.values()) {
|
||||
m_inst.data->available_materials(paths);
|
||||
}
|
||||
for (auto &l_inst : nonmesh_instances_.values()) {
|
||||
l_inst.data->available_materials(paths);
|
||||
}
|
||||
}
|
||||
|
||||
void InstancerData::update_double_sided(MaterialData *mat_data)
|
||||
{
|
||||
for (auto &m_inst : mesh_instances_.values()) {
|
||||
m_inst.data->update_double_sided(mat_data);
|
||||
}
|
||||
}
|
||||
|
||||
void InstancerData::pre_update()
|
||||
{
|
||||
mesh_transforms_.clear();
|
||||
for (auto &m_inst : mesh_instances_.values()) {
|
||||
m_inst.indices.clear();
|
||||
}
|
||||
for (auto &l_inst : nonmesh_instances_.values()) {
|
||||
l_inst.transforms.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void InstancerData::update_instance(DupliObject *dupli)
|
||||
{
|
||||
Object *object = dupli->ob;
|
||||
pxr::SdfPath p_id = object_prim_id(object);
|
||||
if (ObjectData::is_mesh(object)) {
|
||||
MeshInstance *m_inst = mesh_instance(p_id);
|
||||
if (!m_inst) {
|
||||
m_inst = &mesh_instances_.lookup_or_add_default(p_id);
|
||||
m_inst->data = std::make_unique<MeshData>(scene_delegate_, object, p_id);
|
||||
m_inst->data->init();
|
||||
m_inst->data->insert();
|
||||
}
|
||||
else {
|
||||
m_inst->data->update();
|
||||
}
|
||||
ID_LOG(2, "Mesh %s %d", m_inst->data->id->name, (int)mesh_transforms_.size());
|
||||
m_inst->indices.push_back(mesh_transforms_.size());
|
||||
mesh_transforms_.push_back(gf_matrix_from_transform(dupli->mat));
|
||||
}
|
||||
else {
|
||||
NonmeshInstance *nm_inst = nonmesh_instance(p_id);
|
||||
if (!nm_inst) {
|
||||
nm_inst = &nonmesh_instances_.lookup_or_add_default(p_id);
|
||||
nm_inst->data = ObjectData::create(scene_delegate_, object, p_id);
|
||||
}
|
||||
ID_LOG(2, "Light %s %d", nm_inst->data->id->name, (int)nm_inst->transforms.size());
|
||||
nm_inst->transforms.push_back(gf_matrix_from_transform(dupli->mat));
|
||||
}
|
||||
}
|
||||
|
||||
void InstancerData::post_update()
|
||||
{
|
||||
/* Remove mesh intances without indices */
|
||||
mesh_instances_.remove_if([&](auto item) {
|
||||
bool res = item.value.indices.empty();
|
||||
if (res) {
|
||||
item.value.data->remove();
|
||||
}
|
||||
return res;
|
||||
});
|
||||
|
||||
/* Update light intances and remove instances without transforms */
|
||||
for (auto &l_inst : nonmesh_instances_.values()) {
|
||||
update_nonmesh_instance(l_inst);
|
||||
}
|
||||
nonmesh_instances_.remove_if([&](auto item) { return item.value.transforms.empty(); });
|
||||
|
||||
/* Insert/remove/update instancer in RenderIndex */
|
||||
pxr::HdRenderIndex &index = scene_delegate_->GetRenderIndex();
|
||||
if (mesh_instances_.is_empty()) {
|
||||
/* Important: removing instancer when nonmesh_instances_ are empty too */
|
||||
if (index.HasInstancer(prim_id) && nonmesh_instances_.is_empty()) {
|
||||
index.RemoveInstancer(prim_id);
|
||||
ID_LOG(1, "Remove instancer");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (index.HasInstancer(prim_id)) {
|
||||
index.GetChangeTracker().MarkInstancerDirty(prim_id, pxr::HdChangeTracker::AllDirty);
|
||||
ID_LOG(1, "Update instancer");
|
||||
}
|
||||
else {
|
||||
index.InsertInstancer(scene_delegate_, prim_id);
|
||||
ID_LOG(1, "Insert instancer");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pxr::SdfPath InstancerData::object_prim_id(Object *object) const
|
||||
{
|
||||
/* Making id of object in form like <prefix>_<pointer in 16 hex digits format> */
|
||||
char name[32];
|
||||
snprintf(name, sizeof(name), "O_%p", object);
|
||||
return prim_id.AppendElementString(name);
|
||||
}
|
||||
|
||||
pxr::SdfPath InstancerData::nonmesh_prim_id(pxr::SdfPath const &prim_id, int index) const
|
||||
{
|
||||
char name[16];
|
||||
snprintf(name, sizeof(name), "NM_%08d", index);
|
||||
return prim_id.AppendElementString(name);
|
||||
}
|
||||
|
||||
int InstancerData::nonmesh_prim_id_index(pxr::SdfPath const &id) const
|
||||
{
|
||||
int index;
|
||||
sscanf(id.GetName().c_str(), "NM_%d", &index);
|
||||
return index;
|
||||
}
|
||||
|
||||
void InstancerData::update_nonmesh_instance(NonmeshInstance &nm_inst)
|
||||
{
|
||||
ObjectData *obj_data = nm_inst.data.get();
|
||||
pxr::SdfPath prev_id = nm_inst.data->prim_id;
|
||||
int i;
|
||||
|
||||
/* Remove old light instances */
|
||||
while (nm_inst.count > nm_inst.transforms.size()) {
|
||||
--nm_inst.count;
|
||||
obj_data->prim_id = nonmesh_prim_id(prev_id, nm_inst.count);
|
||||
obj_data->remove();
|
||||
}
|
||||
|
||||
/* Update current light instances */
|
||||
LightData *l_data = dynamic_cast<LightData *>(obj_data);
|
||||
if (l_data && l_data->prim_type((Light *)((Object *)l_data->id)->data) != l_data->prim_type_) {
|
||||
/* Special case: recreate instances when prim_type was changed */
|
||||
for (i = 0; i < nm_inst.count; ++i) {
|
||||
obj_data->prim_id = nonmesh_prim_id(prev_id, i);
|
||||
obj_data->remove();
|
||||
}
|
||||
l_data->init();
|
||||
for (i = 0; i < nm_inst.count; ++i) {
|
||||
obj_data->prim_id = nonmesh_prim_id(prev_id, i);
|
||||
obj_data->insert();
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (i = 0; i < nm_inst.count; ++i) {
|
||||
obj_data->prim_id = nonmesh_prim_id(prev_id, i);
|
||||
obj_data->update();
|
||||
}
|
||||
}
|
||||
|
||||
/* Add new light instances */
|
||||
while (nm_inst.count < nm_inst.transforms.size()) {
|
||||
obj_data->prim_id = nonmesh_prim_id(prev_id, nm_inst.count);
|
||||
obj_data->insert();
|
||||
++nm_inst.count;
|
||||
}
|
||||
|
||||
obj_data->prim_id = prev_id;
|
||||
}
|
||||
|
||||
InstancerData::MeshInstance *InstancerData::mesh_instance(pxr::SdfPath const &id) const
|
||||
{
|
||||
auto m_inst = mesh_instances_.lookup_ptr(id.GetPathElementCount() == 4 ? id.GetParentPath() :
|
||||
id);
|
||||
if (!m_inst) {
|
||||
return nullptr;
|
||||
}
|
||||
return const_cast<MeshInstance *>(m_inst);
|
||||
}
|
||||
|
||||
InstancerData::NonmeshInstance *InstancerData::nonmesh_instance(pxr::SdfPath const &id) const
|
||||
{
|
||||
auto nm_inst = nonmesh_instances_.lookup_ptr(id.GetPathElementCount() == 4 ? id.GetParentPath() :
|
||||
id);
|
||||
if (!nm_inst) {
|
||||
return nullptr;
|
||||
}
|
||||
return const_cast<NonmeshInstance *>(nm_inst);
|
||||
}
|
||||
|
||||
} // namespace blender::io::hydra
|
66
source/blender/io/usd/hydra/instancer.h
Normal file
66
source/blender/io/usd/hydra/instancer.h
Normal file
@ -0,0 +1,66 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_set.hh"
|
||||
|
||||
#include "mesh.h"
|
||||
|
||||
namespace blender::io::hydra {
|
||||
|
||||
class InstancerData : public IdData {
|
||||
struct MeshInstance {
|
||||
std::unique_ptr<MeshData> data;
|
||||
pxr::VtIntArray indices;
|
||||
};
|
||||
|
||||
struct NonmeshInstance {
|
||||
std::unique_ptr<ObjectData> data;
|
||||
pxr::VtMatrix4dArray transforms;
|
||||
int count = 0;
|
||||
};
|
||||
|
||||
private:
|
||||
Map<pxr::SdfPath, MeshInstance> mesh_instances_;
|
||||
Map<pxr::SdfPath, NonmeshInstance> nonmesh_instances_;
|
||||
pxr::VtMatrix4dArray mesh_transforms_;
|
||||
|
||||
public:
|
||||
InstancerData(HydraSceneDelegate *scene_delegate, pxr::SdfPath const &prim_id);
|
||||
|
||||
void init() override;
|
||||
void insert() override;
|
||||
void remove() override;
|
||||
void update() override;
|
||||
|
||||
pxr::VtValue get_data(pxr::TfToken const &key) const override;
|
||||
pxr::GfMatrix4d transform(pxr::SdfPath const &id) const;
|
||||
pxr::HdPrimvarDescriptorVector primvar_descriptors(pxr::HdInterpolation interpolation) const;
|
||||
pxr::VtIntArray indices(pxr::SdfPath const &id) const;
|
||||
ObjectData *object_data(pxr::SdfPath const &id) const;
|
||||
pxr::SdfPathVector prototypes() const;
|
||||
void available_materials(Set<pxr::SdfPath> &paths) const;
|
||||
void update_double_sided(MaterialData *mat_data);
|
||||
|
||||
/* Following update functions are working together:
|
||||
* pre_update()
|
||||
* update_instance()
|
||||
* update_instance()
|
||||
* ...
|
||||
* post_update() */
|
||||
void pre_update();
|
||||
void update_instance(DupliObject *dupli);
|
||||
void post_update();
|
||||
|
||||
private:
|
||||
pxr::SdfPath object_prim_id(Object *object) const;
|
||||
pxr::SdfPath nonmesh_prim_id(pxr::SdfPath const &prim_id, int index) const;
|
||||
int nonmesh_prim_id_index(pxr::SdfPath const &id) const;
|
||||
void update_nonmesh_instance(NonmeshInstance &inst);
|
||||
MeshInstance *mesh_instance(pxr::SdfPath const &id) const;
|
||||
NonmeshInstance *nonmesh_instance(pxr::SdfPath const &id) const;
|
||||
};
|
||||
|
||||
} // namespace blender::io::hydra
|
177
source/blender/io/usd/hydra/light.cc
Normal file
177
source/blender/io/usd/hydra/light.cc
Normal file
@ -0,0 +1,177 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#include "light.h"
|
||||
|
||||
#include <pxr/imaging/hd/light.h>
|
||||
#include <pxr/imaging/hd/tokens.h>
|
||||
#include <pxr/usd/usdLux/tokens.h>
|
||||
|
||||
#include "DNA_light_types.h"
|
||||
|
||||
#include "BLI_math_rotation.h"
|
||||
|
||||
#include "hydra_scene_delegate.h"
|
||||
|
||||
namespace blender::io::hydra {
|
||||
|
||||
LightData::LightData(HydraSceneDelegate *scene_delegate,
|
||||
Object *object,
|
||||
pxr::SdfPath const &prim_id)
|
||||
: ObjectData(scene_delegate, object, prim_id)
|
||||
{
|
||||
}
|
||||
|
||||
void LightData::init()
|
||||
{
|
||||
ID_LOGN(1, "");
|
||||
|
||||
Light *light = (Light *)((Object *)id)->data;
|
||||
data_.clear();
|
||||
|
||||
switch (light->type) {
|
||||
case LA_AREA: {
|
||||
switch (light->area_shape) {
|
||||
case LA_AREA_SQUARE:
|
||||
data_[pxr::HdLightTokens->width] = light->area_size;
|
||||
data_[pxr::HdLightTokens->height] = light->area_size;
|
||||
break;
|
||||
case LA_AREA_RECT:
|
||||
data_[pxr::HdLightTokens->width] = light->area_size;
|
||||
data_[pxr::HdLightTokens->height] = light->area_sizey;
|
||||
break;
|
||||
case LA_AREA_DISK:
|
||||
data_[pxr::HdLightTokens->radius] = light->area_size / 2.0f;
|
||||
break;
|
||||
case LA_AREA_ELLIPSE:
|
||||
/* An ellipse light deteriorates into a disk light. */
|
||||
data_[pxr::HdLightTokens->radius] = (light->area_size + light->area_sizey) / 4.0f;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LA_LOCAL:
|
||||
case LA_SPOT: {
|
||||
data_[pxr::HdLightTokens->radius] = light->radius;
|
||||
if (light->radius == 0.0f) {
|
||||
data_[pxr::UsdLuxTokens->treatAsPoint] = true;
|
||||
}
|
||||
|
||||
if (light->type == LA_SPOT) {
|
||||
data_[pxr::UsdLuxTokens->inputsShapingConeAngle] = RAD2DEGF(light->spotsize * 0.5f);
|
||||
data_[pxr::UsdLuxTokens->inputsShapingConeSoftness] = light->spotblend;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LA_SUN: {
|
||||
data_[pxr::HdLightTokens->angle] = RAD2DEGF(light->sun_angle * 0.5f);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float intensity;
|
||||
if (light->type == LA_SUN) {
|
||||
/* Unclear why, but approximately matches Karma. */
|
||||
intensity = light->energy / 4.0f;
|
||||
}
|
||||
else {
|
||||
/* Convert from radiant flux to intensity. */
|
||||
intensity = light->energy / M_PI;
|
||||
}
|
||||
|
||||
data_[pxr::HdLightTokens->intensity] = intensity;
|
||||
data_[pxr::HdLightTokens->exposure] = 0.0f;
|
||||
data_[pxr::HdLightTokens->color] = pxr::GfVec3f(light->r, light->g, light->b);
|
||||
data_[pxr::HdLightTokens->diffuse] = light->diff_fac;
|
||||
data_[pxr::HdLightTokens->specular] = light->spec_fac;
|
||||
data_[pxr::HdLightTokens->normalize] = true;
|
||||
|
||||
prim_type_ = prim_type(light);
|
||||
|
||||
write_transform();
|
||||
}
|
||||
|
||||
void LightData::insert()
|
||||
{
|
||||
ID_LOGN(1, "");
|
||||
scene_delegate_->GetRenderIndex().InsertSprim(prim_type_, scene_delegate_, prim_id);
|
||||
}
|
||||
|
||||
void LightData::remove()
|
||||
{
|
||||
ID_LOG(1, "");
|
||||
scene_delegate_->GetRenderIndex().RemoveSprim(prim_type_, prim_id);
|
||||
}
|
||||
|
||||
void LightData::update()
|
||||
{
|
||||
Object *object = (Object *)id;
|
||||
Light *light = (Light *)object->data;
|
||||
pxr::HdDirtyBits bits = pxr::HdLight::Clean;
|
||||
if (id->recalc & ID_RECALC_GEOMETRY || light->id.recalc & ID_RECALC_GEOMETRY) {
|
||||
if (prim_type(light) != prim_type_) {
|
||||
remove();
|
||||
init();
|
||||
insert();
|
||||
return;
|
||||
}
|
||||
init();
|
||||
bits = pxr::HdLight::AllDirty;
|
||||
}
|
||||
else if (id->recalc & ID_RECALC_TRANSFORM) {
|
||||
write_transform();
|
||||
bits = pxr::HdLight::DirtyTransform;
|
||||
}
|
||||
if (bits != pxr::HdChangeTracker::Clean) {
|
||||
scene_delegate_->GetRenderIndex().GetChangeTracker().MarkSprimDirty(prim_id, bits);
|
||||
ID_LOGN(1, "");
|
||||
}
|
||||
}
|
||||
|
||||
pxr::VtValue LightData::get_data(pxr::TfToken const &key) const
|
||||
{
|
||||
ID_LOGN(3, "%s", key.GetText());
|
||||
auto it = data_.find(key);
|
||||
if (it != data_.end()) {
|
||||
return pxr::VtValue(it->second);
|
||||
}
|
||||
|
||||
return pxr::VtValue();
|
||||
}
|
||||
|
||||
pxr::TfToken LightData::prim_type(Light *light)
|
||||
{
|
||||
switch (light->type) {
|
||||
case LA_AREA:
|
||||
switch (light->area_shape) {
|
||||
case LA_AREA_SQUARE:
|
||||
case LA_AREA_RECT:
|
||||
return pxr::HdPrimTypeTokens->rectLight;
|
||||
|
||||
case LA_AREA_DISK:
|
||||
case LA_AREA_ELLIPSE:
|
||||
return pxr::HdPrimTypeTokens->diskLight;
|
||||
|
||||
default:
|
||||
return pxr::HdPrimTypeTokens->rectLight;
|
||||
}
|
||||
break;
|
||||
|
||||
case LA_LOCAL:
|
||||
case LA_SPOT:
|
||||
return pxr::HdPrimTypeTokens->sphereLight;
|
||||
|
||||
case LA_SUN:
|
||||
return pxr::HdPrimTypeTokens->distantLight;
|
||||
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
return pxr::TfToken();
|
||||
}
|
||||
|
||||
} // namespace blender::io::hydra
|
39
source/blender/io/usd/hydra/light.h
Normal file
39
source/blender/io/usd/hydra/light.h
Normal file
@ -0,0 +1,39 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pxr/base/tf/hashmap.h>
|
||||
#include <pxr/usd/sdf/assetPath.h>
|
||||
#include <pxr/usd/sdf/path.h>
|
||||
|
||||
#include "BKE_light.h"
|
||||
|
||||
#include "object.h"
|
||||
|
||||
namespace blender::io::hydra {
|
||||
|
||||
class InstancerData;
|
||||
|
||||
class LightData : public ObjectData {
|
||||
friend InstancerData;
|
||||
|
||||
protected:
|
||||
std::map<pxr::TfToken, pxr::VtValue> data_;
|
||||
pxr::TfToken prim_type_;
|
||||
|
||||
public:
|
||||
LightData(HydraSceneDelegate *scene_delegate, Object *object, pxr::SdfPath const &prim_id);
|
||||
|
||||
void init() override;
|
||||
void insert() override;
|
||||
void remove() override;
|
||||
void update() override;
|
||||
|
||||
pxr::VtValue get_data(pxr::TfToken const &key) const override;
|
||||
|
||||
protected:
|
||||
pxr::TfToken prim_type(Light *light);
|
||||
};
|
||||
|
||||
} // namespace blender::io::hydra
|
87
source/blender/io/usd/hydra/material.cc
Normal file
87
source/blender/io/usd/hydra/material.cc
Normal file
@ -0,0 +1,87 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#include "material.h"
|
||||
|
||||
#include <Python.h>
|
||||
#include <unicodeobject.h>
|
||||
|
||||
#include <pxr/imaging/hd/material.h>
|
||||
#include <pxr/imaging/hd/renderDelegate.h>
|
||||
#include <pxr/imaging/hd/tokens.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_material.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_prototypes.h"
|
||||
#include "RNA_types.h"
|
||||
|
||||
#include "bpy_rna.h"
|
||||
|
||||
#include "hydra_scene_delegate.h"
|
||||
|
||||
namespace blender::io::hydra {
|
||||
|
||||
MaterialData::MaterialData(HydraSceneDelegate *scene_delegate,
|
||||
Material *material,
|
||||
pxr::SdfPath const &prim_id)
|
||||
: IdData(scene_delegate, (ID *)material, prim_id)
|
||||
{
|
||||
}
|
||||
|
||||
void MaterialData::init()
|
||||
{
|
||||
ID_LOGN(1, "");
|
||||
double_sided = (((Material *)id)->blend_flag & MA_BL_CULL_BACKFACE) == 0;
|
||||
}
|
||||
|
||||
void MaterialData::insert()
|
||||
{
|
||||
ID_LOGN(1, "");
|
||||
scene_delegate_->GetRenderIndex().InsertSprim(
|
||||
pxr::HdPrimTypeTokens->material, scene_delegate_, prim_id);
|
||||
}
|
||||
|
||||
void MaterialData::remove()
|
||||
{
|
||||
ID_LOG(1, "");
|
||||
scene_delegate_->GetRenderIndex().RemoveSprim(pxr::HdPrimTypeTokens->material, prim_id);
|
||||
}
|
||||
|
||||
void MaterialData::update()
|
||||
{
|
||||
ID_LOGN(1, "");
|
||||
bool prev_double_sided = double_sided;
|
||||
init();
|
||||
scene_delegate_->GetRenderIndex().GetChangeTracker().MarkSprimDirty(prim_id,
|
||||
pxr::HdMaterial::AllDirty);
|
||||
if (prev_double_sided != double_sided) {
|
||||
for (auto &obj_data : scene_delegate_->objects_.values()) {
|
||||
MeshData *m_data = dynamic_cast<MeshData *>(obj_data.get());
|
||||
if (m_data) {
|
||||
m_data->update_double_sided(this);
|
||||
}
|
||||
}
|
||||
scene_delegate_->instancer_data_->update_double_sided(this);
|
||||
}
|
||||
}
|
||||
|
||||
pxr::VtValue MaterialData::get_data(pxr::TfToken const & /* key */) const
|
||||
{
|
||||
return pxr::VtValue();
|
||||
}
|
||||
|
||||
pxr::VtValue MaterialData::get_material_resource() const
|
||||
{
|
||||
return pxr::VtValue();
|
||||
}
|
||||
|
||||
pxr::HdCullStyle MaterialData::cull_style() const
|
||||
{
|
||||
return double_sided ? pxr::HdCullStyle::HdCullStyleNothing : pxr::HdCullStyle::HdCullStyleBack;
|
||||
}
|
||||
|
||||
} // namespace blender::io::hydra
|
38
source/blender/io/usd/hydra/material.h
Normal file
38
source/blender/io/usd/hydra/material.h
Normal file
@ -0,0 +1,38 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pxr/imaging/hd/enums.h>
|
||||
#include <pxr/usd/sdf/assetPath.h>
|
||||
#include <pxr/usd/sdf/path.h>
|
||||
|
||||
#include "DNA_material_types.h"
|
||||
|
||||
#include "BLI_map.hh"
|
||||
|
||||
#include "id.h"
|
||||
|
||||
namespace blender::io::hydra {
|
||||
|
||||
class MaterialData : public IdData {
|
||||
public:
|
||||
MaterialData(HydraSceneDelegate *scene_delegate,
|
||||
Material *material,
|
||||
pxr::SdfPath const &prim_id);
|
||||
|
||||
void init() override;
|
||||
void insert() override;
|
||||
void remove() override;
|
||||
void update() override;
|
||||
|
||||
pxr::VtValue get_data(pxr::TfToken const &key) const override;
|
||||
pxr::VtValue get_material_resource() const;
|
||||
pxr::HdCullStyle cull_style() const;
|
||||
|
||||
bool double_sided = true;
|
||||
};
|
||||
|
||||
using MaterialDataMap = Map<pxr::SdfPath, std::unique_ptr<MaterialData>>;
|
||||
|
||||
} // namespace blender::io::hydra
|
322
source/blender/io/usd/hydra/mesh.cc
Normal file
322
source/blender/io/usd/hydra/mesh.cc
Normal file
@ -0,0 +1,322 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#include <pxr/base/gf/vec2f.h>
|
||||
#include <pxr/base/tf/staticTokens.h>
|
||||
#include <pxr/imaging/hd/tokens.h>
|
||||
|
||||
#include "BKE_material.h"
|
||||
#include "BKE_mesh.hh"
|
||||
#include "BKE_mesh_runtime.hh"
|
||||
|
||||
#include "hydra_scene_delegate.h"
|
||||
#include "mesh.h"
|
||||
|
||||
PXR_NAMESPACE_OPEN_SCOPE
|
||||
TF_DEFINE_PRIVATE_TOKENS(tokens_, (st));
|
||||
PXR_NAMESPACE_CLOSE_SCOPE
|
||||
|
||||
namespace blender::io::hydra {
|
||||
|
||||
MeshData::MeshData(HydraSceneDelegate *scene_delegate, Object *object, pxr::SdfPath const &prim_id)
|
||||
: ObjectData(scene_delegate, object, prim_id)
|
||||
{
|
||||
}
|
||||
|
||||
void MeshData::init()
|
||||
{
|
||||
ID_LOGN(1, "");
|
||||
|
||||
Object *object = (Object *)id;
|
||||
Mesh *mesh = BKE_object_to_mesh(nullptr, object, false);
|
||||
if (mesh) {
|
||||
write_submeshes(mesh);
|
||||
}
|
||||
BKE_object_to_mesh_clear(object);
|
||||
|
||||
write_transform();
|
||||
write_materials();
|
||||
}
|
||||
|
||||
void MeshData::insert()
|
||||
{
|
||||
ID_LOGN(1, "");
|
||||
update_prims();
|
||||
}
|
||||
|
||||
void MeshData::remove()
|
||||
{
|
||||
ID_LOG(1, "");
|
||||
submeshes_.clear();
|
||||
update_prims();
|
||||
}
|
||||
|
||||
void MeshData::update()
|
||||
{
|
||||
Object *object = (Object *)id;
|
||||
if ((id->recalc & ID_RECALC_GEOMETRY) || (((ID *)object->data)->recalc & ID_RECALC_GEOMETRY)) {
|
||||
init();
|
||||
update_prims();
|
||||
return;
|
||||
}
|
||||
|
||||
pxr::HdDirtyBits bits = pxr::HdChangeTracker::Clean;
|
||||
if (id->recalc & ID_RECALC_SHADING) {
|
||||
write_materials();
|
||||
bits |= pxr::HdChangeTracker::DirtyMaterialId | pxr::HdChangeTracker::DirtyDoubleSided;
|
||||
}
|
||||
if (id->recalc & ID_RECALC_TRANSFORM) {
|
||||
write_transform();
|
||||
bits |= pxr::HdChangeTracker::DirtyTransform;
|
||||
}
|
||||
|
||||
if (bits == pxr::HdChangeTracker::Clean) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < submeshes_.size(); ++i) {
|
||||
scene_delegate_->GetRenderIndex().GetChangeTracker().MarkRprimDirty(submesh_prim_id(i), bits);
|
||||
ID_LOGN(1, "%d", i);
|
||||
}
|
||||
}
|
||||
|
||||
pxr::VtValue MeshData::get_data(pxr::TfToken const & /* key */) const
|
||||
{
|
||||
return pxr::VtValue();
|
||||
}
|
||||
|
||||
pxr::VtValue MeshData::get_data(pxr::SdfPath const &id, pxr::TfToken const &key) const
|
||||
{
|
||||
if (key == pxr::HdTokens->normals) {
|
||||
return pxr::VtValue(submesh(id).normals);
|
||||
}
|
||||
if (key == pxr::tokens_->st) {
|
||||
return pxr::VtValue(submesh(id).uvs);
|
||||
}
|
||||
if (key == pxr::HdTokens->points) {
|
||||
return pxr::VtValue(submesh(id).vertices);
|
||||
}
|
||||
|
||||
return get_data(key);
|
||||
}
|
||||
|
||||
pxr::SdfPath MeshData::material_id(pxr::SdfPath const &id) const
|
||||
{
|
||||
const SubMesh &sm = submesh(id);
|
||||
if (!sm.mat_data) {
|
||||
return pxr::SdfPath();
|
||||
}
|
||||
return sm.mat_data->prim_id;
|
||||
}
|
||||
|
||||
void MeshData::available_materials(Set<pxr::SdfPath> &paths) const
|
||||
{
|
||||
for (auto &sm : submeshes_) {
|
||||
if (sm.mat_data && !sm.mat_data->prim_id.IsEmpty()) {
|
||||
paths.add(sm.mat_data->prim_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pxr::HdMeshTopology MeshData::topology(pxr::SdfPath const &id) const
|
||||
{
|
||||
const SubMesh &sm = submesh(id);
|
||||
return pxr::HdMeshTopology(pxr::PxOsdOpenSubdivTokens->none,
|
||||
pxr::HdTokens->rightHanded,
|
||||
sm.face_vertex_counts,
|
||||
sm.face_vertex_indices);
|
||||
}
|
||||
|
||||
pxr::HdPrimvarDescriptorVector MeshData::primvar_descriptors(
|
||||
pxr::HdInterpolation interpolation) const
|
||||
{
|
||||
pxr::HdPrimvarDescriptorVector primvars;
|
||||
if (interpolation == pxr::HdInterpolationVertex) {
|
||||
primvars.emplace_back(pxr::HdTokens->points, interpolation, pxr::HdPrimvarRoleTokens->point);
|
||||
}
|
||||
else if (interpolation == pxr::HdInterpolationFaceVarying) {
|
||||
if (!submeshes_[0].normals.empty()) {
|
||||
primvars.emplace_back(
|
||||
pxr::HdTokens->normals, interpolation, pxr::HdPrimvarRoleTokens->normal);
|
||||
}
|
||||
if (!submeshes_[0].uvs.empty()) {
|
||||
primvars.emplace_back(
|
||||
pxr::tokens_->st, interpolation, pxr::HdPrimvarRoleTokens->textureCoordinate);
|
||||
}
|
||||
}
|
||||
return primvars;
|
||||
}
|
||||
|
||||
pxr::HdCullStyle MeshData::cull_style(pxr::SdfPath const &id) const
|
||||
{
|
||||
const SubMesh &sm = submesh(id);
|
||||
if (sm.mat_data) {
|
||||
return sm.mat_data->cull_style();
|
||||
}
|
||||
return pxr::HdCullStyle::HdCullStyleNothing;
|
||||
}
|
||||
|
||||
bool MeshData::double_sided(pxr::SdfPath const &id) const
|
||||
{
|
||||
const SubMesh &sm = submesh(id);
|
||||
if (sm.mat_data) {
|
||||
return sm.mat_data->double_sided;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MeshData::update_double_sided(MaterialData *mat_data)
|
||||
{
|
||||
for (int i = 0; i < submeshes_.size(); ++i) {
|
||||
if (submeshes_[i].mat_data == mat_data) {
|
||||
scene_delegate_->GetRenderIndex().GetChangeTracker().MarkRprimDirty(
|
||||
submesh_prim_id(i),
|
||||
pxr::HdChangeTracker::DirtyDoubleSided | pxr::HdChangeTracker::DirtyCullStyle);
|
||||
ID_LOGN(1, "%d", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pxr::SdfPathVector MeshData::submesh_paths() const
|
||||
{
|
||||
pxr::SdfPathVector ret;
|
||||
for (int i = 0; i < submeshes_.size(); ++i) {
|
||||
ret.push_back(submesh_prim_id(i));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void MeshData::write_materials()
|
||||
{
|
||||
Object *object = (Object *)id;
|
||||
for (int i = 0; i < submeshes_.size(); ++i) {
|
||||
SubMesh &m = submeshes_[i];
|
||||
Material *mat = BKE_object_material_get_eval(object, m.mat_index + 1);
|
||||
m.mat_data = get_or_create_material(mat);
|
||||
}
|
||||
}
|
||||
|
||||
pxr::SdfPath MeshData::submesh_prim_id(int index) const
|
||||
{
|
||||
char name[16];
|
||||
snprintf(name, sizeof(name), "SM_%04d", index);
|
||||
return prim_id.AppendElementString(name);
|
||||
}
|
||||
|
||||
const MeshData::SubMesh &MeshData::submesh(pxr::SdfPath const &id) const
|
||||
{
|
||||
int index;
|
||||
sscanf(id.GetName().c_str(), "SM_%d", &index);
|
||||
return submeshes_[index];
|
||||
}
|
||||
|
||||
void MeshData::write_submeshes(Mesh *mesh)
|
||||
{
|
||||
submeshes_.clear();
|
||||
|
||||
/* Insert base submeshes */
|
||||
int mat_count = BKE_object_material_count_eval((Object *)id);
|
||||
for (int i = 0; i < std::max(mat_count, 1); ++i) {
|
||||
SubMesh sm;
|
||||
sm.mat_index = i;
|
||||
submeshes_.push_back(sm);
|
||||
}
|
||||
|
||||
/* Fill submeshes data */
|
||||
const int *material_indices = BKE_mesh_material_indices(mesh);
|
||||
|
||||
blender::Span<int> looptri_faces = mesh->looptri_faces();
|
||||
blender::Span<int> corner_verts = mesh->corner_verts();
|
||||
blender::Span<MLoopTri> looptris = mesh->looptris();
|
||||
|
||||
BKE_mesh_calc_normals_split(mesh);
|
||||
const float(*lnors)[3] = (float(*)[3])CustomData_get_layer(&mesh->loop_data, CD_NORMAL);
|
||||
const float(*luvs)[2] = (float(*)[2])CustomData_get_layer(&mesh->loop_data, CD_PROP_FLOAT2);
|
||||
|
||||
for (size_t i = 0; i < looptris.size(); ++i) {
|
||||
int mat_ind = material_indices ? material_indices[looptri_faces[i]] : 0;
|
||||
const MLoopTri < = looptris[i];
|
||||
SubMesh &sm = submeshes_[mat_ind];
|
||||
|
||||
sm.face_vertex_counts.push_back(3);
|
||||
sm.face_vertex_indices.push_back(corner_verts[lt.tri[0]]);
|
||||
sm.face_vertex_indices.push_back(corner_verts[lt.tri[1]]);
|
||||
sm.face_vertex_indices.push_back(corner_verts[lt.tri[2]]);
|
||||
|
||||
if (lnors) {
|
||||
sm.normals.push_back(pxr::GfVec3f(lnors[lt.tri[0]]));
|
||||
sm.normals.push_back(pxr::GfVec3f(lnors[lt.tri[1]]));
|
||||
sm.normals.push_back(pxr::GfVec3f(lnors[lt.tri[2]]));
|
||||
}
|
||||
|
||||
if (luvs) {
|
||||
sm.uvs.push_back(pxr::GfVec2f(luvs[lt.tri[0]]));
|
||||
sm.uvs.push_back(pxr::GfVec2f(luvs[lt.tri[1]]));
|
||||
sm.uvs.push_back(pxr::GfVec2f(luvs[lt.tri[2]]));
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove submeshes without faces */
|
||||
for (auto it = submeshes_.begin(); it != submeshes_.end();) {
|
||||
if (it->face_vertex_counts.empty()) {
|
||||
it = submeshes_.erase(it);
|
||||
}
|
||||
else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
if (submeshes_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* vertices */
|
||||
blender::Span<blender::float3> verts = mesh->vert_positions();
|
||||
pxr::VtVec3fArray vertices(mesh->totvert);
|
||||
int i = 0;
|
||||
for (blender::float3 v : verts) {
|
||||
vertices[i++] = pxr::GfVec3f(v.x, v.y, v.z);
|
||||
}
|
||||
|
||||
if (submeshes_.size() == 1) {
|
||||
submeshes_[0].vertices = std::move(vertices);
|
||||
}
|
||||
else {
|
||||
/* Optimizing submeshes: getting only used vertices, rearranged indices */
|
||||
for (SubMesh &sm : submeshes_) {
|
||||
Vector<int> index_map(vertices.size(), 0);
|
||||
for (int &face_vertex_index : sm.face_vertex_indices) {
|
||||
const int v = face_vertex_index;
|
||||
if (index_map[v] == 0) {
|
||||
sm.vertices.push_back(vertices[v]);
|
||||
index_map[v] = sm.vertices.size();
|
||||
}
|
||||
face_vertex_index = index_map[v] - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MeshData::update_prims()
|
||||
{
|
||||
auto &render_index = scene_delegate_->GetRenderIndex();
|
||||
int i;
|
||||
for (i = 0; i < submeshes_.size(); ++i) {
|
||||
pxr::SdfPath p = submesh_prim_id(i);
|
||||
if (i < submeshes_count_) {
|
||||
render_index.GetChangeTracker().MarkRprimDirty(p, pxr::HdChangeTracker::AllDirty);
|
||||
ID_LOGN(1, "Update %d", i);
|
||||
}
|
||||
else {
|
||||
render_index.InsertRprim(pxr::HdPrimTypeTokens->mesh, scene_delegate_, p);
|
||||
ID_LOGN(1, "Insert %d", i);
|
||||
}
|
||||
}
|
||||
for (; i < submeshes_count_; ++i) {
|
||||
render_index.RemoveRprim(submesh_prim_id(i));
|
||||
ID_LOG(1, "Remove %d", i);
|
||||
}
|
||||
submeshes_count_ = submeshes_.size();
|
||||
}
|
||||
|
||||
} // namespace blender::io::hydra
|
63
source/blender/io/usd/hydra/mesh.h
Normal file
63
source/blender/io/usd/hydra/mesh.h
Normal file
@ -0,0 +1,63 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pxr/base/vt/array.h>
|
||||
#include <pxr/imaging/hd/sceneDelegate.h>
|
||||
|
||||
#include "BLI_set.hh"
|
||||
|
||||
#include "BKE_duplilist.h"
|
||||
|
||||
#include "material.h"
|
||||
#include "object.h"
|
||||
|
||||
namespace blender::io::hydra {
|
||||
|
||||
class MeshData : public ObjectData {
|
||||
struct SubMesh {
|
||||
pxr::VtVec3fArray vertices;
|
||||
pxr::VtIntArray face_vertex_counts;
|
||||
pxr::VtIntArray face_vertex_indices;
|
||||
pxr::VtVec3fArray normals;
|
||||
pxr::VtVec2fArray uvs;
|
||||
int mat_index = 0;
|
||||
MaterialData *mat_data = nullptr;
|
||||
};
|
||||
|
||||
private:
|
||||
std::vector<SubMesh> submeshes_;
|
||||
int submeshes_count_ = 0;
|
||||
|
||||
public:
|
||||
MeshData(HydraSceneDelegate *scene_delegate, Object *object, pxr::SdfPath const &prim_id);
|
||||
|
||||
void init() override;
|
||||
void insert() override;
|
||||
void remove() override;
|
||||
void update() override;
|
||||
|
||||
pxr::VtValue get_data(pxr::TfToken const &key) const override;
|
||||
pxr::VtValue get_data(pxr::SdfPath const &id, pxr::TfToken const &key) const override;
|
||||
pxr::SdfPath material_id(pxr::SdfPath const &id) const override;
|
||||
void available_materials(Set<pxr::SdfPath> &paths) const override;
|
||||
|
||||
pxr::HdMeshTopology topology(pxr::SdfPath const &id) const;
|
||||
pxr::HdPrimvarDescriptorVector primvar_descriptors(pxr::HdInterpolation interpolation) const;
|
||||
pxr::HdCullStyle cull_style(pxr::SdfPath const &id) const;
|
||||
bool double_sided(pxr::SdfPath const &id) const;
|
||||
void update_double_sided(MaterialData *mat_data);
|
||||
pxr::SdfPathVector submesh_paths() const;
|
||||
|
||||
protected:
|
||||
void write_materials() override;
|
||||
|
||||
private:
|
||||
pxr::SdfPath submesh_prim_id(int index) const;
|
||||
const SubMesh &submesh(pxr::SdfPath const &id) const;
|
||||
void write_submeshes(Mesh *mesh);
|
||||
void update_prims();
|
||||
};
|
||||
|
||||
} // namespace blender::io::hydra
|
157
source/blender/io/usd/hydra/object.cc
Normal file
157
source/blender/io/usd/hydra/object.cc
Normal file
@ -0,0 +1,157 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "curves.h"
|
||||
#include "hydra_scene_delegate.h"
|
||||
#include "light.h"
|
||||
#include "mesh.h"
|
||||
#include "object.h"
|
||||
#include "volume.h"
|
||||
|
||||
namespace blender::io::hydra {
|
||||
|
||||
ObjectData::ObjectData(HydraSceneDelegate *scene_delegate,
|
||||
Object *object,
|
||||
pxr::SdfPath const &prim_id)
|
||||
: IdData(scene_delegate, (ID *)object, prim_id), transform(pxr::GfMatrix4d(1.0))
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<ObjectData> ObjectData::create(HydraSceneDelegate *scene_delegate,
|
||||
Object *object,
|
||||
pxr::SdfPath const &prim_id)
|
||||
{
|
||||
std::unique_ptr<ObjectData> obj_data;
|
||||
switch (object->type) {
|
||||
case OB_MESH:
|
||||
case OB_SURF:
|
||||
case OB_FONT:
|
||||
case OB_CURVES_LEGACY:
|
||||
case OB_MBALL:
|
||||
if (VolumeModifierData::is_volume_modifier(object)) {
|
||||
obj_data = std::make_unique<VolumeModifierData>(scene_delegate, object, prim_id);
|
||||
break;
|
||||
}
|
||||
obj_data = std::make_unique<MeshData>(scene_delegate, object, prim_id);
|
||||
break;
|
||||
case OB_CURVES:
|
||||
obj_data = std::make_unique<CurvesData>(scene_delegate, object, prim_id);
|
||||
break;
|
||||
case OB_LAMP:
|
||||
obj_data = std::make_unique<LightData>(scene_delegate, object, prim_id);
|
||||
break;
|
||||
case OB_VOLUME:
|
||||
obj_data = std::make_unique<VolumeData>(scene_delegate, object, prim_id);
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
break;
|
||||
}
|
||||
obj_data->init();
|
||||
return obj_data;
|
||||
}
|
||||
|
||||
bool ObjectData::is_supported(Object *object)
|
||||
{
|
||||
switch (object->type) {
|
||||
case OB_MESH:
|
||||
case OB_SURF:
|
||||
case OB_FONT:
|
||||
case OB_CURVES:
|
||||
case OB_CURVES_LEGACY:
|
||||
case OB_MBALL:
|
||||
case OB_LAMP:
|
||||
case OB_VOLUME:
|
||||
return true;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ObjectData::is_mesh(Object *object)
|
||||
{
|
||||
switch (object->type) {
|
||||
case OB_MESH:
|
||||
case OB_SURF:
|
||||
case OB_FONT:
|
||||
case OB_CURVES_LEGACY:
|
||||
case OB_MBALL:
|
||||
if (VolumeModifierData::is_volume_modifier(object)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ObjectData::is_visible(HydraSceneDelegate *scene_delegate, Object *object, int mode)
|
||||
{
|
||||
eEvaluationMode deg_mode = DEG_get_mode(scene_delegate->depsgraph);
|
||||
bool ret = BKE_object_visibility(object, deg_mode) & mode;
|
||||
if (deg_mode == DAG_EVAL_VIEWPORT) {
|
||||
ret &= BKE_object_is_visible_in_viewport(scene_delegate->view3d, object);
|
||||
}
|
||||
/* Note: visibility for final render we are taking from depsgraph */
|
||||
return ret;
|
||||
}
|
||||
|
||||
pxr::VtValue ObjectData::get_data(pxr::SdfPath const & /* id */, pxr::TfToken const &key) const
|
||||
{
|
||||
return get_data(key);
|
||||
}
|
||||
|
||||
pxr::SdfPath ObjectData::material_id() const
|
||||
{
|
||||
return pxr::SdfPath();
|
||||
}
|
||||
|
||||
pxr::SdfPath ObjectData::material_id(pxr::SdfPath const & /* id */) const
|
||||
{
|
||||
return material_id();
|
||||
}
|
||||
|
||||
void ObjectData::available_materials(Set<pxr::SdfPath> & /* paths */) const {}
|
||||
|
||||
void ObjectData::write_transform()
|
||||
{
|
||||
transform = gf_matrix_from_transform(((Object *)id)->object_to_world);
|
||||
}
|
||||
|
||||
void ObjectData::write_materials() {}
|
||||
|
||||
MaterialData *ObjectData::get_or_create_material(Material *mat)
|
||||
{
|
||||
if (!mat) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
pxr::SdfPath p_id = scene_delegate_->material_prim_id(mat);
|
||||
MaterialData *mat_data = scene_delegate_->material_data(p_id);
|
||||
if (!mat_data) {
|
||||
scene_delegate_->materials_.add_new(
|
||||
p_id, std::make_unique<MaterialData>(scene_delegate_, mat, p_id));
|
||||
mat_data = scene_delegate_->material_data(p_id);
|
||||
mat_data->init();
|
||||
mat_data->insert();
|
||||
}
|
||||
return mat_data;
|
||||
}
|
||||
|
||||
pxr::GfMatrix4d gf_matrix_from_transform(float m[4][4])
|
||||
{
|
||||
pxr::GfMatrix4d ret;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
ret[i][j] = m[i][j];
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace blender::io::hydra
|
55
source/blender/io/usd/hydra/object.h
Normal file
55
source/blender/io/usd/hydra/object.h
Normal file
@ -0,0 +1,55 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pxr/base/gf/matrix4d.h>
|
||||
#include <pxr/base/tf/hashmap.h>
|
||||
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_set.hh"
|
||||
|
||||
#include "BKE_layer.h"
|
||||
#include "BKE_object.h"
|
||||
|
||||
#include "id.h"
|
||||
#include "material.h"
|
||||
|
||||
namespace blender::io::hydra {
|
||||
|
||||
class ObjectData : public IdData {
|
||||
public:
|
||||
pxr::GfMatrix4d transform;
|
||||
bool visible = true;
|
||||
|
||||
public:
|
||||
ObjectData(HydraSceneDelegate *scene_delegate, Object *object, pxr::SdfPath const &prim_id);
|
||||
|
||||
static std::unique_ptr<ObjectData> create(HydraSceneDelegate *scene_delegate,
|
||||
Object *object,
|
||||
pxr::SdfPath const &prim_id);
|
||||
static bool is_supported(Object *object);
|
||||
static bool is_mesh(Object *object);
|
||||
static bool is_visible(HydraSceneDelegate *scene_delegate,
|
||||
Object *object,
|
||||
int mode = OB_VISIBLE_SELF);
|
||||
|
||||
using IdData::get_data;
|
||||
virtual pxr::VtValue get_data(pxr::SdfPath const &id, pxr::TfToken const &key) const;
|
||||
virtual pxr::SdfPath material_id() const;
|
||||
virtual pxr::SdfPath material_id(pxr::SdfPath const &id) const;
|
||||
virtual void available_materials(Set<pxr::SdfPath> &paths) const;
|
||||
|
||||
protected:
|
||||
virtual void write_transform();
|
||||
virtual void write_materials();
|
||||
MaterialData *get_or_create_material(Material *mat);
|
||||
};
|
||||
|
||||
using ObjectDataMap = Map<pxr::SdfPath, std::unique_ptr<ObjectData>>;
|
||||
|
||||
pxr::GfMatrix4d gf_matrix_from_transform(float m[4][4]);
|
||||
|
||||
} // namespace blender::io::hydra
|
162
source/blender/io/usd/hydra/volume.cc
Normal file
162
source/blender/io/usd/hydra/volume.cc
Normal file
@ -0,0 +1,162 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#include <pxr/imaging/hd/bprim.h>
|
||||
#include <pxr/imaging/hd/tokens.h>
|
||||
#include <pxr/imaging/hd/volumeFieldSchema.h>
|
||||
#include <pxr/usd/usdHydra/tokens.h>
|
||||
#include <pxr/usd/usdVol/tokens.h>
|
||||
#include <pxr/usdImaging/usdVolImaging/tokens.h>
|
||||
|
||||
#include "BKE_material.h"
|
||||
#include "BKE_volume.h"
|
||||
#include "BLI_index_range.hh"
|
||||
#include "DNA_volume_types.h"
|
||||
|
||||
#include "hydra_scene_delegate.h"
|
||||
#include "volume.h"
|
||||
|
||||
namespace blender::io::hydra {
|
||||
|
||||
VolumeData::VolumeData(HydraSceneDelegate *scene_delegate,
|
||||
Object *object,
|
||||
pxr::SdfPath const &prim_id)
|
||||
: ObjectData(scene_delegate, object, prim_id)
|
||||
{
|
||||
}
|
||||
|
||||
void VolumeData::init()
|
||||
{
|
||||
field_descriptors_.clear();
|
||||
|
||||
Volume *volume = (Volume *)((Object *)this->id)->data;
|
||||
if (!BKE_volume_load(volume, scene_delegate_->bmain)) {
|
||||
return;
|
||||
}
|
||||
filepath_ = BKE_volume_grids_frame_filepath(volume);
|
||||
ID_LOGN(1, "%s", filepath_.c_str());
|
||||
|
||||
if (volume->runtime.grids) {
|
||||
const int num_grids = BKE_volume_num_grids(volume);
|
||||
if (num_grids) {
|
||||
for (const int i : IndexRange(num_grids)) {
|
||||
const VolumeGrid *grid = BKE_volume_grid_get_for_read(volume, i);
|
||||
const std::string grid_name = BKE_volume_grid_name(grid);
|
||||
|
||||
field_descriptors_.emplace_back(pxr::TfToken(grid_name),
|
||||
pxr::UsdVolImagingTokens->openvdbAsset,
|
||||
prim_id.AppendElementString("VF_" + grid_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
write_transform();
|
||||
write_materials();
|
||||
|
||||
BKE_volume_unload(volume);
|
||||
}
|
||||
|
||||
void VolumeData::insert()
|
||||
{
|
||||
scene_delegate_->GetRenderIndex().InsertRprim(
|
||||
pxr::HdPrimTypeTokens->volume, scene_delegate_, prim_id);
|
||||
|
||||
ID_LOGN(1, "");
|
||||
|
||||
for (auto &desc : field_descriptors_) {
|
||||
scene_delegate_->GetRenderIndex().InsertBprim(
|
||||
desc.fieldPrimType, scene_delegate_, desc.fieldId);
|
||||
ID_LOGN(2, "Volume field %s", desc.fieldId.GetText());
|
||||
}
|
||||
}
|
||||
|
||||
void VolumeData::remove()
|
||||
{
|
||||
for (auto &desc : field_descriptors_) {
|
||||
ID_LOG(2, "%s", desc.fieldId.GetText());
|
||||
scene_delegate_->GetRenderIndex().RemoveBprim(desc.fieldPrimType, desc.fieldId);
|
||||
}
|
||||
ID_LOG(1, "");
|
||||
scene_delegate_->GetRenderIndex().RemoveRprim(prim_id);
|
||||
}
|
||||
|
||||
void VolumeData::update()
|
||||
{
|
||||
Object *object = (Object *)id;
|
||||
pxr::HdDirtyBits bits = pxr::HdChangeTracker::Clean;
|
||||
if ((id->recalc & ID_RECALC_GEOMETRY) || (((ID *)object->data)->recalc & ID_RECALC_GEOMETRY)) {
|
||||
init();
|
||||
bits = pxr::HdChangeTracker::AllDirty;
|
||||
}
|
||||
if (id->recalc & ID_RECALC_SHADING) {
|
||||
write_materials();
|
||||
bits |= pxr::HdChangeTracker::DirtyMaterialId | pxr::HdChangeTracker::DirtyDoubleSided;
|
||||
}
|
||||
if (id->recalc & ID_RECALC_TRANSFORM) {
|
||||
write_transform();
|
||||
bits |= pxr::HdChangeTracker::DirtyTransform;
|
||||
}
|
||||
|
||||
if (bits == pxr::HdChangeTracker::Clean) {
|
||||
return;
|
||||
}
|
||||
|
||||
scene_delegate_->GetRenderIndex().GetChangeTracker().MarkRprimDirty(prim_id, bits);
|
||||
ID_LOGN(1, "");
|
||||
}
|
||||
|
||||
pxr::VtValue VolumeData::get_data(pxr::TfToken const &key) const
|
||||
{
|
||||
if (key == pxr::HdVolumeFieldSchemaTokens->filePath) {
|
||||
return pxr::VtValue(pxr::SdfAssetPath(filepath_, filepath_));
|
||||
}
|
||||
if (key == pxr::HdVolumeFieldSchemaTokens->fieldIndex) {
|
||||
return pxr::VtValue(0);
|
||||
}
|
||||
if (key == pxr::UsdHydraTokens->textureMemory) {
|
||||
return pxr::VtValue(0.0f);
|
||||
}
|
||||
return pxr::VtValue();
|
||||
}
|
||||
|
||||
pxr::VtValue VolumeData::get_data(pxr::SdfPath const &id, pxr::TfToken const &key) const
|
||||
{
|
||||
if (key == pxr::HdVolumeFieldSchemaTokens->fieldName) {
|
||||
std::string name = id.GetName();
|
||||
return pxr::VtValue(pxr::TfToken(name.substr(name.find("VF_") + 3)));
|
||||
}
|
||||
|
||||
return get_data(key);
|
||||
}
|
||||
|
||||
pxr::SdfPath VolumeData::material_id() const
|
||||
{
|
||||
if (!mat_data_) {
|
||||
return pxr::SdfPath();
|
||||
}
|
||||
return mat_data_->prim_id;
|
||||
}
|
||||
|
||||
void VolumeData::available_materials(Set<pxr::SdfPath> &paths) const
|
||||
{
|
||||
if (mat_data_ && !mat_data_->prim_id.IsEmpty()) {
|
||||
paths.add(mat_data_->prim_id);
|
||||
}
|
||||
}
|
||||
|
||||
pxr::HdVolumeFieldDescriptorVector VolumeData::field_descriptors() const
|
||||
{
|
||||
return field_descriptors_;
|
||||
}
|
||||
|
||||
void VolumeData::write_materials()
|
||||
{
|
||||
Object *object = (Object *)id;
|
||||
Material *mat = nullptr;
|
||||
/* TODO: Using only first material. Add support for multimaterial. */
|
||||
if (BKE_object_material_count_eval(object) > 0) {
|
||||
mat = BKE_object_material_get_eval(object, 0);
|
||||
}
|
||||
mat_data_ = get_or_create_material(mat);
|
||||
}
|
||||
|
||||
} // namespace blender::io::hydra
|
37
source/blender/io/usd/hydra/volume.h
Normal file
37
source/blender/io/usd/hydra/volume.h
Normal file
@ -0,0 +1,37 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pxr/imaging/hd/sceneDelegate.h>
|
||||
|
||||
#include "object.h"
|
||||
|
||||
namespace blender::io::hydra {
|
||||
|
||||
class VolumeData : public ObjectData {
|
||||
protected:
|
||||
std::string filepath_;
|
||||
pxr::HdVolumeFieldDescriptorVector field_descriptors_;
|
||||
MaterialData *mat_data_ = nullptr;
|
||||
|
||||
public:
|
||||
VolumeData(HydraSceneDelegate *scene_delegate, Object *object, pxr::SdfPath const &prim_id);
|
||||
|
||||
void init() override;
|
||||
void insert() override;
|
||||
void remove() override;
|
||||
void update() override;
|
||||
|
||||
pxr::VtValue get_data(pxr::TfToken const &key) const override;
|
||||
pxr::VtValue get_data(pxr::SdfPath const &id, pxr::TfToken const &key) const override;
|
||||
pxr::SdfPath material_id() const override;
|
||||
void available_materials(Set<pxr::SdfPath> &paths) const override;
|
||||
|
||||
pxr::HdVolumeFieldDescriptorVector field_descriptors() const;
|
||||
|
||||
protected:
|
||||
void write_materials() override;
|
||||
};
|
||||
|
||||
} // namespace blender::io::hydra
|
134
source/blender/io/usd/hydra/volume_modifier.cc
Normal file
134
source/blender/io/usd/hydra/volume_modifier.cc
Normal file
@ -0,0 +1,134 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#include "volume_modifier.h"
|
||||
|
||||
#include <pxr/usdImaging/usdVolImaging/tokens.h>
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_volume_types.h"
|
||||
|
||||
#include "BLI_path_util.h"
|
||||
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_modifier.h"
|
||||
|
||||
#include "hydra_scene_delegate.h"
|
||||
|
||||
PXR_NAMESPACE_OPEN_SCOPE
|
||||
TF_DEFINE_PRIVATE_TOKENS(grid_tokens_, (density)(flame)(shadow)(temperature)(velocity));
|
||||
PXR_NAMESPACE_CLOSE_SCOPE
|
||||
|
||||
namespace blender::io::hydra {
|
||||
|
||||
VolumeModifierData::VolumeModifierData(HydraSceneDelegate *scene_delegate,
|
||||
Object *object,
|
||||
pxr::SdfPath const &prim_id)
|
||||
: VolumeData(scene_delegate, object, prim_id)
|
||||
{
|
||||
}
|
||||
|
||||
bool VolumeModifierData::is_volume_modifier(Object *object)
|
||||
{
|
||||
if (object->type != OB_MESH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FluidModifierData *modifier = (FluidModifierData *)BKE_modifiers_findby_type(
|
||||
object, eModifierType_Fluid);
|
||||
return modifier && modifier->type & MOD_FLUID_TYPE_DOMAIN &&
|
||||
modifier->domain->type == FLUID_DOMAIN_TYPE_GAS;
|
||||
}
|
||||
|
||||
void VolumeModifierData::init()
|
||||
{
|
||||
field_descriptors_.clear();
|
||||
|
||||
Object *object = (Object *)this->id;
|
||||
ModifierData *md = BKE_modifiers_findby_type(object, eModifierType_Fluid);
|
||||
modifier_ = (FluidModifierData *)BKE_modifier_get_evaluated(
|
||||
scene_delegate_->depsgraph, object, md);
|
||||
|
||||
if ((modifier_->domain->cache_data_format & FLUID_DOMAIN_FILE_OPENVDB) == 0) {
|
||||
CLOG_WARN(LOG_HYDRA_SCENE,
|
||||
"Volume %s is't exported: only OpenVDB file format supported",
|
||||
prim_id.GetText());
|
||||
return;
|
||||
}
|
||||
|
||||
filepath_ = get_cached_file_path(modifier_->domain->cache_directory,
|
||||
scene_delegate_->scene->r.cfra);
|
||||
ID_LOG(1, "%s", filepath_.c_str());
|
||||
|
||||
for (auto &grid_name : pxr::grid_tokens_->allTokens) {
|
||||
field_descriptors_.emplace_back(grid_name,
|
||||
pxr::UsdVolImagingTokens->openvdbAsset,
|
||||
prim_id.AppendElementString("VF_" + grid_name.GetString()));
|
||||
}
|
||||
|
||||
write_transform();
|
||||
write_materials();
|
||||
}
|
||||
|
||||
void VolumeModifierData::update()
|
||||
{
|
||||
Object *object = (Object *)id;
|
||||
if ((id->recalc & ID_RECALC_GEOMETRY) || (((ID *)object->data)->recalc & ID_RECALC_GEOMETRY)) {
|
||||
remove();
|
||||
init();
|
||||
insert();
|
||||
return;
|
||||
}
|
||||
pxr::HdDirtyBits bits = pxr::HdChangeTracker::Clean;
|
||||
if (id->recalc & ID_RECALC_SHADING) {
|
||||
write_materials();
|
||||
bits |= pxr::HdChangeTracker::DirtyMaterialId | pxr::HdChangeTracker::DirtyDoubleSided;
|
||||
}
|
||||
if (id->recalc & ID_RECALC_TRANSFORM) {
|
||||
write_transform();
|
||||
bits |= pxr::HdChangeTracker::DirtyTransform;
|
||||
}
|
||||
|
||||
if (bits == pxr::HdChangeTracker::Clean) {
|
||||
return;
|
||||
}
|
||||
|
||||
scene_delegate_->GetRenderIndex().GetChangeTracker().MarkRprimDirty(prim_id, bits);
|
||||
ID_LOG(1, "");
|
||||
}
|
||||
|
||||
void VolumeModifierData::write_transform()
|
||||
{
|
||||
Object *object = (Object *)this->id;
|
||||
|
||||
/* set base scaling */
|
||||
transform = pxr::GfMatrix4d().SetScale(
|
||||
pxr::GfVec3d(modifier_->domain->scale / modifier_->domain->global_size[0],
|
||||
modifier_->domain->scale / modifier_->domain->global_size[1],
|
||||
modifier_->domain->scale / modifier_->domain->global_size[2]));
|
||||
/* positioning to center */
|
||||
transform *= pxr::GfMatrix4d().SetTranslate(pxr::GfVec3d(-1, -1, -1));
|
||||
|
||||
/* including texspace transform */
|
||||
float texspace_loc[3] = {0.0f, 0.0f, 0.0f}, texspace_scale[3] = {1.0f, 1.0f, 1.0f};
|
||||
BKE_mesh_texspace_get((Mesh *)object->data, texspace_loc, texspace_scale);
|
||||
transform *= pxr::GfMatrix4d(1.0f).SetScale(pxr::GfVec3d(texspace_scale)) *
|
||||
pxr::GfMatrix4d(1.0f).SetTranslate(pxr::GfVec3d(texspace_loc));
|
||||
|
||||
/* applying object transform */
|
||||
transform *= gf_matrix_from_transform(object->object_to_world);
|
||||
}
|
||||
|
||||
std::string VolumeModifierData::get_cached_file_path(std::string directory, int frame)
|
||||
{
|
||||
char file_path[FILE_MAX];
|
||||
char file_name[32];
|
||||
snprintf(
|
||||
file_name, sizeof(file_name), "%s_####%s", FLUID_NAME_DATA, FLUID_DOMAIN_EXTENSION_OPENVDB);
|
||||
BLI_path_frame(file_name, sizeof(file_name), frame, 0);
|
||||
BLI_path_join(file_path, sizeof(file_path), directory.c_str(), FLUID_DOMAIN_DIR_DATA, file_name);
|
||||
|
||||
return file_path;
|
||||
}
|
||||
|
||||
} // namespace blender::io::hydra
|
32
source/blender/io/usd/hydra/volume_modifier.h
Normal file
32
source/blender/io/usd/hydra/volume_modifier.h
Normal file
@ -0,0 +1,32 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_fluid_types.h"
|
||||
|
||||
#include "volume.h"
|
||||
|
||||
namespace blender::io::hydra {
|
||||
|
||||
class VolumeModifierData : public VolumeData {
|
||||
|
||||
public:
|
||||
VolumeModifierData(HydraSceneDelegate *scene_delegate,
|
||||
Object *object,
|
||||
pxr::SdfPath const &prim_id);
|
||||
static bool is_volume_modifier(Object *object);
|
||||
|
||||
void init() override;
|
||||
void update() override;
|
||||
|
||||
protected:
|
||||
void write_transform() override;
|
||||
|
||||
private:
|
||||
std::string get_cached_file_path(std::string directory, int frame);
|
||||
|
||||
FluidModifierData *modifier_;
|
||||
};
|
||||
|
||||
} // namespace blender::io::hydra
|
156
source/blender/io/usd/hydra/world.cc
Normal file
156
source/blender/io/usd/hydra/world.cc
Normal file
@ -0,0 +1,156 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#include "world.h"
|
||||
|
||||
#include <pxr/base/gf/rotation.h>
|
||||
#include <pxr/base/gf/vec2f.h>
|
||||
#include <pxr/base/vt/array.h>
|
||||
#include <pxr/imaging/hd/light.h>
|
||||
#include <pxr/imaging/hd/renderDelegate.h>
|
||||
#include <pxr/imaging/hd/tokens.h>
|
||||
#include <pxr/usd/usdLux/tokens.h>
|
||||
|
||||
#include "DNA_node_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BLI_math_rotation.h"
|
||||
#include "BLI_path_util.h"
|
||||
|
||||
#include "BKE_node.h"
|
||||
#include "BKE_node_runtime.hh"
|
||||
#include "BKE_studiolight.h"
|
||||
|
||||
#include "NOD_shader.h"
|
||||
|
||||
#include "hydra_scene_delegate.h"
|
||||
#include "image.h"
|
||||
|
||||
/* TODO : add custom tftoken "transparency"? */
|
||||
|
||||
/* NOTE: opacity and blur aren't supported by USD */
|
||||
|
||||
namespace blender::io::hydra {
|
||||
|
||||
WorldData::WorldData(HydraSceneDelegate *scene_delegate, pxr::SdfPath const &prim_id)
|
||||
: LightData(scene_delegate, nullptr, prim_id)
|
||||
{
|
||||
prim_type_ = pxr::HdPrimTypeTokens->domeLight;
|
||||
}
|
||||
|
||||
void WorldData::init()
|
||||
{
|
||||
data_.clear();
|
||||
data_[pxr::UsdLuxTokens->orientToStageUpAxis] = true;
|
||||
|
||||
float intensity = 1.0f;
|
||||
float exposure = 1.0f;
|
||||
pxr::GfVec3f color(1.0f, 1.0f, 1.0f);
|
||||
pxr::SdfAssetPath texture_file;
|
||||
|
||||
if (scene_delegate_->shading_settings.use_scene_world) {
|
||||
World *world = scene_delegate_->scene->world;
|
||||
ID_LOG(1, "%s", world->id.name);
|
||||
|
||||
exposure = world->exposure;
|
||||
if (world->use_nodes) {
|
||||
/* TODO: Create nodes parsing system */
|
||||
|
||||
bNode *output_node = ntreeShaderOutputNode(world->nodetree, SHD_OUTPUT_ALL);
|
||||
blender::Span<bNodeSocket *> input_sockets = output_node->input_sockets();
|
||||
bNodeSocket *input_socket = nullptr;
|
||||
|
||||
for (auto socket : input_sockets) {
|
||||
if (STREQ(socket->name, "Surface")) {
|
||||
input_socket = socket;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!input_socket) {
|
||||
return;
|
||||
}
|
||||
bNodeLink const *link = input_socket->directly_linked_links()[0];
|
||||
if (input_socket->directly_linked_links().is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bNode *input_node = link->fromnode;
|
||||
if (input_node->type != SH_NODE_BACKGROUND) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bNodeSocket &color_input = input_node->input_by_identifier("Color");
|
||||
const bNodeSocket &strength_input = input_node->input_by_identifier("Strength");
|
||||
|
||||
float const *strength = strength_input.default_value_typed<float>();
|
||||
float const *input_color = color_input.default_value_typed<float>();
|
||||
intensity = strength[1];
|
||||
color = pxr::GfVec3f(input_color[0], input_color[1], input_color[2]);
|
||||
|
||||
if (!color_input.directly_linked_links().is_empty()) {
|
||||
bNode *color_input_node = color_input.directly_linked_links()[0]->fromnode;
|
||||
if (ELEM(color_input_node->type, SH_NODE_TEX_IMAGE, SH_NODE_TEX_ENVIRONMENT)) {
|
||||
NodeTexImage *tex = static_cast<NodeTexImage *>(color_input_node->storage);
|
||||
Image *image = (Image *)color_input_node->id;
|
||||
if (image) {
|
||||
std::string image_path = cache_or_get_image_file(
|
||||
scene_delegate_->bmain, scene_delegate_->scene, image, &tex->iuser);
|
||||
if (!image_path.empty()) {
|
||||
texture_file = pxr::SdfAssetPath(image_path, image_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
intensity = 1.0f;
|
||||
color = pxr::GfVec3f(world->horr, world->horg, world->horb);
|
||||
}
|
||||
|
||||
if (texture_file.GetAssetPath().empty()) {
|
||||
float fill_color[4] = {color[0], color[1], color[2], 1.0f};
|
||||
std::string image_path = cache_image_color(fill_color);
|
||||
texture_file = pxr::SdfAssetPath(image_path, image_path);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ID_LOG(1, "studiolight: %s", scene_delegate_->shading_settings.studiolight_name.c_str());
|
||||
|
||||
StudioLight *sl = BKE_studiolight_find(
|
||||
scene_delegate_->shading_settings.studiolight_name.c_str(),
|
||||
STUDIOLIGHT_ORIENTATIONS_MATERIAL_MODE);
|
||||
if (sl != NULL && sl->flag & STUDIOLIGHT_TYPE_WORLD) {
|
||||
texture_file = pxr::SdfAssetPath(sl->filepath, sl->filepath);
|
||||
/* coefficient to follow Cycles result */
|
||||
intensity = scene_delegate_->shading_settings.studiolight_intensity / 2;
|
||||
}
|
||||
}
|
||||
|
||||
data_[pxr::HdLightTokens->intensity] = intensity;
|
||||
data_[pxr::HdLightTokens->exposure] = exposure;
|
||||
data_[pxr::HdLightTokens->color] = color;
|
||||
data_[pxr::HdLightTokens->textureFile] = texture_file;
|
||||
|
||||
write_transform();
|
||||
}
|
||||
|
||||
void WorldData::update()
|
||||
{
|
||||
ID_LOG(1, "");
|
||||
init();
|
||||
scene_delegate_->GetRenderIndex().GetChangeTracker().MarkSprimDirty(prim_id,
|
||||
pxr::HdLight::AllDirty);
|
||||
}
|
||||
|
||||
void WorldData::write_transform()
|
||||
{
|
||||
transform = pxr::GfMatrix4d().SetRotate(pxr::GfRotation(pxr::GfVec3d(1.0, 0.0, 0.0), 90.0)) *
|
||||
pxr::GfMatrix4d().SetRotate(pxr::GfRotation(pxr::GfVec3d(0.0, 0.0, 1.0), 90.0));
|
||||
if (!scene_delegate_->shading_settings.use_scene_world) {
|
||||
transform *= pxr::GfMatrix4d().SetRotate(
|
||||
pxr::GfRotation(pxr::GfVec3d(0.0, 0.0, -1.0),
|
||||
RAD2DEGF(scene_delegate_->shading_settings.studiolight_rotation)));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::io::hydra
|
32
source/blender/io/usd/hydra/world.h
Normal file
32
source/blender/io/usd/hydra/world.h
Normal file
@ -0,0 +1,32 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <pxr/base/gf/matrix4d.h>
|
||||
#include <pxr/base/tf/staticTokens.h>
|
||||
#include <pxr/base/vt/value.h>
|
||||
#include <pxr/usd/sdf/assetPath.h>
|
||||
#include <pxr/usd/sdf/path.h>
|
||||
|
||||
#include "DNA_view3d_types.h"
|
||||
#include "DNA_world_types.h"
|
||||
|
||||
#include "light.h"
|
||||
|
||||
namespace blender::io::hydra {
|
||||
|
||||
class WorldData : public LightData {
|
||||
public:
|
||||
WorldData(HydraSceneDelegate *scene_delegate, pxr::SdfPath const &prim_id);
|
||||
|
||||
void init() override;
|
||||
void update() override;
|
||||
|
||||
protected:
|
||||
void write_transform() override;
|
||||
};
|
||||
|
||||
} // namespace blender::io::hydra
|
@ -3,6 +3,7 @@
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "usd.h"
|
||||
#include "usd.hh"
|
||||
#include "usd_hierarchy_iterator.h"
|
||||
|
||||
#include <pxr/base/plug/registry.h>
|
||||
@ -198,67 +199,41 @@ static bool perform_usdz_conversion(const ExportJobData *data)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void export_startjob(void *customdata,
|
||||
/* Cannot be const, this function implements wm_jobs_start_callback.
|
||||
* NOLINTNEXTLINE: readability-non-const-parameter. */
|
||||
bool *stop,
|
||||
bool *do_update,
|
||||
float *progress)
|
||||
static pxr::UsdStageRefPtr export_to_stage(const USDExportParams ¶ms,
|
||||
Depsgraph *depsgraph,
|
||||
const char *filepath,
|
||||
bool *stop,
|
||||
bool *do_update,
|
||||
float *progress)
|
||||
{
|
||||
ExportJobData *data = static_cast<ExportJobData *>(customdata);
|
||||
data->export_ok = false;
|
||||
data->start_time = timeit::Clock::now();
|
||||
|
||||
G.is_rendering = true;
|
||||
if (data->wm) {
|
||||
WM_set_locked_interface(data->wm, true);
|
||||
}
|
||||
G.is_break = false;
|
||||
|
||||
/* Construct the depsgraph for exporting. */
|
||||
Scene *scene = DEG_get_input_scene(data->depsgraph);
|
||||
if (data->params.visible_objects_only) {
|
||||
DEG_graph_build_from_view_layer(data->depsgraph);
|
||||
}
|
||||
else {
|
||||
DEG_graph_build_for_all_objects(data->depsgraph);
|
||||
}
|
||||
BKE_scene_graph_update_tagged(data->depsgraph, data->bmain);
|
||||
|
||||
*progress = 0.0f;
|
||||
*do_update = true;
|
||||
|
||||
/* For restoring the current frame after exporting animation is done. */
|
||||
const int orig_frame = scene->r.cfra;
|
||||
|
||||
pxr::UsdStageRefPtr usd_stage = pxr::UsdStage::CreateNew(data->unarchived_filepath);
|
||||
pxr::UsdStageRefPtr usd_stage = pxr::UsdStage::CreateNew(filepath);
|
||||
if (!usd_stage) {
|
||||
/* This happens when the USD JSON files cannot be found. When that happens,
|
||||
* the USD library doesn't know it has the functionality to write USDA and
|
||||
* USDC files, and creating a new UsdStage fails. */
|
||||
WM_reportf(RPT_ERROR,
|
||||
"USD Export: unable to find suitable USD plugin to write %s",
|
||||
data->unarchived_filepath);
|
||||
return;
|
||||
return usd_stage;
|
||||
}
|
||||
|
||||
usd_stage->SetMetadata(pxr::UsdGeomTokens->upAxis, pxr::VtValue(pxr::UsdGeomTokens->z));
|
||||
Scene *scene = DEG_get_input_scene(depsgraph);
|
||||
Main *bmain = DEG_get_bmain(depsgraph);
|
||||
|
||||
usd_stage->SetMetadata(pxr::UsdGeomTokens->metersPerUnit, double(scene->unit.scale_length));
|
||||
usd_stage->GetRootLayer()->SetDocumentation(std::string("Blender v") +
|
||||
BKE_blender_version_string());
|
||||
|
||||
/* Set up the stage for animated data. */
|
||||
if (data->params.export_animation) {
|
||||
if (params.export_animation) {
|
||||
usd_stage->SetTimeCodesPerSecond(FPS);
|
||||
usd_stage->SetStartTimeCode(scene->r.sfra);
|
||||
usd_stage->SetEndTimeCode(scene->r.efra);
|
||||
}
|
||||
|
||||
ensure_root_prim(usd_stage, data->params);
|
||||
/* For restoring the current frame after exporting animation is done. */
|
||||
const int orig_frame = scene->r.cfra;
|
||||
|
||||
USDHierarchyIterator iter(data->bmain, data->depsgraph, usd_stage, data->params);
|
||||
usd_stage->SetMetadata(pxr::UsdGeomTokens->upAxis, pxr::VtValue(pxr::UsdGeomTokens->z));
|
||||
ensure_root_prim(usd_stage, params);
|
||||
|
||||
if (data->params.export_animation) {
|
||||
USDHierarchyIterator iter(bmain, depsgraph, usd_stage, params);
|
||||
|
||||
if (params.export_animation) {
|
||||
/* Writing the animated frames is not 100% of the work, but it's our best guess. */
|
||||
float progress_per_frame = 1.0f / std::max(1, (scene->r.efra - scene->r.sfra + 1));
|
||||
|
||||
@ -270,13 +245,17 @@ static void export_startjob(void *customdata,
|
||||
/* Update the scene for the next frame to render. */
|
||||
scene->r.cfra = int(frame);
|
||||
scene->r.subframe = frame - scene->r.cfra;
|
||||
BKE_scene_graph_update_for_newframe(data->depsgraph);
|
||||
BKE_scene_graph_update_for_newframe(depsgraph);
|
||||
|
||||
iter.set_export_frame(frame);
|
||||
iter.iterate_and_write();
|
||||
|
||||
*progress += progress_per_frame;
|
||||
*do_update = true;
|
||||
if (progress) {
|
||||
*progress += progress_per_frame;
|
||||
}
|
||||
if (do_update) {
|
||||
*do_update = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -296,14 +275,65 @@ static void export_startjob(void *customdata,
|
||||
}
|
||||
}
|
||||
|
||||
usd_stage->GetRootLayer()->Save();
|
||||
|
||||
/* Finish up by going back to the keyframe that was current before we started. */
|
||||
if (scene->r.cfra != orig_frame) {
|
||||
scene->r.cfra = orig_frame;
|
||||
BKE_scene_graph_update_for_newframe(data->depsgraph);
|
||||
BKE_scene_graph_update_for_newframe(depsgraph);
|
||||
}
|
||||
|
||||
return usd_stage;
|
||||
}
|
||||
|
||||
pxr::UsdStageRefPtr export_to_stage(const USDExportParams ¶ms,
|
||||
Depsgraph *depsgraph,
|
||||
const char *filepath)
|
||||
{
|
||||
return export_to_stage(params, depsgraph, filepath, nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
static void export_startjob(void *customdata,
|
||||
/* Cannot be const, this function implements wm_jobs_start_callback.
|
||||
* NOLINTNEXTLINE: readability-non-const-parameter. */
|
||||
bool *stop,
|
||||
bool *do_update,
|
||||
float *progress)
|
||||
{
|
||||
ExportJobData *data = static_cast<ExportJobData *>(customdata);
|
||||
data->export_ok = false;
|
||||
data->start_time = timeit::Clock::now();
|
||||
|
||||
G.is_rendering = true;
|
||||
if (data->wm) {
|
||||
WM_set_locked_interface(data->wm, true);
|
||||
}
|
||||
G.is_break = false;
|
||||
|
||||
/* Construct the depsgraph for exporting. */
|
||||
if (data->params.visible_objects_only) {
|
||||
DEG_graph_build_from_view_layer(data->depsgraph);
|
||||
}
|
||||
else {
|
||||
DEG_graph_build_for_all_objects(data->depsgraph);
|
||||
}
|
||||
BKE_scene_graph_update_tagged(data->depsgraph, data->bmain);
|
||||
|
||||
*progress = 0.0f;
|
||||
*do_update = true;
|
||||
|
||||
pxr::UsdStageRefPtr usd_stage = export_to_stage(
|
||||
data->params, data->depsgraph, data->unarchived_filepath, stop, do_update, progress);
|
||||
if (!usd_stage) {
|
||||
/* This happens when the USD JSON files cannot be found. When that happens,
|
||||
* the USD library doesn't know it has the functionality to write USDA and
|
||||
* USDC files, and creating a new UsdStage fails. */
|
||||
WM_reportf(RPT_ERROR,
|
||||
"USD Export: unable to find suitable USD plugin to write %s",
|
||||
data->unarchived_filepath);
|
||||
return;
|
||||
}
|
||||
|
||||
usd_stage->GetRootLayer()->Save();
|
||||
|
||||
if (data->targets_usdz()) {
|
||||
bool usd_conversion_success = perform_usdz_conversion(data);
|
||||
if (!usd_conversion_success) {
|
||||
|
18
source/blender/io/usd/usd.hh
Normal file
18
source/blender/io/usd/usd.hh
Normal file
@ -0,0 +1,18 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pxr/usd/usd/stage.h>
|
||||
|
||||
struct Depsgraph;
|
||||
struct USDExportParams;
|
||||
|
||||
namespace blender::io::usd {
|
||||
|
||||
pxr::UsdStageRefPtr export_to_stage(const USDExportParams ¶ms,
|
||||
Depsgraph *depsgraph,
|
||||
const char *filepath);
|
||||
|
||||
};
|
@ -1010,6 +1010,14 @@ static void rna_def_render_engine(BlenderRNA *brna)
|
||||
RNA_define_verify_sdna(true);
|
||||
}
|
||||
|
||||
static void rna_def_hydra_render_engine(BlenderRNA *brna)
|
||||
{
|
||||
/* This is implemented in Python. */
|
||||
StructRNA *srna = RNA_def_struct(brna, "HydraRenderEngine", "RenderEngine");
|
||||
RNA_def_struct_sdna(srna, "RenderEngine");
|
||||
RNA_def_struct_ui_text(srna, "Hydra Render Engine", "Base class from USD Hydra based renderers");
|
||||
}
|
||||
|
||||
static void rna_def_render_result(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
@ -1238,6 +1246,7 @@ static void rna_def_render_pass(BlenderRNA *brna)
|
||||
void RNA_def_render(BlenderRNA *brna)
|
||||
{
|
||||
rna_def_render_engine(brna);
|
||||
rna_def_hydra_render_engine(brna);
|
||||
rna_def_render_result(brna);
|
||||
rna_def_render_view(brna);
|
||||
rna_def_render_layer(brna);
|
||||
|
@ -364,6 +364,13 @@ if(WITH_HARU)
|
||||
add_definitions(-DWITH_HARU)
|
||||
endif()
|
||||
|
||||
if(WITH_HYDRA)
|
||||
list(APPEND LIB
|
||||
bf_render_hydra
|
||||
)
|
||||
add_definitions(-DWITH_HYDRA)
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_python "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
|
||||
# RNA_prototypes.h
|
||||
|
@ -257,6 +257,11 @@ static PyObject *CCL_initPython()
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WITH_HYDRA
|
||||
/* defined in render_hydra module */
|
||||
PyObject *BPyInit_hydra();
|
||||
#endif
|
||||
|
||||
static _inittab bpy_internal_modules[] = {
|
||||
{"mathutils", PyInit_mathutils},
|
||||
#if 0
|
||||
@ -286,6 +291,9 @@ static _inittab bpy_internal_modules[] = {
|
||||
#endif
|
||||
{"gpu", BPyInit_gpu},
|
||||
{"idprop", BPyInit_idprop},
|
||||
#ifdef WITH_HYDRA
|
||||
{"_bpy_hydra", BPyInit_hydra},
|
||||
#endif
|
||||
{nullptr, nullptr},
|
||||
};
|
||||
|
||||
|
@ -93,3 +93,7 @@ endif()
|
||||
|
||||
|
||||
blender_add_lib_nolist(bf_render "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
|
||||
if(WITH_HYDRA)
|
||||
add_subdirectory(hydra)
|
||||
endif()
|
||||
|
96
source/blender/render/hydra/CMakeLists.txt
Normal file
96
source/blender/render/hydra/CMakeLists.txt
Normal file
@ -0,0 +1,96 @@
|
||||
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# This suppresses the warning "This file includes at least one deprecated or antiquated
|
||||
# header which may be removed without further notice at a future date", which is caused
|
||||
# by the USD library including <ext/hash_set> on Linux. This has been reported at:
|
||||
# https://github.com/PixarAnimationStudios/USD/issues/1057.
|
||||
if(UNIX AND NOT APPLE)
|
||||
add_definitions(-D_GLIBCXX_PERMIT_BACKWARD_HASH)
|
||||
endif()
|
||||
if(WIN32)
|
||||
add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN -DBOOST_DEBUG_PYTHON)
|
||||
endif()
|
||||
add_definitions(-DBOOST_ALL_NO_LIB)
|
||||
|
||||
# Precompiled Linux libs are made with GCC, and USD uses some extensions
|
||||
# which lead to an incompatible ABI for Clang. Using those extensions with
|
||||
# Clang as well works around the issue.
|
||||
if(UNIX AND NOT APPLE)
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
if(EXISTS ${LIBDIR})
|
||||
add_definitions(-DARCH_HAS_GNU_STL_EXTENSIONS)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# USD headers use deprecated TBB headers, silence warning.
|
||||
add_definitions(-DTBB_SUPPRESS_DEPRECATED_MESSAGES=1)
|
||||
|
||||
if(WIN32)
|
||||
# Some USD library headers trigger the "unreferenced formal parameter"
|
||||
# warning alert.
|
||||
# Silence them by restore warn C4100 back to w4
|
||||
remove_cc_flag("/w34100")
|
||||
endif()
|
||||
|
||||
set(INC
|
||||
../../../../intern/clog
|
||||
../../../../intern/guardedalloc
|
||||
../../makesdna
|
||||
../../makesrna
|
||||
../../nodes
|
||||
../../blenlib
|
||||
../../depsgraph
|
||||
../../blenkernel
|
||||
../../imbuf
|
||||
../../io/usd
|
||||
../../gpu
|
||||
../../gpu/intern
|
||||
../../python/intern
|
||||
# RNA_prototypes.h
|
||||
${CMAKE_BINARY_DIR}/source/blender/makesrna
|
||||
..
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
${PYTHON_INCLUDE_DIRS}
|
||||
${Epoxy_INCLUDE_DIRS}
|
||||
${USD_INCLUDE_DIRS}
|
||||
${BOOST_INCLUDE_DIR}
|
||||
${TBB_INCLUDE_DIR}
|
||||
${GFLAGS_INCLUDE_DIRS}
|
||||
${EIGEN3_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
set(LIB
|
||||
${Epoxy_LIBRARIES}
|
||||
${PYTHON_LIBRARIES}
|
||||
${BOOST_LIBRARIES}
|
||||
${USD_LIBRARIES}
|
||||
${TBB_LIBRARIES}
|
||||
bf_usd
|
||||
)
|
||||
|
||||
set(SRC
|
||||
engine.cc
|
||||
final_engine.cc
|
||||
light_tasks_delegate.cc
|
||||
preview_engine.cc
|
||||
python.cc
|
||||
render_task_delegate.cc
|
||||
viewport_engine.cc
|
||||
|
||||
engine.h
|
||||
final_engine.h
|
||||
light_tasks_delegate.h
|
||||
preview_engine.h
|
||||
render_task_delegate.h
|
||||
viewport_engine.h
|
||||
)
|
||||
|
||||
blender_add_lib(bf_render_hydra "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
|
||||
# RNA_prototypes.h
|
||||
add_dependencies(bf_render_hydra bf_rna)
|
124
source/blender/render/hydra/engine.cc
Normal file
124
source/blender/render/hydra/engine.cc
Normal file
@ -0,0 +1,124 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#include "engine.h"
|
||||
|
||||
#include <pxr/base/plug/plugin.h>
|
||||
#include <pxr/base/plug/registry.h>
|
||||
#include <pxr/imaging/hd/rendererPluginRegistry.h>
|
||||
#include <pxr/imaging/hdSt/renderDelegate.h>
|
||||
#include <pxr/imaging/hgi/tokens.h>
|
||||
#include <pxr/usd/usdGeom/tokens.h>
|
||||
|
||||
#include "BLI_path_util.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
|
||||
#include "GPU_context.h"
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "RE_engine.h"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
namespace blender::render::hydra {
|
||||
|
||||
CLG_LOGREF_DECLARE_GLOBAL(LOG_HYDRA_RENDER, "hydra.render");
|
||||
|
||||
Engine::Engine(RenderEngine *bl_engine, const std::string &render_delegate_name)
|
||||
: render_delegate_name_(render_delegate_name), bl_engine_(bl_engine)
|
||||
{
|
||||
pxr::HdRendererPluginRegistry ®istry = pxr::HdRendererPluginRegistry::GetInstance();
|
||||
|
||||
pxr::TF_PY_ALLOW_THREADS_IN_SCOPE();
|
||||
|
||||
if (GPU_backend_get_type() == GPU_BACKEND_VULKAN) {
|
||||
BLI_setenv("HGI_ENABLE_VULKAN", "1");
|
||||
}
|
||||
|
||||
pxr::HdDriverVector hd_drivers;
|
||||
if (bl_engine->type->flag & RE_USE_GPU_CONTEXT) {
|
||||
hgi_ = pxr::Hgi::CreatePlatformDefaultHgi();
|
||||
hgi_driver_.name = pxr::HgiTokens->renderDriver;
|
||||
hgi_driver_.driver = pxr::VtValue(hgi_.get());
|
||||
|
||||
hd_drivers.push_back(&hgi_driver_);
|
||||
}
|
||||
render_delegate_ = registry.CreateRenderDelegate(pxr::TfToken(render_delegate_name_));
|
||||
|
||||
if (!render_delegate_) {
|
||||
throw std::runtime_error("Cannot create render delegate: " + render_delegate_name_);
|
||||
}
|
||||
|
||||
render_index_.reset(pxr::HdRenderIndex::New(render_delegate_.Get(), hd_drivers));
|
||||
free_camera_delegate_ = std::make_unique<pxr::HdxFreeCameraSceneDelegate>(
|
||||
render_index_.get(), pxr::SdfPath::AbsoluteRootPath().AppendElementString("freeCamera"));
|
||||
|
||||
if (bl_engine->type->flag & RE_USE_GPU_CONTEXT && GPU_backend_get_type() == GPU_BACKEND_OPENGL) {
|
||||
render_task_delegate_ = std::make_unique<GPURenderTaskDelegate>(
|
||||
render_index_.get(), pxr::SdfPath::AbsoluteRootPath().AppendElementString("renderTask"));
|
||||
}
|
||||
else {
|
||||
render_task_delegate_ = std::make_unique<RenderTaskDelegate>(
|
||||
render_index_.get(), pxr::SdfPath::AbsoluteRootPath().AppendElementString("renderTask"));
|
||||
}
|
||||
render_task_delegate_->set_camera(free_camera_delegate_->GetCameraId());
|
||||
|
||||
if (render_delegate_name_ == "HdStormRendererPlugin") {
|
||||
light_tasks_delegate_ = std::make_unique<LightTasksDelegate>(
|
||||
render_index_.get(), pxr::SdfPath::AbsoluteRootPath().AppendElementString("lightTasks"));
|
||||
light_tasks_delegate_->set_camera(free_camera_delegate_->GetCameraId());
|
||||
}
|
||||
|
||||
engine_ = std::make_unique<pxr::HdEngine>();
|
||||
}
|
||||
|
||||
void Engine::sync(Depsgraph *depsgraph, bContext *context)
|
||||
{
|
||||
depsgraph_ = depsgraph;
|
||||
context_ = context;
|
||||
scene_ = DEG_get_evaluated_scene(depsgraph);
|
||||
|
||||
if (!hydra_scene_delegate_) {
|
||||
pxr::SdfPath scene_path = pxr::SdfPath::AbsoluteRootPath().AppendElementString("scene");
|
||||
hydra_scene_delegate_ = std::make_unique<io::hydra::HydraSceneDelegate>(render_index_.get(),
|
||||
scene_path);
|
||||
}
|
||||
hydra_scene_delegate_->populate(depsgraph, context ? CTX_wm_view3d(context) : nullptr);
|
||||
}
|
||||
|
||||
void Engine::set_render_setting(const std::string &key, const pxr::VtValue &val)
|
||||
{
|
||||
render_delegate_->SetRenderSetting(pxr::TfToken(key), val);
|
||||
}
|
||||
|
||||
float Engine::renderer_percent_done()
|
||||
{
|
||||
pxr::VtDictionary render_stats = render_delegate_->GetRenderStats();
|
||||
auto it = render_stats.find("percentDone");
|
||||
if (it == render_stats.end()) {
|
||||
return 0.0f;
|
||||
}
|
||||
return (float)it->second.UncheckedGet<double>();
|
||||
}
|
||||
|
||||
pxr::HdTaskSharedPtrVector Engine::tasks()
|
||||
{
|
||||
pxr::HdTaskSharedPtrVector res;
|
||||
if (light_tasks_delegate_) {
|
||||
if (scene_->r.alphamode != R_ALPHAPREMUL) {
|
||||
#ifndef __APPLE__
|
||||
/* TODO: Temporary disable skydome task for MacOS due to crash with error:
|
||||
* Failed to created pipeline state, error depthAttachmentPixelFormat is not valid
|
||||
* and shader writes to depth */
|
||||
res.push_back(light_tasks_delegate_->skydome_task());
|
||||
#endif
|
||||
}
|
||||
res.push_back(light_tasks_delegate_->simple_task());
|
||||
}
|
||||
res.push_back(render_task_delegate_->task());
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace blender::render::hydra
|
69
source/blender/render/hydra/engine.h
Normal file
69
source/blender/render/hydra/engine.h
Normal file
@ -0,0 +1,69 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <pxr/imaging/hd/driver.h>
|
||||
#include <pxr/imaging/hd/engine.h>
|
||||
#include <pxr/imaging/hd/pluginRenderDelegateUniqueHandle.h>
|
||||
#include <pxr/imaging/hdx/freeCameraSceneDelegate.h>
|
||||
#include <pxr/imaging/hgi/hgi.h>
|
||||
#include <pxr/usd/usd/stage.h>
|
||||
#include <pxr/usdImaging/usdImaging/delegate.h>
|
||||
|
||||
#include "hydra/hydra_scene_delegate.h"
|
||||
#include "hydra/settings.h"
|
||||
|
||||
#include "light_tasks_delegate.h"
|
||||
#include "render_task_delegate.h"
|
||||
|
||||
struct bContext;
|
||||
struct RenderEngine;
|
||||
struct CLG_LogRef;
|
||||
|
||||
namespace blender::render::hydra {
|
||||
|
||||
extern struct CLG_LogRef *LOG_HYDRA_RENDER;
|
||||
|
||||
class Engine {
|
||||
protected:
|
||||
std::string render_delegate_name_;
|
||||
RenderEngine *bl_engine_ = nullptr;
|
||||
Depsgraph *depsgraph_ = nullptr;
|
||||
bContext *context_ = nullptr;
|
||||
Scene *scene_ = nullptr;
|
||||
|
||||
/* The order is important due to deletion order */
|
||||
pxr::HgiUniquePtr hgi_;
|
||||
pxr::HdDriver hgi_driver_;
|
||||
pxr::HdPluginRenderDelegateUniqueHandle render_delegate_;
|
||||
std::unique_ptr<pxr::HdRenderIndex> render_index_;
|
||||
|
||||
std::unique_ptr<io::hydra::HydraSceneDelegate> hydra_scene_delegate_;
|
||||
|
||||
std::unique_ptr<RenderTaskDelegate> render_task_delegate_;
|
||||
std::unique_ptr<pxr::HdxFreeCameraSceneDelegate> free_camera_delegate_;
|
||||
std::unique_ptr<LightTasksDelegate> light_tasks_delegate_;
|
||||
std::unique_ptr<pxr::HdEngine> engine_;
|
||||
|
||||
public:
|
||||
Engine(RenderEngine *bl_engine, const std::string &render_delegate_name);
|
||||
virtual ~Engine() = default;
|
||||
|
||||
void sync(Depsgraph *depsgraph, bContext *context);
|
||||
virtual void render() = 0;
|
||||
|
||||
virtual void set_render_setting(const std::string &key, const pxr::VtValue &val);
|
||||
|
||||
protected:
|
||||
float renderer_percent_done();
|
||||
pxr::HdTaskSharedPtrVector tasks();
|
||||
virtual void notify_status(float progress,
|
||||
const std::string &title,
|
||||
const std::string &info) = 0;
|
||||
};
|
||||
|
||||
} // namespace blender::render::hydra
|
137
source/blender/render/hydra/final_engine.cc
Normal file
137
source/blender/render/hydra/final_engine.cc
Normal file
@ -0,0 +1,137 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#include "final_engine.h"
|
||||
|
||||
#include <pxr/imaging/hd/light.h>
|
||||
#include <pxr/imaging/hd/renderBuffer.h>
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BLI_timecode.h"
|
||||
#include "PIL_time.h"
|
||||
|
||||
#include "BKE_lib_id.h"
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "IMB_imbuf_types.h"
|
||||
|
||||
#include "RE_engine.h"
|
||||
|
||||
#include "hydra/camera.h"
|
||||
|
||||
namespace blender::render::hydra {
|
||||
|
||||
void FinalEngine::render()
|
||||
{
|
||||
const ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph_);
|
||||
|
||||
char scene_name[MAX_ID_FULL_NAME];
|
||||
BKE_id_full_name_get(scene_name, &scene_->id, 0);
|
||||
|
||||
const RenderData &r = scene_->r;
|
||||
pxr::GfVec4f border(0, 0, 1, 1);
|
||||
if (r.mode & R_BORDER) {
|
||||
border.Set(r.border.xmin,
|
||||
r.border.ymin,
|
||||
r.border.xmax - r.border.xmin,
|
||||
r.border.ymax - r.border.ymin);
|
||||
}
|
||||
pxr::GfVec2i image_res(r.xsch * r.size / 100, r.ysch * r.size / 100);
|
||||
int width = image_res[0] * border[2];
|
||||
int height = image_res[1] * border[3];
|
||||
pxr::GfCamera camera =
|
||||
io::hydra::CameraData(scene_->camera, image_res, pxr::GfVec4f(0, 0, 1, 1)).gf_camera(border);
|
||||
|
||||
free_camera_delegate_->SetCamera(camera);
|
||||
render_task_delegate_->set_viewport(pxr::GfVec4d(0, 0, width, height));
|
||||
if (light_tasks_delegate_) {
|
||||
light_tasks_delegate_->set_viewport(pxr::GfVec4d(0, 0, width, height));
|
||||
}
|
||||
|
||||
RenderResult *rr = RE_engine_get_result(bl_engine_);
|
||||
RenderLayer *rlayer = (RenderLayer *)rr->layers.first;
|
||||
LISTBASE_FOREACH (RenderPass *, rpass, &rlayer->passes) {
|
||||
pxr::TfToken *aov_token = aov_tokens_.lookup_ptr(rpass->name);
|
||||
if (!aov_token) {
|
||||
CLOG_WARN(LOG_HYDRA_RENDER, "Couldn't find AOV token for render pass: %s", rpass->name);
|
||||
continue;
|
||||
}
|
||||
render_task_delegate_->add_aov(*aov_token);
|
||||
}
|
||||
if (bl_engine_->type->flag & RE_USE_GPU_CONTEXT) {
|
||||
/* For GPU context engine color and depth AOVs has to be added anyway */
|
||||
render_task_delegate_->add_aov(pxr::HdAovTokens->color);
|
||||
render_task_delegate_->add_aov(pxr::HdAovTokens->depth);
|
||||
}
|
||||
|
||||
render_task_delegate_->bind();
|
||||
|
||||
auto t = tasks();
|
||||
engine_->Execute(render_index_.get(), &t);
|
||||
|
||||
char elapsed_time[32];
|
||||
double time_begin = PIL_check_seconds_timer();
|
||||
float percent_done = 0.0;
|
||||
|
||||
while (true) {
|
||||
if (RE_engine_test_break(bl_engine_)) {
|
||||
break;
|
||||
}
|
||||
|
||||
percent_done = renderer_percent_done();
|
||||
BLI_timecode_string_from_time_simple(
|
||||
elapsed_time, sizeof(elapsed_time), PIL_check_seconds_timer() - time_begin);
|
||||
notify_status(percent_done / 100.0,
|
||||
std::string(scene_name) + ": " + view_layer->name,
|
||||
std::string("Render Time: ") + elapsed_time +
|
||||
" | Done: " + std::to_string(int(percent_done)) + "%");
|
||||
|
||||
if (render_task_delegate_->is_converged()) {
|
||||
break;
|
||||
}
|
||||
|
||||
update_render_result(width, height, view_layer->name);
|
||||
}
|
||||
|
||||
update_render_result(width, height, view_layer->name);
|
||||
render_task_delegate_->unbind();
|
||||
}
|
||||
|
||||
void FinalEngine::set_render_setting(const std::string &key, const pxr::VtValue &val)
|
||||
{
|
||||
if (STRPREFIX(key.c_str(), "aovToken:")) {
|
||||
aov_tokens_.add_overwrite(key.substr(key.find(":") + 1),
|
||||
pxr::TfToken(val.UncheckedGet<std::string>()));
|
||||
return;
|
||||
}
|
||||
Engine::set_render_setting(key, val);
|
||||
}
|
||||
|
||||
void FinalEngine::notify_status(float progress, const std::string &title, const std::string &info)
|
||||
{
|
||||
RE_engine_update_progress(bl_engine_, progress);
|
||||
RE_engine_update_stats(bl_engine_, title.c_str(), info.c_str());
|
||||
}
|
||||
|
||||
void FinalEngine::update_render_result(int width, int height, const char *layer_name)
|
||||
{
|
||||
RenderResult *rr = RE_engine_begin_result(bl_engine_, 0, 0, width, height, layer_name, nullptr);
|
||||
|
||||
RenderLayer *rlayer = static_cast<RenderLayer *>(
|
||||
BLI_findstring(&rr->layers, layer_name, offsetof(RenderLayer, name)));
|
||||
|
||||
if (rlayer) {
|
||||
LISTBASE_FOREACH (RenderPass *, rpass, &rlayer->passes) {
|
||||
pxr::TfToken *aov_token = aov_tokens_.lookup_ptr(rpass->name);
|
||||
if (aov_token) {
|
||||
render_task_delegate_->read_aov(*aov_token, rpass->ibuf->float_buffer.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RE_engine_end_result(bl_engine_, rr, false, false, false);
|
||||
}
|
||||
|
||||
} // namespace blender::render::hydra
|
27
source/blender/render/hydra/final_engine.h
Normal file
27
source/blender/render/hydra/final_engine.h
Normal file
@ -0,0 +1,27 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "engine.h"
|
||||
|
||||
namespace blender::render::hydra {
|
||||
|
||||
class FinalEngine : public Engine {
|
||||
private:
|
||||
Map<std::string, pxr::TfToken> aov_tokens_;
|
||||
|
||||
public:
|
||||
using Engine::Engine;
|
||||
|
||||
void render() override;
|
||||
void set_render_setting(const std::string &key, const pxr::VtValue &val) override;
|
||||
|
||||
protected:
|
||||
void notify_status(float progress, const std::string &title, const std::string &info) override;
|
||||
|
||||
private:
|
||||
void update_render_result(int width, int height, const char *layer_name);
|
||||
};
|
||||
|
||||
} // namespace blender::render::hydra
|
73
source/blender/render/hydra/light_tasks_delegate.cc
Normal file
73
source/blender/render/hydra/light_tasks_delegate.cc
Normal file
@ -0,0 +1,73 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#include "light_tasks_delegate.h"
|
||||
#include "engine.h"
|
||||
|
||||
namespace blender::render::hydra {
|
||||
|
||||
LightTasksDelegate::LightTasksDelegate(pxr::HdRenderIndex *parent_index,
|
||||
pxr::SdfPath const &delegate_id)
|
||||
: pxr::HdSceneDelegate(parent_index, delegate_id)
|
||||
{
|
||||
simple_task_id_ = GetDelegateID().AppendElementString("simpleTask");
|
||||
GetRenderIndex().InsertTask<pxr::HdxSimpleLightTask>(this, simple_task_id_);
|
||||
skydome_task_id_ = GetDelegateID().AppendElementString("skydomeTask");
|
||||
GetRenderIndex().InsertTask<pxr::HdxSkydomeTask>(this, skydome_task_id_);
|
||||
|
||||
CLOG_INFO(LOG_HYDRA_RENDER, 1, "%s", simple_task_id_.GetText());
|
||||
CLOG_INFO(LOG_HYDRA_RENDER, 1, "%s", skydome_task_id_.GetText());
|
||||
}
|
||||
|
||||
pxr::VtValue LightTasksDelegate::Get(pxr::SdfPath const &id, pxr::TfToken const &key)
|
||||
{
|
||||
CLOG_INFO(LOG_HYDRA_RENDER, 3, "%s, %s", id.GetText(), key.GetText());
|
||||
|
||||
if (key == pxr::HdTokens->params) {
|
||||
if (id == simple_task_id_) {
|
||||
return pxr::VtValue(simple_task_params_);
|
||||
}
|
||||
else if (id == skydome_task_id_) {
|
||||
return pxr::VtValue(skydome_task_params_);
|
||||
}
|
||||
}
|
||||
return pxr::VtValue();
|
||||
}
|
||||
|
||||
pxr::HdTaskSharedPtr LightTasksDelegate::simple_task()
|
||||
{
|
||||
return GetRenderIndex().GetTask(simple_task_id_);
|
||||
}
|
||||
|
||||
pxr::HdTaskSharedPtr LightTasksDelegate::skydome_task()
|
||||
{
|
||||
/* Note that this task is intended to be the first "Render Task",
|
||||
* so that the AOV's are properly cleared, however it
|
||||
* does not spawn a HdRenderPass. */
|
||||
return GetRenderIndex().GetTask(skydome_task_id_);
|
||||
}
|
||||
|
||||
void LightTasksDelegate::set_camera(pxr::SdfPath const &camera_id)
|
||||
{
|
||||
if (simple_task_params_.cameraPath == camera_id) {
|
||||
return;
|
||||
}
|
||||
simple_task_params_.cameraPath = camera_id;
|
||||
GetRenderIndex().GetChangeTracker().MarkTaskDirty(simple_task_id_,
|
||||
pxr::HdChangeTracker::DirtyParams);
|
||||
skydome_task_params_.camera = camera_id;
|
||||
GetRenderIndex().GetChangeTracker().MarkTaskDirty(skydome_task_id_,
|
||||
pxr::HdChangeTracker::DirtyParams);
|
||||
}
|
||||
|
||||
void LightTasksDelegate::set_viewport(pxr::GfVec4d const &viewport)
|
||||
{
|
||||
if (skydome_task_params_.viewport == viewport) {
|
||||
return;
|
||||
}
|
||||
skydome_task_params_.viewport = viewport;
|
||||
GetRenderIndex().GetChangeTracker().MarkTaskDirty(skydome_task_id_,
|
||||
pxr::HdChangeTracker::DirtyParams);
|
||||
}
|
||||
|
||||
} // namespace blender::render::hydra
|
32
source/blender/render/hydra/light_tasks_delegate.h
Normal file
32
source/blender/render/hydra/light_tasks_delegate.h
Normal file
@ -0,0 +1,32 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pxr/imaging/hd/sceneDelegate.h>
|
||||
#include <pxr/imaging/hdx/simpleLightTask.h>
|
||||
#include <pxr/imaging/hdx/skydomeTask.h>
|
||||
|
||||
namespace blender::render::hydra {
|
||||
|
||||
class LightTasksDelegate : public pxr::HdSceneDelegate {
|
||||
public:
|
||||
LightTasksDelegate(pxr::HdRenderIndex *parentIndex, pxr::SdfPath const &delegate_id);
|
||||
~LightTasksDelegate() override = default;
|
||||
|
||||
/* Delegate methods */
|
||||
pxr::VtValue Get(pxr::SdfPath const &id, pxr::TfToken const &key) override;
|
||||
|
||||
pxr::HdTaskSharedPtr simple_task();
|
||||
pxr::HdTaskSharedPtr skydome_task();
|
||||
void set_camera(pxr::SdfPath const &camera_id);
|
||||
void set_viewport(pxr::GfVec4d const &viewport);
|
||||
|
||||
private:
|
||||
pxr::SdfPath simple_task_id_;
|
||||
pxr::SdfPath skydome_task_id_;
|
||||
pxr::HdxSimpleLightTaskParams simple_task_params_;
|
||||
pxr::HdxRenderTaskParams skydome_task_params_;
|
||||
};
|
||||
|
||||
} // namespace blender::render::hydra
|
15
source/blender/render/hydra/preview_engine.cc
Normal file
15
source/blender/render/hydra/preview_engine.cc
Normal file
@ -0,0 +1,15 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#include "preview_engine.h"
|
||||
|
||||
namespace blender::render::hydra {
|
||||
|
||||
void PreviewEngine::notify_status(float /* progress */,
|
||||
const std::string & /* title */,
|
||||
const std::string & /* info */)
|
||||
{
|
||||
/* Empty fucntion */
|
||||
}
|
||||
|
||||
} // namespace blender::render::hydra
|
18
source/blender/render/hydra/preview_engine.h
Normal file
18
source/blender/render/hydra/preview_engine.h
Normal file
@ -0,0 +1,18 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "final_engine.h"
|
||||
|
||||
namespace blender::render::hydra {
|
||||
|
||||
class PreviewEngine : public FinalEngine {
|
||||
public:
|
||||
using FinalEngine::FinalEngine;
|
||||
|
||||
protected:
|
||||
void notify_status(float progress, const std::string &title, const std::string &info) override;
|
||||
};
|
||||
|
||||
} // namespace blender::render::hydra
|
213
source/blender/render/hydra/python.cc
Normal file
213
source/blender/render/hydra/python.cc
Normal file
@ -0,0 +1,213 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#include "final_engine.h"
|
||||
#include "preview_engine.h"
|
||||
#include "viewport_engine.h"
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "RE_engine.h"
|
||||
|
||||
#include "bpy_rna.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
|
||||
#include "RE_engine.h"
|
||||
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "hydra/image.h"
|
||||
|
||||
namespace blender::render::hydra {
|
||||
|
||||
template<typename T> T *pyrna_to_pointer(PyObject *pyobject, const StructRNA *rnatype)
|
||||
{
|
||||
const PointerRNA *ptr = pyrna_struct_as_ptr_or_null(pyobject, rnatype);
|
||||
return (ptr) ? static_cast<T *>(ptr->data) : nullptr;
|
||||
}
|
||||
|
||||
static PyObject *engine_create_func(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *pyengine;
|
||||
char *engine_type, *render_delegate_id;
|
||||
if (!PyArg_ParseTuple(args, "Oss", &pyengine, &engine_type, &render_delegate_id)) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
RenderEngine *bl_engine = pyrna_to_pointer<RenderEngine>(pyengine, &RNA_RenderEngine);
|
||||
|
||||
CLOG_INFO(LOG_HYDRA_RENDER, 1, "Engine %s", engine_type);
|
||||
Engine *engine = nullptr;
|
||||
try {
|
||||
if (STREQ(engine_type, "VIEWPORT")) {
|
||||
engine = new ViewportEngine(bl_engine, render_delegate_id);
|
||||
}
|
||||
else if (STREQ(engine_type, "PREVIEW")) {
|
||||
engine = new PreviewEngine(bl_engine, render_delegate_id);
|
||||
}
|
||||
else {
|
||||
engine = new FinalEngine(bl_engine, render_delegate_id);
|
||||
}
|
||||
}
|
||||
catch (std::runtime_error &e) {
|
||||
CLOG_ERROR(LOG_HYDRA_RENDER, "%s", e.what());
|
||||
}
|
||||
|
||||
CLOG_INFO(LOG_HYDRA_RENDER, 1, "Engine %p", engine);
|
||||
return PyLong_FromVoidPtr(engine);
|
||||
}
|
||||
|
||||
static PyObject *engine_free_func(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *pyengine;
|
||||
if (!PyArg_ParseTuple(args, "O", &pyengine)) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
Engine *engine = static_cast<Engine *>(PyLong_AsVoidPtr(pyengine));
|
||||
CLOG_INFO(LOG_HYDRA_RENDER, 1, "Engine %p", engine);
|
||||
delete engine;
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *engine_update_func(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *pyengine, *pydepsgraph, *pycontext;
|
||||
if (!PyArg_ParseTuple(args, "OOO", &pyengine, &pydepsgraph, &pycontext)) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
Engine *engine = static_cast<Engine *>(PyLong_AsVoidPtr(pyengine));
|
||||
Depsgraph *depsgraph = pyrna_to_pointer<Depsgraph>(pydepsgraph, &RNA_Depsgraph);
|
||||
bContext *context = pyrna_to_pointer<bContext>(pycontext, &RNA_Context);
|
||||
|
||||
CLOG_INFO(LOG_HYDRA_RENDER, 2, "Engine %p", engine);
|
||||
engine->sync(depsgraph, context);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *engine_render_func(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *pyengine;
|
||||
if (!PyArg_ParseTuple(args, "O", &pyengine)) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
Engine *engine = static_cast<Engine *>(PyLong_AsVoidPtr(pyengine));
|
||||
|
||||
CLOG_INFO(LOG_HYDRA_RENDER, 2, "Engine %p", engine);
|
||||
|
||||
/* Allow Blender to execute other Python scripts. */
|
||||
Py_BEGIN_ALLOW_THREADS;
|
||||
engine->render();
|
||||
Py_END_ALLOW_THREADS;
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *engine_view_draw_func(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *pyengine, *pycontext;
|
||||
if (!PyArg_ParseTuple(args, "OO", &pyengine, &pycontext)) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
ViewportEngine *engine = static_cast<ViewportEngine *>(PyLong_AsVoidPtr(pyengine));
|
||||
bContext *context = pyrna_to_pointer<bContext>(pycontext, &RNA_Context);
|
||||
|
||||
CLOG_INFO(LOG_HYDRA_RENDER, 3, "Engine %p", engine);
|
||||
|
||||
/* Allow Blender to execute other Python scripts. */
|
||||
Py_BEGIN_ALLOW_THREADS;
|
||||
engine->render(context);
|
||||
Py_END_ALLOW_THREADS;
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static pxr::VtValue get_setting_val(PyObject *pyval)
|
||||
{
|
||||
pxr::VtValue val;
|
||||
if (PyBool_Check(pyval)) {
|
||||
val = Py_IsTrue(pyval);
|
||||
}
|
||||
else if (PyLong_Check(pyval)) {
|
||||
val = PyLong_AsLong(pyval);
|
||||
}
|
||||
else if (PyFloat_Check(pyval)) {
|
||||
val = PyFloat_AsDouble(pyval);
|
||||
}
|
||||
else if (PyUnicode_Check(pyval)) {
|
||||
val = std::string(PyUnicode_AsUTF8(pyval));
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static PyObject *engine_set_render_setting_func(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *pyengine, *pyval;
|
||||
char *key;
|
||||
if (!PyArg_ParseTuple(args, "OsO", &pyengine, &key, &pyval)) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
Engine *engine = static_cast<Engine *>(PyLong_AsVoidPtr(pyengine));
|
||||
|
||||
CLOG_INFO(LOG_HYDRA_RENDER, 3, "Engine %p: %s", engine, key);
|
||||
engine->set_render_setting(key, get_setting_val(pyval));
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *cache_or_get_image_file_func(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *pycontext, *pyimage;
|
||||
if (!PyArg_ParseTuple(args, "OO", &pycontext, &pyimage)) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
bContext *context = static_cast<bContext *>(PyLong_AsVoidPtr(pycontext));
|
||||
Image *image = static_cast<Image *>(PyLong_AsVoidPtr(pyimage));
|
||||
|
||||
std::string image_path = io::hydra::cache_or_get_image_file(
|
||||
CTX_data_main(context), CTX_data_scene(context), image, nullptr);
|
||||
return PyUnicode_FromString(image_path.c_str());
|
||||
}
|
||||
|
||||
static PyMethodDef methods[] = {
|
||||
{"engine_create", engine_create_func, METH_VARARGS, ""},
|
||||
{"engine_free", engine_free_func, METH_VARARGS, ""},
|
||||
{"engine_update", engine_update_func, METH_VARARGS, ""},
|
||||
{"engine_render", engine_render_func, METH_VARARGS, ""},
|
||||
{"engine_view_draw", engine_view_draw_func, METH_VARARGS, ""},
|
||||
{"engine_set_render_setting", engine_set_render_setting_func, METH_VARARGS, ""},
|
||||
|
||||
{"cache_or_get_image_file", cache_or_get_image_file_func, METH_VARARGS, ""},
|
||||
|
||||
{NULL, NULL, 0, NULL},
|
||||
};
|
||||
|
||||
static struct PyModuleDef module = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"_bpy_hydra",
|
||||
"Hydra render API",
|
||||
-1,
|
||||
methods,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
|
||||
} // namespace blender::render::hydra
|
||||
|
||||
PyObject *BPyInit_hydra();
|
||||
|
||||
PyObject *BPyInit_hydra()
|
||||
{
|
||||
PyObject *mod = PyModule_Create(&blender::render::hydra::module);
|
||||
return mod;
|
||||
}
|
342
source/blender/render/hydra/render_task_delegate.cc
Normal file
342
source/blender/render/hydra/render_task_delegate.cc
Normal file
@ -0,0 +1,342 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#include "render_task_delegate.h"
|
||||
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
#include "GPU_context.h"
|
||||
|
||||
#include <pxr/imaging/hd/renderBuffer.h>
|
||||
#include <pxr/imaging/hd/renderDelegate.h>
|
||||
#include <pxr/imaging/hdx/renderTask.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "Eigen/Core"
|
||||
|
||||
#include "engine.h"
|
||||
|
||||
namespace blender::render::hydra {
|
||||
|
||||
RenderTaskDelegate::RenderTaskDelegate(pxr::HdRenderIndex *parent_index,
|
||||
pxr::SdfPath const &delegate_id)
|
||||
: pxr::HdSceneDelegate(parent_index, delegate_id)
|
||||
{
|
||||
task_id_ = GetDelegateID().AppendElementString("task");
|
||||
GetRenderIndex().InsertTask<pxr::HdxRenderTask>(this, task_id_);
|
||||
|
||||
task_params_.enableLighting = true;
|
||||
task_params_.alphaThreshold = 0.1f;
|
||||
|
||||
CLOG_INFO(LOG_HYDRA_RENDER, 1, "%s", task_id_.GetText());
|
||||
}
|
||||
|
||||
pxr::VtValue RenderTaskDelegate::Get(pxr::SdfPath const &id, pxr::TfToken const &key)
|
||||
{
|
||||
CLOG_INFO(LOG_HYDRA_RENDER, 3, "%s, %s", id.GetText(), key.GetText());
|
||||
|
||||
if (key == pxr::HdTokens->params) {
|
||||
return pxr::VtValue(task_params_);
|
||||
}
|
||||
if (key == pxr::HdTokens->collection) {
|
||||
return pxr::VtValue(pxr::HdRprimCollection(
|
||||
pxr::HdTokens->geometry, pxr::HdReprSelector(pxr::HdReprTokens->smoothHull)));
|
||||
}
|
||||
return pxr::VtValue();
|
||||
}
|
||||
|
||||
pxr::TfTokenVector RenderTaskDelegate::GetTaskRenderTags(pxr::SdfPath const &id)
|
||||
{
|
||||
CLOG_INFO(LOG_HYDRA_RENDER, 3, "%s", id.GetText());
|
||||
|
||||
return {pxr::HdRenderTagTokens->geometry};
|
||||
}
|
||||
|
||||
pxr::HdRenderBufferDescriptor RenderTaskDelegate::GetRenderBufferDescriptor(pxr::SdfPath const &id)
|
||||
{
|
||||
CLOG_INFO(LOG_HYDRA_RENDER, 3, "%s", id.GetText());
|
||||
|
||||
return buffer_descriptors_[id];
|
||||
}
|
||||
|
||||
pxr::HdTaskSharedPtr RenderTaskDelegate::task()
|
||||
{
|
||||
return GetRenderIndex().GetTask(task_id_);
|
||||
}
|
||||
|
||||
void RenderTaskDelegate::set_camera(pxr::SdfPath const &camera_id)
|
||||
{
|
||||
if (task_params_.camera == camera_id) {
|
||||
return;
|
||||
}
|
||||
task_params_.camera = camera_id;
|
||||
GetRenderIndex().GetChangeTracker().MarkTaskDirty(task_id_, pxr::HdChangeTracker::DirtyParams);
|
||||
}
|
||||
|
||||
bool RenderTaskDelegate::is_converged()
|
||||
{
|
||||
return static_cast<pxr::HdxRenderTask *>(task().get())->IsConverged();
|
||||
}
|
||||
|
||||
void RenderTaskDelegate::set_viewport(pxr::GfVec4d const &viewport)
|
||||
{
|
||||
if (task_params_.viewport == viewport) {
|
||||
return;
|
||||
}
|
||||
auto &render_index = GetRenderIndex();
|
||||
task_params_.viewport = viewport;
|
||||
render_index.GetChangeTracker().MarkTaskDirty(task_id_, pxr::HdChangeTracker::DirtyParams);
|
||||
|
||||
int w = viewport[2] - viewport[0];
|
||||
int h = viewport[3] - viewport[1];
|
||||
for (auto &it : buffer_descriptors_) {
|
||||
it.second.dimensions = pxr::GfVec3i(w, h, 1);
|
||||
render_index.GetChangeTracker().MarkBprimDirty(it.first,
|
||||
pxr::HdRenderBuffer::DirtyDescription);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderTaskDelegate::add_aov(pxr::TfToken const &aov_key)
|
||||
{
|
||||
pxr::SdfPath buf_id = buffer_id(aov_key);
|
||||
if (buffer_descriptors_.find(buf_id) != buffer_descriptors_.end()) {
|
||||
return;
|
||||
}
|
||||
auto &render_index = GetRenderIndex();
|
||||
pxr::HdAovDescriptor aov_desc = render_index.GetRenderDelegate()->GetDefaultAovDescriptor(
|
||||
aov_key);
|
||||
|
||||
if (aov_desc.format == pxr::HdFormatInvalid) {
|
||||
CLOG_ERROR(LOG_HYDRA_RENDER, "Invalid AOV: %s", aov_key.GetText());
|
||||
return;
|
||||
}
|
||||
if (!ELEM(
|
||||
pxr::HdGetComponentFormat(aov_desc.format), pxr::HdFormatFloat32, pxr::HdFormatFloat16))
|
||||
{
|
||||
CLOG_WARN(LOG_HYDRA_RENDER,
|
||||
"Unsupported data format %s for AOV %s",
|
||||
pxr::TfEnum::GetName(aov_desc.format).c_str(),
|
||||
aov_key.GetText());
|
||||
return;
|
||||
}
|
||||
|
||||
int w = task_params_.viewport[2] - task_params_.viewport[0];
|
||||
int h = task_params_.viewport[3] - task_params_.viewport[1];
|
||||
render_index.InsertBprim(pxr::HdPrimTypeTokens->renderBuffer, this, buf_id);
|
||||
buffer_descriptors_[buf_id] = pxr::HdRenderBufferDescriptor(
|
||||
pxr::GfVec3i(w, h, 1), aov_desc.format, aov_desc.multiSampled);
|
||||
|
||||
pxr::HdRenderPassAovBinding binding;
|
||||
binding.aovName = aov_key;
|
||||
binding.renderBufferId = buf_id;
|
||||
binding.aovSettings = aov_desc.aovSettings;
|
||||
binding.clearValue = aov_desc.clearValue;
|
||||
task_params_.aovBindings.push_back(binding);
|
||||
render_index.GetChangeTracker().MarkTaskDirty(task_id_, pxr::HdChangeTracker::DirtyParams);
|
||||
|
||||
CLOG_INFO(LOG_HYDRA_RENDER, 1, "%s", aov_key.GetText());
|
||||
}
|
||||
|
||||
void RenderTaskDelegate::read_aov(pxr::TfToken const &aov_key, void *data)
|
||||
{
|
||||
pxr::HdRenderBuffer *buffer = static_cast<pxr::HdRenderBuffer *>(
|
||||
GetRenderIndex().GetBprim(pxr::HdPrimTypeTokens->renderBuffer, buffer_id(aov_key)));
|
||||
if (!buffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
pxr::HdFormat format = buffer->GetFormat();
|
||||
size_t len = buffer->GetWidth() * buffer->GetHeight() * pxr::HdGetComponentCount(format);
|
||||
if (pxr::HdGetComponentFormat(format) == pxr::HdFormatFloat32) {
|
||||
void *buf_data = buffer->Map();
|
||||
memcpy(data, buf_data, len * sizeof(float));
|
||||
buffer->Unmap();
|
||||
}
|
||||
else if (pxr::HdGetComponentFormat(format) == pxr::HdFormatFloat16) {
|
||||
Eigen::half *buf_data = (Eigen::half *)buffer->Map();
|
||||
float *fdata = (float *)data;
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
fdata[i] = buf_data[i];
|
||||
}
|
||||
buffer->Unmap();
|
||||
}
|
||||
else {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
void RenderTaskDelegate::read_aov(pxr::TfToken const &aov_key, GPUTexture *texture)
|
||||
{
|
||||
pxr::HdRenderBuffer *buffer = (pxr::HdRenderBuffer *)GetRenderIndex().GetBprim(
|
||||
pxr::HdPrimTypeTokens->renderBuffer, buffer_id(aov_key));
|
||||
if (!buffer) {
|
||||
return;
|
||||
}
|
||||
eGPUDataFormat format = buffer->GetFormat() == pxr::HdFormat::HdFormatFloat16Vec4 ?
|
||||
GPU_DATA_HALF_FLOAT :
|
||||
GPU_DATA_FLOAT;
|
||||
void *buf_data = buffer->Map();
|
||||
GPU_texture_update(texture, format, buf_data);
|
||||
buffer->Unmap();
|
||||
}
|
||||
|
||||
void RenderTaskDelegate::bind() {}
|
||||
|
||||
void RenderTaskDelegate::unbind() {}
|
||||
|
||||
pxr::SdfPath RenderTaskDelegate::buffer_id(pxr::TfToken const &aov_key) const
|
||||
{
|
||||
return GetDelegateID().AppendElementString("aov_" + aov_key.GetString());
|
||||
}
|
||||
|
||||
GPURenderTaskDelegate::~GPURenderTaskDelegate()
|
||||
{
|
||||
unbind();
|
||||
if (tex_color_) {
|
||||
GPU_texture_free(tex_color_);
|
||||
}
|
||||
if (tex_depth_) {
|
||||
GPU_texture_free(tex_depth_);
|
||||
}
|
||||
}
|
||||
|
||||
void GPURenderTaskDelegate::set_viewport(pxr::GfVec4d const &viewport)
|
||||
{
|
||||
if (task_params_.viewport == viewport) {
|
||||
return;
|
||||
}
|
||||
auto &render_index = GetRenderIndex();
|
||||
task_params_.viewport = viewport;
|
||||
render_index.GetChangeTracker().MarkTaskDirty(task_id_, pxr::HdChangeTracker::DirtyParams);
|
||||
|
||||
if (tex_color_) {
|
||||
GPU_texture_free(tex_color_);
|
||||
tex_color_ = nullptr;
|
||||
add_aov(pxr::HdAovTokens->color);
|
||||
}
|
||||
if (tex_depth_) {
|
||||
GPU_texture_free(tex_depth_);
|
||||
tex_depth_ = nullptr;
|
||||
add_aov(pxr::HdAovTokens->depth);
|
||||
}
|
||||
}
|
||||
|
||||
void GPURenderTaskDelegate::add_aov(pxr::TfToken const &aov_key)
|
||||
{
|
||||
eGPUTextureFormat format;
|
||||
GPUTexture **tex;
|
||||
if (aov_key == pxr::HdAovTokens->color) {
|
||||
format = GPU_RGBA32F;
|
||||
tex = &tex_color_;
|
||||
}
|
||||
else if (aov_key == pxr::HdAovTokens->depth) {
|
||||
format = GPU_DEPTH_COMPONENT32F;
|
||||
tex = &tex_depth_;
|
||||
}
|
||||
else {
|
||||
CLOG_ERROR(LOG_HYDRA_RENDER, "Invalid AOV: %s", aov_key.GetText());
|
||||
return;
|
||||
}
|
||||
|
||||
if (*tex) {
|
||||
return;
|
||||
}
|
||||
|
||||
*tex = GPU_texture_create_2d(("tex_render_hydra_" + aov_key.GetString()).c_str(),
|
||||
task_params_.viewport[2] - task_params_.viewport[0],
|
||||
task_params_.viewport[3] - task_params_.viewport[1],
|
||||
1,
|
||||
format,
|
||||
GPU_TEXTURE_USAGE_GENERAL,
|
||||
nullptr);
|
||||
|
||||
CLOG_INFO(LOG_HYDRA_RENDER, 1, "%s", aov_key.GetText());
|
||||
}
|
||||
|
||||
void GPURenderTaskDelegate::read_aov(pxr::TfToken const &aov_key, void *data)
|
||||
{
|
||||
GPUTexture *tex = nullptr;
|
||||
int c;
|
||||
if (aov_key == pxr::HdAovTokens->color) {
|
||||
tex = tex_color_;
|
||||
c = 4;
|
||||
}
|
||||
else if (aov_key == pxr::HdAovTokens->depth) {
|
||||
tex = tex_depth_;
|
||||
c = 1;
|
||||
}
|
||||
if (!tex) {
|
||||
return;
|
||||
}
|
||||
|
||||
int w = GPU_texture_width(tex), h = GPU_texture_height(tex);
|
||||
void *tex_data = GPU_texture_read(tex, GPU_DATA_FLOAT, 0);
|
||||
memcpy(data, tex_data, sizeof(float) * w * h * c);
|
||||
MEM_freeN(tex_data);
|
||||
}
|
||||
|
||||
void GPURenderTaskDelegate::read_aov(pxr::TfToken const &aov_key, GPUTexture *texture)
|
||||
{
|
||||
GPUTexture *tex = nullptr;
|
||||
if (aov_key == pxr::HdAovTokens->color) {
|
||||
tex = tex_color_;
|
||||
}
|
||||
else if (aov_key == pxr::HdAovTokens->depth) {
|
||||
tex = tex_depth_;
|
||||
}
|
||||
if (!tex) {
|
||||
return;
|
||||
}
|
||||
|
||||
void *tex_data = GPU_texture_read(tex, GPU_DATA_FLOAT, 0);
|
||||
GPU_texture_update(texture, GPU_DATA_FLOAT, tex_data);
|
||||
MEM_freeN(tex_data);
|
||||
}
|
||||
|
||||
void GPURenderTaskDelegate::bind()
|
||||
{
|
||||
if (!framebuffer_) {
|
||||
framebuffer_ = GPU_framebuffer_create("fb_render_hydra");
|
||||
}
|
||||
GPU_framebuffer_ensure_config(
|
||||
&framebuffer_, {GPU_ATTACHMENT_TEXTURE(tex_depth_), GPU_ATTACHMENT_TEXTURE(tex_color_)});
|
||||
GPU_framebuffer_bind(framebuffer_);
|
||||
|
||||
float clear_color[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
GPU_framebuffer_clear_color_depth(framebuffer_, clear_color, 1.0f);
|
||||
|
||||
/* Workaround missing/buggy VAOs in hgiGL and hdSt. For OpenGL compatibility
|
||||
* profile this is not a problem, but for core profile it is. */
|
||||
if (VAO_ == 0 && GPU_backend_get_type() == GPU_BACKEND_OPENGL) {
|
||||
glGenVertexArrays(1, &VAO_);
|
||||
glBindVertexArray(VAO_);
|
||||
}
|
||||
CLOG_INFO(LOG_HYDRA_RENDER, 3, "bind");
|
||||
}
|
||||
|
||||
void GPURenderTaskDelegate::unbind()
|
||||
{
|
||||
if (VAO_) {
|
||||
glDeleteVertexArrays(1, &VAO_);
|
||||
VAO_ = 0;
|
||||
}
|
||||
if (framebuffer_) {
|
||||
GPU_framebuffer_free(framebuffer_);
|
||||
framebuffer_ = nullptr;
|
||||
}
|
||||
CLOG_INFO(LOG_HYDRA_RENDER, 3, "unbind");
|
||||
}
|
||||
|
||||
GPUTexture *GPURenderTaskDelegate::aov_texture(pxr::TfToken const &aov_key)
|
||||
{
|
||||
if (aov_key == pxr::HdAovTokens->color) {
|
||||
return tex_color_;
|
||||
}
|
||||
if (aov_key == pxr::HdAovTokens->depth) {
|
||||
return tex_depth_;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace blender::render::hydra
|
66
source/blender/render/hydra/render_task_delegate.h
Normal file
66
source/blender/render/hydra/render_task_delegate.h
Normal file
@ -0,0 +1,66 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pxr/imaging/hd/sceneDelegate.h>
|
||||
#include <pxr/imaging/hdx/renderSetupTask.h>
|
||||
|
||||
#include "GPU_framebuffer.h"
|
||||
#include "GPU_texture.h"
|
||||
|
||||
namespace blender::render::hydra {
|
||||
|
||||
/* Delegate to create a render task with given camera, viewport and AOVs. */
|
||||
|
||||
class RenderTaskDelegate : public pxr::HdSceneDelegate {
|
||||
protected:
|
||||
pxr::SdfPath task_id_;
|
||||
pxr::HdxRenderTaskParams task_params_;
|
||||
pxr::TfHashMap<pxr::SdfPath, pxr::HdRenderBufferDescriptor, pxr::SdfPath::Hash>
|
||||
buffer_descriptors_;
|
||||
|
||||
public:
|
||||
RenderTaskDelegate(pxr::HdRenderIndex *parent_index, pxr::SdfPath const &delegate_id);
|
||||
~RenderTaskDelegate() override = default;
|
||||
|
||||
/* Delegate methods */
|
||||
pxr::VtValue Get(pxr::SdfPath const &id, pxr::TfToken const &key) override;
|
||||
pxr::TfTokenVector GetTaskRenderTags(pxr::SdfPath const &id) override;
|
||||
pxr::HdRenderBufferDescriptor GetRenderBufferDescriptor(pxr::SdfPath const &id) override;
|
||||
|
||||
pxr::HdTaskSharedPtr task();
|
||||
void set_camera(pxr::SdfPath const &camera_id);
|
||||
bool is_converged();
|
||||
virtual void set_viewport(pxr::GfVec4d const &viewport);
|
||||
virtual void add_aov(pxr::TfToken const &aov_key);
|
||||
virtual void read_aov(pxr::TfToken const &aov_key, void *data);
|
||||
virtual void read_aov(pxr::TfToken const &aov_key, GPUTexture *texture);
|
||||
virtual void bind();
|
||||
virtual void unbind();
|
||||
|
||||
protected:
|
||||
pxr::SdfPath buffer_id(pxr::TfToken const &aov_key) const;
|
||||
};
|
||||
|
||||
class GPURenderTaskDelegate : public RenderTaskDelegate {
|
||||
private:
|
||||
GPUFrameBuffer *framebuffer_ = nullptr;
|
||||
GPUTexture *tex_color_ = nullptr;
|
||||
GPUTexture *tex_depth_ = nullptr;
|
||||
unsigned int VAO_ = 0;
|
||||
|
||||
public:
|
||||
using RenderTaskDelegate::RenderTaskDelegate;
|
||||
~GPURenderTaskDelegate() override;
|
||||
|
||||
void set_viewport(pxr::GfVec4d const &viewport) override;
|
||||
void add_aov(pxr::TfToken const &aov_key) override;
|
||||
void read_aov(pxr::TfToken const &aov_key, void *data) override;
|
||||
void read_aov(pxr::TfToken const &aov_key, GPUTexture *texture) override;
|
||||
void bind() override;
|
||||
void unbind() override;
|
||||
GPUTexture *aov_texture(pxr::TfToken const &aov_key);
|
||||
};
|
||||
|
||||
} // namespace blender::render::hydra
|
0
source/blender/render/hydra/settings.h
Normal file
0
source/blender/render/hydra/settings.h
Normal file
295
source/blender/render/hydra/viewport_engine.cc
Normal file
295
source/blender/render/hydra/viewport_engine.cc
Normal file
@ -0,0 +1,295 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#include "viewport_engine.h"
|
||||
|
||||
#include <pxr/base/gf/camera.h>
|
||||
#include <pxr/imaging/glf/drawTarget.h>
|
||||
#include <pxr/usd/usdGeom/camera.h>
|
||||
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_vec_types.h" /* this include must be before BKE_camera.h due to "rctf" type */
|
||||
#include "DNA_view3d_types.h"
|
||||
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_timecode.h"
|
||||
#include "PIL_time.h"
|
||||
|
||||
#include "BKE_camera.h"
|
||||
#include "BKE_context.h"
|
||||
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "GPU_context.h"
|
||||
#include "GPU_matrix.h"
|
||||
|
||||
#include "RE_engine.h"
|
||||
|
||||
#include "hydra/camera.h"
|
||||
|
||||
namespace blender::render::hydra {
|
||||
|
||||
struct ViewSettings {
|
||||
ViewSettings(bContext *context);
|
||||
|
||||
int width();
|
||||
int height();
|
||||
|
||||
pxr::GfCamera gf_camera();
|
||||
|
||||
io::hydra::CameraData camera_data;
|
||||
|
||||
int screen_width;
|
||||
int screen_height;
|
||||
pxr::GfVec4i border;
|
||||
};
|
||||
|
||||
ViewSettings::ViewSettings(bContext *context)
|
||||
: camera_data(CTX_wm_view3d(context), CTX_wm_region(context))
|
||||
{
|
||||
View3D *view3d = CTX_wm_view3d(context);
|
||||
RegionView3D *region_data = static_cast<RegionView3D *>(CTX_wm_region_data(context));
|
||||
ARegion *region = CTX_wm_region(context);
|
||||
|
||||
screen_width = region->winx;
|
||||
screen_height = region->winy;
|
||||
|
||||
Scene *scene = CTX_data_scene(context);
|
||||
|
||||
/* Getting render border. */
|
||||
int x1 = 0, y1 = 0;
|
||||
int x2 = screen_width, y2 = screen_height;
|
||||
|
||||
if (region_data->persp == RV3D_CAMOB) {
|
||||
if (scene->r.mode & R_BORDER) {
|
||||
Object *camera_obj = scene->camera;
|
||||
|
||||
float camera_points[4][3];
|
||||
BKE_camera_view_frame(scene, static_cast<Camera *>(camera_obj->data), camera_points);
|
||||
|
||||
float screen_points[4][2];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
float world_location[] = {
|
||||
camera_points[i][0], camera_points[i][1], camera_points[i][2], 1.0f};
|
||||
mul_m4_v4(camera_obj->object_to_world, world_location);
|
||||
mul_m4_v4(region_data->persmat, world_location);
|
||||
|
||||
if (world_location[3] > 0.0) {
|
||||
screen_points[i][0] = screen_width * 0.5f +
|
||||
screen_width * 0.5f * (world_location[0] / world_location[3]);
|
||||
screen_points[i][1] = screen_height * 0.5f +
|
||||
screen_height * 0.5f * (world_location[1] / world_location[3]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Getting camera view region. */
|
||||
float x1_f = std::min(
|
||||
{screen_points[0][0], screen_points[1][0], screen_points[2][0], screen_points[3][0]});
|
||||
float x2_f = std::max(
|
||||
{screen_points[0][0], screen_points[1][0], screen_points[2][0], screen_points[3][0]});
|
||||
float y1_f = std::min(
|
||||
{screen_points[0][1], screen_points[1][1], screen_points[2][1], screen_points[3][1]});
|
||||
float y2_f = std::max(
|
||||
{screen_points[0][1], screen_points[1][1], screen_points[2][1], screen_points[3][1]});
|
||||
|
||||
/* Adjusting region to border. */
|
||||
float x = x1_f, y = y1_f;
|
||||
float dx = x2_f - x1_f, dy = y2_f - y1_f;
|
||||
|
||||
x1 = x + scene->r.border.xmin * dx;
|
||||
x2 = x + scene->r.border.xmax * dx;
|
||||
y1 = y + scene->r.border.ymin * dy;
|
||||
y2 = y + scene->r.border.ymax * dy;
|
||||
|
||||
/* Adjusting to region screen resolution. */
|
||||
x1 = std::max(std::min(x1, screen_width), 0);
|
||||
x2 = std::max(std::min(x2, screen_width), 0);
|
||||
y1 = std::max(std::min(y1, screen_height), 0);
|
||||
y2 = std::max(std::min(y2, screen_height), 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (view3d->flag2 & V3D_RENDER_BORDER) {
|
||||
int x = x1, y = y1;
|
||||
int dx = x2 - x1, dy = y2 - y1;
|
||||
|
||||
x1 = int(x + view3d->render_border.xmin * dx);
|
||||
x2 = int(x + view3d->render_border.xmax * dx);
|
||||
y1 = int(y + view3d->render_border.ymin * dy);
|
||||
y2 = int(y + view3d->render_border.ymax * dy);
|
||||
}
|
||||
}
|
||||
|
||||
border = pxr::GfVec4i(x1, y1, x2 - x1, y2 - y1);
|
||||
}
|
||||
|
||||
int ViewSettings::width()
|
||||
{
|
||||
return border[2];
|
||||
}
|
||||
|
||||
int ViewSettings::height()
|
||||
{
|
||||
return border[3];
|
||||
}
|
||||
|
||||
pxr::GfCamera ViewSettings::gf_camera()
|
||||
{
|
||||
return camera_data.gf_camera(pxr::GfVec4f((float)border[0] / screen_width,
|
||||
(float)border[1] / screen_height,
|
||||
(float)border[2] / screen_width,
|
||||
(float)border[3] / screen_height));
|
||||
}
|
||||
|
||||
DrawTexture::DrawTexture()
|
||||
{
|
||||
float coords[8] = {0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0};
|
||||
|
||||
GPUVertFormat format = {0};
|
||||
GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
||||
GPU_vertformat_attr_add(&format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
||||
GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
|
||||
GPU_vertbuf_data_alloc(vbo, 4);
|
||||
GPU_vertbuf_attr_fill(vbo, 0, coords);
|
||||
GPU_vertbuf_attr_fill(vbo, 1, coords);
|
||||
|
||||
batch_ = GPU_batch_create_ex(GPU_PRIM_TRI_FAN, vbo, nullptr, GPU_BATCH_OWNS_VBO);
|
||||
}
|
||||
|
||||
DrawTexture::~DrawTexture()
|
||||
{
|
||||
if (texture_) {
|
||||
GPU_texture_free(texture_);
|
||||
}
|
||||
GPU_batch_discard(batch_);
|
||||
}
|
||||
|
||||
void DrawTexture::write_data(int width, int height, const void *data)
|
||||
{
|
||||
if (texture_ && width == GPU_texture_width(texture_) && height == GPU_texture_height(texture_)) {
|
||||
if (data) {
|
||||
GPU_texture_update(texture_, GPU_DATA_FLOAT, data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (texture_) {
|
||||
GPU_texture_free(texture_);
|
||||
}
|
||||
|
||||
texture_ = GPU_texture_create_2d("tex_hydra_render_viewport",
|
||||
width,
|
||||
height,
|
||||
1,
|
||||
GPU_RGBA32F,
|
||||
GPU_TEXTURE_USAGE_GENERAL,
|
||||
(float *)data);
|
||||
}
|
||||
|
||||
void DrawTexture::draw(GPUShader *shader, const pxr::GfVec4d &viewport, GPUTexture *tex)
|
||||
{
|
||||
if (!tex) {
|
||||
tex = texture_;
|
||||
}
|
||||
int slot = GPU_shader_get_sampler_binding(shader, "image");
|
||||
GPU_texture_bind(tex, slot);
|
||||
GPU_shader_uniform_1i(shader, "image", slot);
|
||||
|
||||
GPU_matrix_push();
|
||||
GPU_matrix_translate_2f(viewport[0], viewport[1]);
|
||||
GPU_matrix_scale_2f(viewport[2] - viewport[0], viewport[3] - viewport[1]);
|
||||
GPU_batch_set_shader(batch_, shader);
|
||||
GPU_batch_draw(batch_);
|
||||
GPU_matrix_pop();
|
||||
}
|
||||
|
||||
GPUTexture *DrawTexture::texture() const
|
||||
{
|
||||
return texture_;
|
||||
}
|
||||
|
||||
void ViewportEngine::render()
|
||||
{
|
||||
ViewSettings view_settings(context_);
|
||||
if (view_settings.width() * view_settings.height() == 0) {
|
||||
return;
|
||||
};
|
||||
|
||||
pxr::GfCamera gf_camera = view_settings.gf_camera();
|
||||
free_camera_delegate_->SetCamera(gf_camera);
|
||||
|
||||
pxr::GfVec4d viewport(view_settings.border[0],
|
||||
view_settings.border[1],
|
||||
view_settings.border[2],
|
||||
view_settings.border[3]);
|
||||
render_task_delegate_->set_viewport(viewport);
|
||||
if (light_tasks_delegate_) {
|
||||
light_tasks_delegate_->set_viewport(viewport);
|
||||
}
|
||||
|
||||
render_task_delegate_->add_aov(pxr::HdAovTokens->color);
|
||||
render_task_delegate_->add_aov(pxr::HdAovTokens->depth);
|
||||
|
||||
GPUFrameBuffer *view_framebuffer = GPU_framebuffer_active_get();
|
||||
render_task_delegate_->bind();
|
||||
|
||||
auto t = tasks();
|
||||
engine_->Execute(render_index_.get(), &t);
|
||||
|
||||
render_task_delegate_->unbind();
|
||||
|
||||
GPU_framebuffer_bind(view_framebuffer);
|
||||
GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_3D_IMAGE);
|
||||
GPU_shader_bind(shader);
|
||||
|
||||
GPURenderTaskDelegate *gpu_task = dynamic_cast<GPURenderTaskDelegate *>(
|
||||
render_task_delegate_.get());
|
||||
if (gpu_task) {
|
||||
draw_texture_.draw(shader, viewport, gpu_task->aov_texture(pxr::HdAovTokens->color));
|
||||
}
|
||||
else {
|
||||
draw_texture_.write_data(view_settings.width(), view_settings.height(), nullptr);
|
||||
render_task_delegate_->read_aov(pxr::HdAovTokens->color, draw_texture_.texture());
|
||||
draw_texture_.draw(shader, viewport);
|
||||
}
|
||||
|
||||
GPU_shader_unbind();
|
||||
|
||||
if (renderer_percent_done() == 0.0f) {
|
||||
time_begin_ = PIL_check_seconds_timer();
|
||||
}
|
||||
|
||||
char elapsed_time[32];
|
||||
|
||||
BLI_timecode_string_from_time_simple(
|
||||
elapsed_time, sizeof(elapsed_time), PIL_check_seconds_timer() - time_begin_);
|
||||
|
||||
float percent_done = renderer_percent_done();
|
||||
if (!render_task_delegate_->is_converged()) {
|
||||
notify_status(percent_done / 100.0,
|
||||
std ::string("Time: ") + elapsed_time +
|
||||
" | Done: " + std::to_string(int(percent_done)) + "%",
|
||||
"Render");
|
||||
bl_engine_->flag |= RE_ENGINE_DO_DRAW;
|
||||
}
|
||||
else {
|
||||
notify_status(percent_done / 100.0, std::string("Time: ") + elapsed_time, "Rendering Done");
|
||||
}
|
||||
}
|
||||
|
||||
void ViewportEngine::render(bContext *context)
|
||||
{
|
||||
context_ = context;
|
||||
render();
|
||||
}
|
||||
|
||||
void ViewportEngine::notify_status(float /*progress*/,
|
||||
const std::string &info,
|
||||
const std::string &status)
|
||||
{
|
||||
RE_engine_update_stats(bl_engine_, status.c_str(), info.c_str());
|
||||
}
|
||||
|
||||
} // namespace blender::render::hydra
|
47
source/blender/render/hydra/viewport_engine.h
Normal file
47
source/blender/render/hydra/viewport_engine.h
Normal file
@ -0,0 +1,47 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pxr/imaging/hd/renderBuffer.h>
|
||||
|
||||
#include "GPU_batch.h"
|
||||
#include "GPU_shader.h"
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "engine.h"
|
||||
|
||||
namespace blender::render::hydra {
|
||||
|
||||
class DrawTexture {
|
||||
private:
|
||||
GPUTexture *texture_ = nullptr;
|
||||
GPUBatch *batch_;
|
||||
|
||||
public:
|
||||
DrawTexture();
|
||||
~DrawTexture();
|
||||
|
||||
void write_data(int width, int height, const void *data);
|
||||
void draw(GPUShader *shader, const pxr::GfVec4d &viewport, GPUTexture *tex = nullptr);
|
||||
GPUTexture *texture() const;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
class ViewportEngine : public Engine {
|
||||
private:
|
||||
double time_begin_;
|
||||
DrawTexture draw_texture_;
|
||||
|
||||
public:
|
||||
using Engine::Engine;
|
||||
|
||||
void render() override;
|
||||
void render(bContext *context);
|
||||
|
||||
protected:
|
||||
void notify_status(float progress, const std::string &title, const std::string &info) override;
|
||||
};
|
||||
|
||||
} // namespace blender::render::hydra
|
@ -853,9 +853,16 @@ static void engine_render_view_layer(Render *re,
|
||||
|
||||
/* Sync data to engine, within draw lock so scene data can be accessed safely. */
|
||||
if (use_engine) {
|
||||
const bool use_gpu_context = (engine->type->flag & RE_USE_GPU_CONTEXT);
|
||||
if (use_gpu_context) {
|
||||
DRW_render_context_enable(engine->re);
|
||||
}
|
||||
if (engine->type->update) {
|
||||
engine->type->update(engine, re->main, engine->depsgraph);
|
||||
}
|
||||
if (use_gpu_context) {
|
||||
DRW_render_context_disable(engine->re);
|
||||
}
|
||||
}
|
||||
|
||||
if (re->draw_lock) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user