Files
workinf_Blender_Wasm/blender-5.2.0/intern/cycles/hydra/mesh.cpp
2026-08-12 04:47:48 -04:00

618 lines
22 KiB
C++

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