/* SPDX-License-Identifier: Apache-2.0 * Copyright 2022 NVIDIA Corporation * Copyright 2022 Blender Foundation */ #include "hydra/mesh.h" #include "hydra/geometry.inl" #include "scene/mesh.h" #include #include HDCYCLES_NAMESPACE_OPEN_SCOPE namespace { template VtValue ComputeTriangulatedUniformPrimvar(VtValue value, const VtIntArray &primitiveParams) { T output; output.reserve(primitiveParams.size()); const T &input = value.Get(); 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(value, primitiveParams); case HdTypeFloatVec2: return ComputeTriangulatedUniformPrimvar(value, primitiveParams); case HdTypeFloatVec3: return ComputeTriangulatedUniformPrimvar(value, primitiveParams); case HdTypeFloatVec4: return ComputeTriangulatedUniformPrimvar(value, primitiveParams); default: TF_RUNTIME_ERROR("Unsupported attribute type %d", static_cast(valueType)); return VtValue(); } } VtValue ComputeTriangulatedFaceVaryingPrimvar(VtValue value, const HdType valueType, HdMeshUtil &meshUtil) { if (meshUtil.ComputeTriangulatedFaceVaryingPrimvar( HdGetValueData(value), value.GetArraySize(), valueType, &value)) { 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 #if PXR_VERSION < 2102 , const SdfPath &instancerId #endif ) : HdCyclesGeometry(rprimId #if PXR_VERSION < 2102 , instancerId #endif ), _util(&_topology, rprimId) { } HdCyclesMesh::~HdCyclesMesh() { } 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) { VtValue value; for (const HdExtComputationPrimvarDescriptor &desc : sceneDelegate->GetExtComputationPrimvarDescriptors(GetId(), HdInterpolationVertex)) { if (desc.name == HdTokens->points) { auto valueStore = HdExtComputationUtils::GetComputedPrimvarValues({desc}, sceneDelegate); const auto valueStoreIt = valueStore.find(desc.name); if (valueStoreIt != valueStore.end()) { value = std::move(valueStoreIt->second); } break; } } if (value.IsEmpty()) { value = GetPoints(sceneDelegate); } if (!value.IsHolding()) { TF_WARN("Invalid points data for %s", GetId().GetText()); return; } const auto &points = value.UncheckedGet(); TF_VERIFY(points.size() >= static_cast(_topology.GetNumPoints())); array pointsDataCycles; pointsDataCycles.reserve(points.size()); for (const GfVec3f &point : points) { pointsDataCycles.push_back_reserved(make_float3(point[0], point[1], point[2])); } _geom->set_verts(pointsDataCycles); } void HdCyclesMesh::PopulateNormals(HdSceneDelegate *sceneDelegate) { _geom->attributes.remove(ATTR_STD_FACE_NORMAL); _geom->attributes.remove(ATTR_STD_VERTEX_NORMAL); // Authored normals should only exist on triangle meshes if (_geom->get_subdivision_type() != Mesh::SUBDIVISION_NONE) { return; } VtValue value; HdInterpolation interpolation = HdInterpolationCount; for (int i = 0; i < HdInterpolationCount && interpolation == HdInterpolationCount; ++i) { for (const HdExtComputationPrimvarDescriptor &desc : sceneDelegate->GetExtComputationPrimvarDescriptors(GetId(), static_cast(i))) { if (desc.name == HdTokens->normals) { auto valueStore = HdExtComputationUtils::GetComputedPrimvarValues({desc}, sceneDelegate); const auto valueStoreIt = valueStore.find(desc.name); if (valueStoreIt != valueStore.end()) { value = std::move(valueStoreIt->second); interpolation = static_cast(i); } break; } } } if (value.IsEmpty()) { interpolation = GetPrimvarInterpolation(sceneDelegate, HdTokens->normals); if (interpolation == HdInterpolationCount) { return; // Ignore missing normals } value = GetNormals(sceneDelegate); } if (!value.IsHolding()) { TF_WARN("Invalid normals data for %s", GetId().GetText()); return; } const auto &normals = value.UncheckedGet(); if (interpolation == HdInterpolationConstant) { TF_VERIFY(normals.size() == 1); const GfVec3f constantNormal = normals[0]; float3 *const N = _geom->attributes.add(ATTR_STD_VERTEX_NORMAL)->data_float3(); for (size_t i = 0; i < _geom->get_verts().size(); ++i) { N[i] = make_float3(constantNormal[0], constantNormal[1], constantNormal[2]); } } else if (interpolation == HdInterpolationUniform) { TF_VERIFY(normals.size() == static_cast(_topology.GetNumFaces())); float3 *const N = _geom->attributes.add(ATTR_STD_FACE_NORMAL)->data_float3(); for (size_t i = 0; i < _geom->num_triangles(); ++i) { const int faceIndex = HdMeshUtil::DecodeFaceIndexFromCoarseFaceParam(_primitiveParams[i]); N[i] = make_float3(normals[faceIndex][0], normals[faceIndex][1], normals[faceIndex][2]); } } else if (interpolation == HdInterpolationVertex || interpolation == HdInterpolationVarying) { TF_VERIFY(normals.size() == static_cast(_topology.GetNumPoints()) && static_cast(_topology.GetNumPoints()) == _geom->get_verts().size()); float3 *const N = _geom->attributes.add(ATTR_STD_VERTEX_NORMAL)->data_float3(); for (size_t i = 0; i < _geom->get_verts().size(); ++i) { N[i] = make_float3(normals[i][0], normals[i][1], normals[i][2]); } } else if (interpolation == HdInterpolationFaceVarying) { TF_VERIFY(normals.size() == static_cast(_topology.GetNumFaceVaryings())); if (!_util.ComputeTriangulatedFaceVaryingPrimvar( normals.data(), normals.size(), HdTypeFloatVec3, &value)) { return; } const auto &normalsTriangulated = value.UncheckedGet(); // Cycles has no standard attribute for face-varying normals, so this is a lossy transformation float3 *const N = _geom->attributes.add(ATTR_STD_FACE_NORMAL)->data_float3(); for (size_t i = 0; i < _geom->num_triangles(); ++i) { GfVec3f averageNormal = normalsTriangulated[i * 3] + normalsTriangulated[i * 3 + 1] + normalsTriangulated[i * 3 + 2]; GfNormalize(&averageNormal); N[i] = make_float3(averageNormal[0], averageNormal[1], averageNormal[2]); } } } 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 std::pair 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 HdPrimvarDescriptor &desc : GetPrimvarDescriptors(sceneDelegate, interpolation.first)) { // Skip special primvars that are handled separately if (desc.name == HdTokens->points || desc.name == HdTokens->normals) { continue; } VtValue value = GetPrimvar(sceneDelegate, desc.name); if (value.IsEmpty()) { continue; } const ustring name(desc.name.GetString()); AttributeStandard std = ATTR_STD_NONE; if (desc.role == HdPrimvarRoleTokens->textureCoordinate) { std = ATTR_STD_UV; } else if (interpolation.first == HdInterpolationVertex) { if (desc.name == HdTokens->displayColor || desc.role == HdPrimvarRoleTokens->color) { std = ATTR_STD_VERTEX_COLOR; } else if (desc.name == HdTokens->normals) { std = ATTR_STD_VERTEX_NORMAL; } } else if (desc.name == HdTokens->displayColor && interpolation.first == HdInterpolationConstant) { if (value.IsHolding() && value.GetArraySize() == 1) { const GfVec3f color = value.UncheckedGet()[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 HdDisplayStyle displayStyle = GetDisplayStyle(sceneDelegate); _topology = HdMeshTopology(GetMeshTopology(sceneDelegate), displayStyle.refineLevel); 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 = !displayStyle.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); HdGeomSubsets const &geomSubsets = _topology.GetGeomSubsets(); if (!geomSubsets.empty()) { array 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 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 material = static_cast( sceneDelegate->GetRenderIndex().GetSprim(HdPrimTypeTokens->material, geomSubset.materialId)); if (material && material->GetCyclesShader()) { shader = static_cast(usedShaders.size()); usedShaders.push_back_slow(material->GetCyclesShader()); materials.emplace(geomSubset.materialId, shader); } } for (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->reserve_mesh(_topology.GetNumPoints(), triangles.size()); for (size_t i = 0; i < _primitiveParams.size(); ++i) { const int faceIndex = HdMeshUtil::DecodeFaceIndexFromCoarseFaceParam(_primitiveParams[i]); const GfVec3i triangle = triangles[i]; _geom->add_triangle(triangle[0], triangle[1], triangle[2], faceShaders[faceIndex], smooth); } } else { PxOsdSubdivTags subdivTags = GetSubdivTags(sceneDelegate); _topology.SetSubdivTags(subdivTags); size_t numNgons = 0; size_t numCorners = 0; for (int vertCount : vertCounts) { numNgons += (vertCount == 4) ? 0 : 1; numCorners += vertCount; } _geom->reserve_subd_faces(_topology.GetNumFaces(), numNgons, numCorners); // TODO: Handle hole indices size_t faceIndex = 0; size_t indexOffset = 0; for (int vertCount : vertCounts) { _geom->add_subd_face(&vertIndx[indexOffset], vertCount, faceShaders[faceIndex], smooth); faceIndex++; indexOffset += vertCount; } const VtIntArray creaseLengths = subdivTags.GetCreaseLengths(); if (!creaseLengths.empty()) { size_t numCreases = 0; for (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 (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]; 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::Finalize(renderParam); } HDCYCLES_NAMESPACE_CLOSE_SCOPE