blender/intern/cycles/hydra/geometry.inl
Patrick Mours c31b89e76e Cycles: Add support for "stageMetersPerUnit" Hydra render setting
This can be useful to match transforms to what native Cycles
would see in Blender, as USD typically uses centimeters, but
Blender uses meters. This patch also fixes the hardcoded focal
length multiplicator, which is now using the same units as
everything else. Default of "stageMetersPerUnit" is 0.01 to match
the USD default of centimeters.

Differential Revision: https://developer.blender.org/D14630
2022-04-13 12:24:21 +02:00

252 lines
7.7 KiB
C++

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