Refactor: Move more subdivision code into subd/
No logic or functional changes. Pull Request: https://projects.blender.org/blender/blender/pulls/135681
This commit is contained in:
@@ -1,393 +1,20 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
/* SPDX-FileCopyrightText: 2011-2025 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "scene/attribute.h"
|
||||
#include "scene/camera.h"
|
||||
#include "scene/mesh.h"
|
||||
|
||||
#include "subd/osd.h"
|
||||
#include "subd/patch.h"
|
||||
#include "subd/patch_table.h"
|
||||
#include "subd/split.h"
|
||||
|
||||
#include "util/algorithm.h"
|
||||
#include "util/vector.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
# include <opensubdiv/far/patchMap.h>
|
||||
# include <opensubdiv/far/patchTableFactory.h>
|
||||
# include <opensubdiv/far/primvarRefiner.h>
|
||||
# include <opensubdiv/far/topologyRefinerFactory.h>
|
||||
|
||||
/* specializations of TopologyRefinerFactory for ccl::Mesh */
|
||||
|
||||
namespace OpenSubdiv::OPENSUBDIV_VERSION::Far {
|
||||
template<>
|
||||
bool TopologyRefinerFactory<ccl::Mesh>::resizeComponentTopology(TopologyRefiner &refiner,
|
||||
const ccl::Mesh &mesh)
|
||||
{
|
||||
setNumBaseVertices(refiner, mesh.get_verts().size());
|
||||
setNumBaseFaces(refiner, mesh.get_num_subd_faces());
|
||||
|
||||
for (int i = 0; i < mesh.get_num_subd_faces(); i++) {
|
||||
setNumBaseFaceVertices(refiner, i, mesh.get_subd_num_corners()[i]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
bool TopologyRefinerFactory<ccl::Mesh>::assignComponentTopology(TopologyRefiner &refiner,
|
||||
const ccl::Mesh &mesh)
|
||||
{
|
||||
const ccl::array<int> &subd_face_corners = mesh.get_subd_face_corners();
|
||||
const ccl::array<int> &subd_start_corner = mesh.get_subd_start_corner();
|
||||
const ccl::array<int> &subd_num_corners = mesh.get_subd_num_corners();
|
||||
|
||||
for (int i = 0; i < mesh.get_num_subd_faces(); i++) {
|
||||
IndexArray face_verts = getBaseFaceVertices(refiner, i);
|
||||
|
||||
const int start_corner = subd_start_corner[i];
|
||||
int *corner = &subd_face_corners[start_corner];
|
||||
|
||||
for (int j = 0; j < subd_num_corners[i]; j++, corner++) {
|
||||
face_verts[j] = *corner;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
bool TopologyRefinerFactory<ccl::Mesh>::assignComponentTags(TopologyRefiner &refiner,
|
||||
const ccl::Mesh &mesh)
|
||||
{
|
||||
/* Historical maximum crease weight used at Pixar, influencing the maximum in OpenSubDiv. */
|
||||
static constexpr float CREASE_SCALE = 10.0f;
|
||||
|
||||
const size_t num_creases = mesh.get_subd_creases_weight().size();
|
||||
const size_t num_vertex_creases = mesh.get_subd_vert_creases().size();
|
||||
|
||||
/* The last loop is over the vertices, so early exit to avoid iterating them needlessly. */
|
||||
if (num_creases == 0 && num_vertex_creases == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_creases; i++) {
|
||||
const ccl::Mesh::SubdEdgeCrease crease = mesh.get_subd_crease(i);
|
||||
const Index edge = findBaseEdge(refiner, crease.v[0], crease.v[1]);
|
||||
|
||||
if (edge != INDEX_INVALID) {
|
||||
setBaseEdgeSharpness(refiner, edge, crease.crease * CREASE_SCALE);
|
||||
}
|
||||
}
|
||||
|
||||
std::map<int, float> vertex_creases;
|
||||
|
||||
for (size_t i = 0; i < num_vertex_creases; ++i) {
|
||||
const int vertex_idx = mesh.get_subd_vert_creases()[i];
|
||||
const float weight = mesh.get_subd_vert_creases_weight()[i];
|
||||
|
||||
vertex_creases[vertex_idx] = weight * CREASE_SCALE;
|
||||
}
|
||||
|
||||
for (int i = 0; i < mesh.get_verts().size(); i++) {
|
||||
float sharpness = 0.0f;
|
||||
const std::map<int, float>::const_iterator iter = vertex_creases.find(i);
|
||||
|
||||
if (iter != vertex_creases.end()) {
|
||||
sharpness = iter->second;
|
||||
}
|
||||
|
||||
const ConstIndexArray vert_edges = getBaseVertexEdges(refiner, i);
|
||||
|
||||
if (vert_edges.size() == 2) {
|
||||
const float sharpness0 = refiner.getLevel(0).getEdgeSharpness(vert_edges[0]);
|
||||
const float sharpness1 = refiner.getLevel(0).getEdgeSharpness(vert_edges[1]);
|
||||
|
||||
sharpness += ccl::min(sharpness0, sharpness1);
|
||||
sharpness = ccl::min(sharpness, CREASE_SCALE);
|
||||
}
|
||||
|
||||
if (sharpness != 0.0f) {
|
||||
setBaseVertexSharpness(refiner, i, sharpness);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
bool TopologyRefinerFactory<ccl::Mesh>::assignFaceVaryingTopology(TopologyRefiner & /*refiner*/,
|
||||
const ccl::Mesh & /*mesh*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
void TopologyRefinerFactory<ccl::Mesh>::reportInvalidTopology(TopologyError /*err_code*/,
|
||||
const char * /*msg*/,
|
||||
const ccl::Mesh & /*mesh*/)
|
||||
{
|
||||
}
|
||||
} // namespace OpenSubdiv::OPENSUBDIV_VERSION::Far
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
using namespace OpenSubdiv;
|
||||
|
||||
/* struct that implements OpenSubdiv's vertex interface */
|
||||
|
||||
template<typename T> struct OsdValue {
|
||||
T value;
|
||||
|
||||
OsdValue() = default;
|
||||
|
||||
void Clear(void * /*unused*/ = nullptr)
|
||||
{
|
||||
memset(&value, 0, sizeof(T));
|
||||
}
|
||||
|
||||
void AddWithWeight(const OsdValue<T> &src, const float weight)
|
||||
{
|
||||
value += src.value * weight;
|
||||
}
|
||||
};
|
||||
|
||||
template<> void OsdValue<uchar4>::AddWithWeight(const OsdValue<uchar4> &src, const float weight)
|
||||
{
|
||||
for (int i = 0; i < 4; i++) {
|
||||
value[i] += (uchar)(src.value[i] * weight);
|
||||
}
|
||||
}
|
||||
|
||||
/* class for holding OpenSubdiv data used during tessellation */
|
||||
|
||||
class OsdData {
|
||||
Mesh *mesh = nullptr;
|
||||
vector<OsdValue<float3>> verts;
|
||||
unique_ptr<Far::TopologyRefiner> refiner;
|
||||
unique_ptr<Far::PatchTable> patch_table;
|
||||
unique_ptr<Far::PatchMap> patch_map;
|
||||
|
||||
public:
|
||||
OsdData() = default;
|
||||
|
||||
void build_from_mesh(Mesh *mesh_)
|
||||
{
|
||||
mesh = mesh_;
|
||||
|
||||
/* type and options */
|
||||
const Sdc::SchemeType type = Sdc::SCHEME_CATMARK;
|
||||
|
||||
Sdc::Options options;
|
||||
options.SetVtxBoundaryInterpolation(Sdc::Options::VTX_BOUNDARY_EDGE_ONLY);
|
||||
|
||||
/* create refiner */
|
||||
refiner.reset(Far::TopologyRefinerFactory<Mesh>::Create(
|
||||
*mesh, Far::TopologyRefinerFactory<Mesh>::Options(type, options)));
|
||||
|
||||
/* adaptive refinement */
|
||||
const int max_isolation = calculate_max_isolation();
|
||||
refiner->RefineAdaptive(Far::TopologyRefiner::AdaptiveOptions(max_isolation));
|
||||
|
||||
/* create patch table */
|
||||
Far::PatchTableFactory::Options patch_options;
|
||||
patch_options.endCapType = Far::PatchTableFactory::Options::ENDCAP_GREGORY_BASIS;
|
||||
|
||||
patch_table.reset(Far::PatchTableFactory::Create(*refiner, patch_options));
|
||||
|
||||
/* interpolate verts */
|
||||
const int num_refiner_verts = refiner->GetNumVerticesTotal();
|
||||
const int num_local_points = patch_table->GetNumLocalPoints();
|
||||
|
||||
verts.resize(num_refiner_verts + num_local_points);
|
||||
for (int i = 0; i < mesh->get_verts().size(); i++) {
|
||||
verts[i].value = mesh->get_verts()[i];
|
||||
}
|
||||
|
||||
OsdValue<float3> *src = verts.data();
|
||||
for (int i = 0; i < refiner->GetMaxLevel(); i++) {
|
||||
OsdValue<float3> *dest = src + refiner->GetLevel(i).GetNumVertices();
|
||||
Far::PrimvarRefiner(*refiner).Interpolate(i + 1, src, dest);
|
||||
src = dest;
|
||||
}
|
||||
|
||||
if (num_local_points) {
|
||||
patch_table->ComputeLocalPointValues(verts.data(), &verts[num_refiner_verts]);
|
||||
}
|
||||
|
||||
/* create patch map */
|
||||
patch_map = make_unique<Far::PatchMap>(*patch_table);
|
||||
}
|
||||
|
||||
void subdivide_attribute(Attribute &attr)
|
||||
{
|
||||
const Far::PrimvarRefiner primvar_refiner(*refiner);
|
||||
|
||||
if (attr.element == ATTR_ELEMENT_VERTEX) {
|
||||
const int num_refiner_verts = refiner->GetNumVerticesTotal();
|
||||
const int num_local_points = patch_table->GetNumLocalPoints();
|
||||
|
||||
attr.resize(num_refiner_verts + num_local_points);
|
||||
attr.flags |= ATTR_FINAL_SIZE;
|
||||
|
||||
char *src = attr.buffer.data();
|
||||
|
||||
for (int i = 0; i < refiner->GetMaxLevel(); i++) {
|
||||
char *dest = src + refiner->GetLevel(i).GetNumVertices() * attr.data_sizeof();
|
||||
|
||||
if (ccl::Attribute::same_storage(attr.type, TypeFloat)) {
|
||||
primvar_refiner.Interpolate(i + 1, (OsdValue<float> *)src, (OsdValue<float> *&)dest);
|
||||
}
|
||||
else if (ccl::Attribute::same_storage(attr.type, TypeFloat2)) {
|
||||
primvar_refiner.Interpolate(i + 1, (OsdValue<float2> *)src, (OsdValue<float2> *&)dest);
|
||||
// float3 is not interchangeable with float4 and so needs to be handled
|
||||
// separately
|
||||
}
|
||||
else if (ccl::Attribute::same_storage(attr.type, TypeFloat4)) {
|
||||
primvar_refiner.Interpolate(i + 1, (OsdValue<float4> *)src, (OsdValue<float4> *&)dest);
|
||||
}
|
||||
else {
|
||||
primvar_refiner.Interpolate(i + 1, (OsdValue<float3> *)src, (OsdValue<float3> *&)dest);
|
||||
}
|
||||
|
||||
src = dest;
|
||||
}
|
||||
|
||||
if (num_local_points) {
|
||||
if (ccl::Attribute::same_storage(attr.type, TypeFloat)) {
|
||||
patch_table->ComputeLocalPointValues(
|
||||
(OsdValue<float> *)attr.buffer.data(),
|
||||
(OsdValue<float> *)&attr.buffer[num_refiner_verts * attr.data_sizeof()]);
|
||||
}
|
||||
else if (ccl::Attribute::same_storage(attr.type, TypeFloat2)) {
|
||||
patch_table->ComputeLocalPointValues(
|
||||
(OsdValue<float2> *)attr.buffer.data(),
|
||||
(OsdValue<float2> *)&attr.buffer[num_refiner_verts * attr.data_sizeof()]);
|
||||
}
|
||||
else if (ccl::Attribute::same_storage(attr.type, TypeFloat4)) {
|
||||
// float3 is not interchangeable with float4 and so needs to be handled
|
||||
// separately
|
||||
patch_table->ComputeLocalPointValues(
|
||||
(OsdValue<float4> *)attr.buffer.data(),
|
||||
(OsdValue<float4> *)&attr.buffer[num_refiner_verts * attr.data_sizeof()]);
|
||||
}
|
||||
else {
|
||||
// float3 is not interchangeable with float4 and so needs to be handled
|
||||
// separately
|
||||
patch_table->ComputeLocalPointValues(
|
||||
(OsdValue<float3> *)attr.buffer.data(),
|
||||
(OsdValue<float3> *)&attr.buffer[num_refiner_verts * attr.data_sizeof()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (attr.element == ATTR_ELEMENT_CORNER || attr.element == ATTR_ELEMENT_CORNER_BYTE) {
|
||||
// TODO(mai): fvar interpolation
|
||||
}
|
||||
}
|
||||
|
||||
int calculate_max_isolation()
|
||||
{
|
||||
/* loop over all edges to find longest in screen space */
|
||||
const Far::TopologyLevel &level = refiner->GetLevel(0);
|
||||
const SubdParams *subd_params = mesh->get_subd_params();
|
||||
const Transform objecttoworld = subd_params->objecttoworld;
|
||||
Camera *cam = subd_params->camera;
|
||||
|
||||
float longest_edge = 0.0f;
|
||||
|
||||
for (size_t i = 0; i < level.GetNumEdges(); i++) {
|
||||
const Far::ConstIndexArray verts = level.GetEdgeVertices(i);
|
||||
|
||||
float3 a = mesh->get_verts()[verts[0]];
|
||||
float3 b = mesh->get_verts()[verts[1]];
|
||||
|
||||
float edge_len;
|
||||
|
||||
if (cam) {
|
||||
a = transform_point(&objecttoworld, a);
|
||||
b = transform_point(&objecttoworld, b);
|
||||
|
||||
edge_len = len(a - b) / cam->world_to_raster_size((a + b) * 0.5f);
|
||||
}
|
||||
else {
|
||||
edge_len = len(a - b);
|
||||
}
|
||||
|
||||
longest_edge = max(longest_edge, edge_len);
|
||||
}
|
||||
|
||||
/* calculate isolation level */
|
||||
const int isolation = (int)(log2f(max(longest_edge / subd_params->dicing_rate, 1.0f)) + 1.0f);
|
||||
|
||||
return min(isolation, 10);
|
||||
}
|
||||
|
||||
friend struct OsdPatch;
|
||||
friend class Mesh;
|
||||
};
|
||||
|
||||
/* ccl::Patch implementation that uses OpenSubdiv for eval */
|
||||
|
||||
struct OsdPatch : Patch {
|
||||
OsdData *osd_data;
|
||||
|
||||
OsdPatch() = default;
|
||||
OsdPatch(OsdData *data) : osd_data(data) {}
|
||||
|
||||
void eval(float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, const float u, float v) override
|
||||
{
|
||||
const Far::PatchTable::PatchHandle *handle = osd_data->patch_map->FindPatch(
|
||||
patch_index, (double)u, (double)v);
|
||||
assert(handle);
|
||||
|
||||
float p_weights[20];
|
||||
float du_weights[20];
|
||||
float dv_weights[20];
|
||||
osd_data->patch_table->EvaluateBasis(*handle, u, v, p_weights, du_weights, dv_weights);
|
||||
|
||||
const Far::ConstIndexArray cv = osd_data->patch_table->GetPatchVertices(*handle);
|
||||
|
||||
float3 du;
|
||||
float3 dv;
|
||||
if (P) {
|
||||
*P = zero_float3();
|
||||
}
|
||||
du = zero_float3();
|
||||
dv = zero_float3();
|
||||
|
||||
for (int i = 0; i < cv.size(); i++) {
|
||||
const float3 p = osd_data->verts[cv[i]].value;
|
||||
|
||||
if (P) {
|
||||
*P += p * p_weights[i];
|
||||
}
|
||||
du += p * du_weights[i];
|
||||
dv += p * dv_weights[i];
|
||||
}
|
||||
|
||||
if (dPdu) {
|
||||
*dPdu = du;
|
||||
}
|
||||
if (dPdv) {
|
||||
*dPdv = dv;
|
||||
}
|
||||
if (N) {
|
||||
*N = cross(du, dv);
|
||||
|
||||
const float t = len(*N);
|
||||
*N = (t != 0.0f) ? *N / t : make_float3(0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
void Mesh::tessellate(DiagSplit *split)
|
||||
{
|
||||
/* reset the number of subdivision vertices, in case the Mesh was not cleared
|
||||
|
||||
@@ -12,6 +12,8 @@ set(INC_SYS
|
||||
|
||||
set(SRC
|
||||
dice.cpp
|
||||
interpolation.cpp
|
||||
osd.cpp
|
||||
patch.cpp
|
||||
split.cpp
|
||||
patch_table.cpp
|
||||
@@ -19,6 +21,8 @@ set(SRC
|
||||
|
||||
set(SRC_HEADERS
|
||||
dice.h
|
||||
interpolation.h
|
||||
osd.h
|
||||
patch.h
|
||||
patch_table.h
|
||||
split.h
|
||||
|
||||
83
intern/cycles/subd/interpolation.cpp
Normal file
83
intern/cycles/subd/interpolation.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2024 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "subd/interpolation.h"
|
||||
#include "subd/osd.h"
|
||||
|
||||
#include "scene/attribute.h"
|
||||
#include "scene/mesh.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
|
||||
void OsdData::subdivide_attribute(Attribute &attr)
|
||||
{
|
||||
const Far::PrimvarRefiner primvar_refiner(*refiner);
|
||||
|
||||
if (attr.element == ATTR_ELEMENT_VERTEX) {
|
||||
const int num_refiner_verts = refiner->GetNumVerticesTotal();
|
||||
const int num_local_points = patch_table->GetNumLocalPoints();
|
||||
|
||||
attr.resize(num_refiner_verts + num_local_points);
|
||||
attr.flags |= ATTR_FINAL_SIZE;
|
||||
|
||||
char *src = attr.buffer.data();
|
||||
|
||||
for (int i = 0; i < refiner->GetMaxLevel(); i++) {
|
||||
char *dest = src + refiner->GetLevel(i).GetNumVertices() * attr.data_sizeof();
|
||||
|
||||
if (ccl::Attribute::same_storage(attr.type, TypeFloat)) {
|
||||
primvar_refiner.Interpolate(i + 1, (OsdValue<float> *)src, (OsdValue<float> *&)dest);
|
||||
}
|
||||
else if (ccl::Attribute::same_storage(attr.type, TypeFloat2)) {
|
||||
primvar_refiner.Interpolate(i + 1, (OsdValue<float2> *)src, (OsdValue<float2> *&)dest);
|
||||
// float3 is not interchangeable with float4 and so needs to be handled
|
||||
// separately
|
||||
}
|
||||
else if (ccl::Attribute::same_storage(attr.type, TypeFloat4)) {
|
||||
primvar_refiner.Interpolate(i + 1, (OsdValue<float4> *)src, (OsdValue<float4> *&)dest);
|
||||
}
|
||||
else {
|
||||
primvar_refiner.Interpolate(i + 1, (OsdValue<float3> *)src, (OsdValue<float3> *&)dest);
|
||||
}
|
||||
|
||||
src = dest;
|
||||
}
|
||||
|
||||
if (num_local_points) {
|
||||
if (ccl::Attribute::same_storage(attr.type, TypeFloat)) {
|
||||
patch_table->ComputeLocalPointValues(
|
||||
(OsdValue<float> *)attr.buffer.data(),
|
||||
(OsdValue<float> *)&attr.buffer[num_refiner_verts * attr.data_sizeof()]);
|
||||
}
|
||||
else if (ccl::Attribute::same_storage(attr.type, TypeFloat2)) {
|
||||
patch_table->ComputeLocalPointValues(
|
||||
(OsdValue<float2> *)attr.buffer.data(),
|
||||
(OsdValue<float2> *)&attr.buffer[num_refiner_verts * attr.data_sizeof()]);
|
||||
}
|
||||
else if (ccl::Attribute::same_storage(attr.type, TypeFloat4)) {
|
||||
// float3 is not interchangeable with float4 and so needs to be handled
|
||||
// separately
|
||||
patch_table->ComputeLocalPointValues(
|
||||
(OsdValue<float4> *)attr.buffer.data(),
|
||||
(OsdValue<float4> *)&attr.buffer[num_refiner_verts * attr.data_sizeof()]);
|
||||
}
|
||||
else {
|
||||
// float3 is not interchangeable with float4 and so needs to be handled
|
||||
// separately
|
||||
patch_table->ComputeLocalPointValues(
|
||||
(OsdValue<float3> *)attr.buffer.data(),
|
||||
(OsdValue<float3> *)&attr.buffer[num_refiner_verts * attr.data_sizeof()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (attr.element == ATTR_ELEMENT_CORNER || attr.element == ATTR_ELEMENT_CORNER_BYTE) {
|
||||
// TODO(mai): fvar interpolation
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
9
intern/cycles/subd/interpolation.h
Normal file
9
intern/cycles/subd/interpolation.h
Normal file
@@ -0,0 +1,9 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2024 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
264
intern/cycles/subd/osd.cpp
Normal file
264
intern/cycles/subd/osd.cpp
Normal file
@@ -0,0 +1,264 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2024 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
|
||||
# include "subd/osd.h"
|
||||
|
||||
# include "scene/attribute.h"
|
||||
# include "scene/camera.h"
|
||||
# include "scene/mesh.h"
|
||||
|
||||
/* Specialization of TopologyRefinerFactory for OsdMesh */
|
||||
|
||||
namespace OpenSubdiv::OPENSUBDIV_VERSION::Far {
|
||||
template<>
|
||||
bool TopologyRefinerFactory<ccl::Mesh>::resizeComponentTopology(TopologyRefiner &refiner,
|
||||
const ccl::Mesh &mesh)
|
||||
{
|
||||
setNumBaseVertices(refiner, mesh.get_verts().size());
|
||||
setNumBaseFaces(refiner, mesh.get_num_subd_faces());
|
||||
|
||||
for (int i = 0; i < mesh.get_num_subd_faces(); i++) {
|
||||
setNumBaseFaceVertices(refiner, i, mesh.get_subd_num_corners()[i]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
bool TopologyRefinerFactory<ccl::Mesh>::assignComponentTopology(TopologyRefiner &refiner,
|
||||
const ccl::Mesh &mesh)
|
||||
{
|
||||
const ccl::array<int> &subd_face_corners = mesh.get_subd_face_corners();
|
||||
const ccl::array<int> &subd_start_corner = mesh.get_subd_start_corner();
|
||||
const ccl::array<int> &subd_num_corners = mesh.get_subd_num_corners();
|
||||
|
||||
for (int i = 0; i < mesh.get_num_subd_faces(); i++) {
|
||||
IndexArray face_verts = getBaseFaceVertices(refiner, i);
|
||||
|
||||
const int start_corner = subd_start_corner[i];
|
||||
int *corner = &subd_face_corners[start_corner];
|
||||
|
||||
for (int j = 0; j < subd_num_corners[i]; j++, corner++) {
|
||||
face_verts[j] = *corner;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
bool TopologyRefinerFactory<ccl::Mesh>::assignComponentTags(TopologyRefiner &refiner,
|
||||
const ccl::Mesh &mesh)
|
||||
{
|
||||
/* Historical maximum crease weight used at Pixar, influencing the maximum in OpenSubDiv. */
|
||||
static constexpr float CREASE_SCALE = 10.0f;
|
||||
|
||||
const size_t num_creases = mesh.get_subd_creases_weight().size();
|
||||
const size_t num_vertex_creases = mesh.get_subd_vert_creases().size();
|
||||
|
||||
/* The last loop is over the vertices, so early exit to avoid iterating them needlessly. */
|
||||
if (num_creases == 0 && num_vertex_creases == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_creases; i++) {
|
||||
const ccl::Mesh::SubdEdgeCrease crease = mesh.get_subd_crease(i);
|
||||
const Index edge = findBaseEdge(refiner, crease.v[0], crease.v[1]);
|
||||
|
||||
if (edge != INDEX_INVALID) {
|
||||
setBaseEdgeSharpness(refiner, edge, crease.crease * CREASE_SCALE);
|
||||
}
|
||||
}
|
||||
|
||||
std::map<int, float> vertex_creases;
|
||||
|
||||
for (size_t i = 0; i < num_vertex_creases; ++i) {
|
||||
const int vertex_idx = mesh.get_subd_vert_creases()[i];
|
||||
const float weight = mesh.get_subd_vert_creases_weight()[i];
|
||||
|
||||
vertex_creases[vertex_idx] = weight * CREASE_SCALE;
|
||||
}
|
||||
|
||||
for (int i = 0; i < mesh.get_verts().size(); i++) {
|
||||
float sharpness = 0.0f;
|
||||
const std::map<int, float>::const_iterator iter = vertex_creases.find(i);
|
||||
|
||||
if (iter != vertex_creases.end()) {
|
||||
sharpness = iter->second;
|
||||
}
|
||||
|
||||
const ConstIndexArray vert_edges = getBaseVertexEdges(refiner, i);
|
||||
|
||||
if (vert_edges.size() == 2) {
|
||||
const float sharpness0 = refiner.getLevel(0).getEdgeSharpness(vert_edges[0]);
|
||||
const float sharpness1 = refiner.getLevel(0).getEdgeSharpness(vert_edges[1]);
|
||||
|
||||
sharpness += ccl::min(sharpness0, sharpness1);
|
||||
sharpness = ccl::min(sharpness, CREASE_SCALE);
|
||||
}
|
||||
|
||||
if (sharpness != 0.0f) {
|
||||
setBaseVertexSharpness(refiner, i, sharpness);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
bool TopologyRefinerFactory<ccl::Mesh>::assignFaceVaryingTopology(TopologyRefiner & /*refiner*/,
|
||||
const ccl::Mesh & /*mesh*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
void TopologyRefinerFactory<ccl::Mesh>::reportInvalidTopology(TopologyError /*err_code*/,
|
||||
const char * /*msg*/,
|
||||
const ccl::Mesh & /*mesh*/)
|
||||
{
|
||||
}
|
||||
} // namespace OpenSubdiv::OPENSUBDIV_VERSION::Far
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* OsdData */
|
||||
|
||||
void OsdData::build_from_mesh(Mesh *mesh_)
|
||||
{
|
||||
mesh = mesh_;
|
||||
|
||||
/* type and options */
|
||||
const Sdc::SchemeType type = Sdc::SCHEME_CATMARK;
|
||||
|
||||
Sdc::Options options;
|
||||
options.SetVtxBoundaryInterpolation(Sdc::Options::VTX_BOUNDARY_EDGE_ONLY);
|
||||
|
||||
/* create refiner */
|
||||
refiner.reset(Far::TopologyRefinerFactory<Mesh>::Create(
|
||||
*mesh, Far::TopologyRefinerFactory<Mesh>::Options(type, options)));
|
||||
|
||||
/* adaptive refinement */
|
||||
const int max_isolation = calculate_max_isolation();
|
||||
refiner->RefineAdaptive(Far::TopologyRefiner::AdaptiveOptions(max_isolation));
|
||||
|
||||
/* create patch table */
|
||||
Far::PatchTableFactory::Options patch_options;
|
||||
patch_options.endCapType = Far::PatchTableFactory::Options::ENDCAP_GREGORY_BASIS;
|
||||
|
||||
patch_table.reset(Far::PatchTableFactory::Create(*refiner, patch_options));
|
||||
|
||||
/* interpolate verts */
|
||||
const int num_refiner_verts = refiner->GetNumVerticesTotal();
|
||||
const int num_local_points = patch_table->GetNumLocalPoints();
|
||||
|
||||
verts.resize(num_refiner_verts + num_local_points);
|
||||
for (int i = 0; i < mesh->get_verts().size(); i++) {
|
||||
verts[i].value = mesh->get_verts()[i];
|
||||
}
|
||||
|
||||
OsdValue<float3> *src = verts.data();
|
||||
for (int i = 0; i < refiner->GetMaxLevel(); i++) {
|
||||
OsdValue<float3> *dest = src + refiner->GetLevel(i).GetNumVertices();
|
||||
Far::PrimvarRefiner(*refiner).Interpolate(i + 1, src, dest);
|
||||
src = dest;
|
||||
}
|
||||
|
||||
if (num_local_points) {
|
||||
patch_table->ComputeLocalPointValues(verts.data(), &verts[num_refiner_verts]);
|
||||
}
|
||||
|
||||
/* create patch map */
|
||||
patch_map = make_unique<Far::PatchMap>(*patch_table);
|
||||
}
|
||||
|
||||
int OsdData::calculate_max_isolation()
|
||||
{
|
||||
/* loop over all edges to find longest in screen space */
|
||||
const Far::TopologyLevel &level = refiner->GetLevel(0);
|
||||
const SubdParams *subd_params = mesh->get_subd_params();
|
||||
const Transform objecttoworld = subd_params->objecttoworld;
|
||||
Camera *cam = subd_params->camera;
|
||||
|
||||
float longest_edge = 0.0f;
|
||||
|
||||
for (size_t i = 0; i < level.GetNumEdges(); i++) {
|
||||
const Far::ConstIndexArray verts = level.GetEdgeVertices(i);
|
||||
|
||||
float3 a = mesh->get_verts()[verts[0]];
|
||||
float3 b = mesh->get_verts()[verts[1]];
|
||||
|
||||
float edge_len;
|
||||
|
||||
if (cam) {
|
||||
a = transform_point(&objecttoworld, a);
|
||||
b = transform_point(&objecttoworld, b);
|
||||
|
||||
edge_len = len(a - b) / cam->world_to_raster_size((a + b) * 0.5f);
|
||||
}
|
||||
else {
|
||||
edge_len = len(a - b);
|
||||
}
|
||||
|
||||
longest_edge = max(longest_edge, edge_len);
|
||||
}
|
||||
|
||||
/* calculate isolation level */
|
||||
const int isolation = (int)(log2f(max(longest_edge / subd_params->dicing_rate, 1.0f)) + 1.0f);
|
||||
|
||||
return min(isolation, 10);
|
||||
}
|
||||
|
||||
/* OsdPatch */
|
||||
|
||||
void OsdPatch::eval(float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, const float u, float v)
|
||||
{
|
||||
const Far::PatchTable::PatchHandle *handle = osd_data->patch_map->FindPatch(
|
||||
patch_index, (double)u, (double)v);
|
||||
assert(handle);
|
||||
|
||||
float p_weights[20];
|
||||
float du_weights[20];
|
||||
float dv_weights[20];
|
||||
osd_data->patch_table->EvaluateBasis(*handle, u, v, p_weights, du_weights, dv_weights);
|
||||
|
||||
const Far::ConstIndexArray cv = osd_data->patch_table->GetPatchVertices(*handle);
|
||||
|
||||
float3 du;
|
||||
float3 dv;
|
||||
if (P) {
|
||||
*P = zero_float3();
|
||||
}
|
||||
du = zero_float3();
|
||||
dv = zero_float3();
|
||||
|
||||
for (int i = 0; i < cv.size(); i++) {
|
||||
const float3 p = osd_data->verts[cv[i]].value;
|
||||
|
||||
if (P) {
|
||||
*P += p * p_weights[i];
|
||||
}
|
||||
du += p * du_weights[i];
|
||||
dv += p * dv_weights[i];
|
||||
}
|
||||
|
||||
if (dPdu) {
|
||||
*dPdu = du;
|
||||
}
|
||||
if (dPdv) {
|
||||
*dPdv = dv;
|
||||
}
|
||||
if (N) {
|
||||
*N = cross(du, dv);
|
||||
|
||||
const float t = len(*N);
|
||||
*N = (t != 0.0f) ? *N / t : make_float3(0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
85
intern/cycles/subd/osd.h
Normal file
85
intern/cycles/subd/osd.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2025 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
|
||||
# include "scene/attribute.h"
|
||||
# include "scene/mesh.h"
|
||||
|
||||
# include "subd/patch.h"
|
||||
# include "subd/split.h"
|
||||
|
||||
# include <opensubdiv/far/patchMap.h>
|
||||
# include <opensubdiv/far/patchTableFactory.h>
|
||||
# include <opensubdiv/far/primvarRefiner.h>
|
||||
# include <opensubdiv/far/topologyRefinerFactory.h>
|
||||
|
||||
/* specializations of TopologyRefinerFactory for ccl::Mesh */
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
using namespace OpenSubdiv;
|
||||
|
||||
/* struct that implements OpenSubdiv's vertex interface */
|
||||
|
||||
template<typename T> struct OsdValue {
|
||||
T value;
|
||||
|
||||
OsdValue() = default;
|
||||
|
||||
void Clear(void * /*unused*/ = nullptr)
|
||||
{
|
||||
memset(&value, 0, sizeof(T));
|
||||
}
|
||||
|
||||
void AddWithWeight(const OsdValue<T> &src, const float weight)
|
||||
{
|
||||
value += src.value * weight;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
inline void OsdValue<uchar4>::AddWithWeight(const OsdValue<uchar4> &src, const float weight)
|
||||
{
|
||||
for (int i = 0; i < 4; i++) {
|
||||
value[i] += (uchar)(src.value[i] * weight);
|
||||
}
|
||||
}
|
||||
|
||||
/* class for holding OpenSubdiv data used during tessellation */
|
||||
|
||||
class OsdData {
|
||||
Mesh *mesh = nullptr;
|
||||
vector<OsdValue<float3>> verts;
|
||||
unique_ptr<Far::TopologyRefiner> refiner;
|
||||
unique_ptr<Far::PatchTable> patch_table;
|
||||
unique_ptr<Far::PatchMap> patch_map;
|
||||
|
||||
public:
|
||||
OsdData() = default;
|
||||
|
||||
void build_from_mesh(Mesh *mesh_);
|
||||
int calculate_max_isolation();
|
||||
void subdivide_attribute(Attribute &attr);
|
||||
|
||||
friend struct OsdPatch;
|
||||
friend class Mesh;
|
||||
};
|
||||
|
||||
/* ccl::Patch implementation that uses OpenSubdiv for eval */
|
||||
|
||||
struct OsdPatch : Patch {
|
||||
OsdData *osd_data;
|
||||
|
||||
OsdPatch() = default;
|
||||
OsdPatch(OsdData *data) : osd_data(data) {}
|
||||
|
||||
void eval(float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, const float u, float v) override;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user