Cycles: Multithread adaptive subdivision dicing
* Perform attribute interpolation as part of dicing. * Remove temporary subd uv and face index attributes. On a MacBook M3 with 12 P-cores and 4 E-cores, these changes overall give a 10x-14x speedup on various scenes. Note that splitting is still single threaded and can be expensive, and UV subdivision can be optimized more. Pull Request: https://projects.blender.org/blender/blender/pulls/136411
This commit is contained in:
@@ -198,11 +198,6 @@ void Mesh::resize_mesh(const int numverts, const int numtris)
|
||||
shader.resize(numtris);
|
||||
smooth.resize(numtris);
|
||||
|
||||
if (get_num_subd_faces()) {
|
||||
subd_triangle_patch_index.resize(numtris);
|
||||
subd_corner_patch_uv.resize(numtris * 3);
|
||||
}
|
||||
|
||||
attributes.resize();
|
||||
}
|
||||
|
||||
@@ -214,11 +209,6 @@ void Mesh::reserve_mesh(const int numverts, const int numtris)
|
||||
shader.reserve(numtris);
|
||||
smooth.reserve(numtris);
|
||||
|
||||
if (get_num_subd_faces()) {
|
||||
subd_triangle_patch_index.reserve(numtris);
|
||||
subd_corner_patch_uv.reserve(numtris * 3);
|
||||
}
|
||||
|
||||
attributes.resize(true);
|
||||
}
|
||||
|
||||
@@ -318,13 +308,6 @@ void Mesh::add_triangle(const int v0, const int v1, const int v2, const int shad
|
||||
tag_triangles_modified();
|
||||
tag_shader_modified();
|
||||
tag_smooth_modified();
|
||||
|
||||
if (get_num_subd_faces()) {
|
||||
subd_triangle_patch_index.push_back_reserved(-1);
|
||||
subd_corner_patch_uv.push_back_reserved(zero_float2());
|
||||
subd_corner_patch_uv.push_back_reserved(zero_float2());
|
||||
subd_corner_patch_uv.push_back_reserved(zero_float2());
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::add_subd_face(const int *corners,
|
||||
|
||||
@@ -167,10 +167,6 @@ class Mesh : public Geometry {
|
||||
|
||||
AttributeSet subd_attributes;
|
||||
|
||||
/* Temporary storage for attribute interpolation, per triangle and per vertex. */
|
||||
array<int> subd_triangle_patch_index;
|
||||
array<float2> subd_corner_patch_uv;
|
||||
|
||||
/* BVH */
|
||||
size_t vert_offset;
|
||||
|
||||
|
||||
@@ -82,8 +82,11 @@ void Mesh::tessellate(SubdParams ¶ms)
|
||||
DiagSplit split(params);
|
||||
split.split_patches(osd_patches.data(), sizeof(OsdPatch));
|
||||
|
||||
/* Setup interpolation. */
|
||||
SubdAttributeInterpolation interpolation(*this, osd_mesh, osd_data);
|
||||
|
||||
/* Dice patches. */
|
||||
EdgeDice dice(params, split.get_num_verts(), split.get_num_triangles());
|
||||
EdgeDice dice(params, split.get_num_verts(), split.get_num_triangles(), interpolation);
|
||||
dice.dice(split);
|
||||
}
|
||||
else
|
||||
@@ -146,36 +149,19 @@ void Mesh::tessellate(SubdParams ¶ms)
|
||||
DiagSplit split(params);
|
||||
split.split_patches(linear_patches.data(), sizeof(LinearQuadPatch));
|
||||
|
||||
/* Dice patches. */
|
||||
EdgeDice dice(params, split.get_num_verts(), split.get_num_triangles());
|
||||
dice.dice(split);
|
||||
}
|
||||
|
||||
if (get_num_subd_faces()) {
|
||||
/* Create a tessellated mesh attributes from subd base mesh attributes. */
|
||||
/* Setup interpolation. */
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
SubdAttributeInterpolation interpolation(*this, osd_mesh, osd_data, num_patches);
|
||||
SubdAttributeInterpolation interpolation(*this, osd_mesh, osd_data);
|
||||
#else
|
||||
SubdAttributeInterpolation interpolation(*this, num_patches);
|
||||
#endif
|
||||
|
||||
for (const Attribute &subd_attr : subd_attributes.attributes) {
|
||||
if (!interpolation.support_interp_attribute(subd_attr)) {
|
||||
continue;
|
||||
}
|
||||
Attribute &mesh_attr = attributes.copy(subd_attr);
|
||||
interpolation.interp_attribute(subd_attr, mesh_attr);
|
||||
}
|
||||
/* Dice patches. */
|
||||
EdgeDice dice(params, split.get_num_verts(), split.get_num_triangles(), interpolation);
|
||||
dice.dice(split);
|
||||
}
|
||||
|
||||
// TODO: Free subd base data? Or will this break interactive updates?
|
||||
|
||||
// TODO: Use ATTR_STD_PTEX attributes instead, and create only for lifetime of this function
|
||||
// if there are attributes to interpolation. And then keep only if needed for ptex texturing
|
||||
|
||||
/* Clear temporary buffers needed for interpolation. */
|
||||
subd_triangle_patch_index.clear();
|
||||
subd_corner_patch_uv.clear();
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "scene/mesh.h"
|
||||
|
||||
#include "subd/dice.h"
|
||||
#include "subd/interpolation.h"
|
||||
#include "subd/patch.h"
|
||||
#include "subd/split.h"
|
||||
|
||||
@@ -13,8 +14,11 @@
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
EdgeDice::EdgeDice(const SubdParams ¶ms_, const int num_verts, const int num_triangles)
|
||||
: params(params_)
|
||||
EdgeDice::EdgeDice(const SubdParams ¶ms_,
|
||||
const int num_verts,
|
||||
const int num_triangles,
|
||||
SubdAttributeInterpolation &interpolation)
|
||||
: params(params_), interpolation(interpolation)
|
||||
{
|
||||
Mesh *mesh = params.mesh;
|
||||
|
||||
@@ -36,6 +40,8 @@ EdgeDice::EdgeDice(const SubdParams ¶ms_, const int num_verts, const int num
|
||||
mesh_ptex_face_id = attr_ptex_face_id->data_float();
|
||||
mesh_ptex_uv = attr_ptex_uv->data_float2();
|
||||
}
|
||||
|
||||
interpolation.setup();
|
||||
}
|
||||
|
||||
float3 EdgeDice::eval_projected(const SubPatch &sub, const float2 uv)
|
||||
@@ -98,6 +104,10 @@ void EdgeDice::set_vertex(const SubPatch &sub, const int index, const float2 uv)
|
||||
|
||||
mesh_P[index] = P;
|
||||
mesh_N[index] = N;
|
||||
|
||||
for (const SubdAttribute &attr : interpolation.vertex_attributes) {
|
||||
attr.interp(sub.patch->patch_index, sub.face_index, sub.corner, &index, &uv, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void EdgeDice::add_triangle(const SubPatch &sub,
|
||||
@@ -119,20 +129,20 @@ void EdgeDice::add_triangle(const SubPatch &sub,
|
||||
mesh_shader[triangle_index] = patch->shader;
|
||||
mesh_smooth[triangle_index] = true;
|
||||
|
||||
params.mesh->subd_triangle_patch_index[triangle_index] = patch->patch_index;
|
||||
if (mesh_ptex_face_id) {
|
||||
mesh_ptex_face_id[triangle_index] = patch->patch_index;
|
||||
}
|
||||
|
||||
params.mesh->subd_corner_patch_uv[triangle_index * 3 + 0] = uv0;
|
||||
params.mesh->subd_corner_patch_uv[triangle_index * 3 + 1] = uv1;
|
||||
params.mesh->subd_corner_patch_uv[triangle_index * 3 + 2] = uv2;
|
||||
|
||||
if (mesh_ptex_uv) {
|
||||
mesh_ptex_uv[triangle_index * 3 + 0] = uv0;
|
||||
mesh_ptex_uv[triangle_index * 3 + 0] = uv1;
|
||||
mesh_ptex_uv[triangle_index * 3 + 0] = uv2;
|
||||
}
|
||||
|
||||
/* TODO: batch together multiple triangles. */
|
||||
float2 uv[3] = {uv0, uv1, uv2};
|
||||
for (const SubdAttribute &attr : interpolation.triangle_attributes) {
|
||||
attr.interp(sub.patch->patch_index, sub.face_index, sub.corner, &triangle_index, uv, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void EdgeDice::add_grid_triangles_and_stitch(const SubPatch &sub, const int Mu, const int Mv)
|
||||
@@ -363,9 +373,6 @@ void EdgeDice::dice(const SubPatch &sub)
|
||||
const int Mu = max(sub.edge_u0.edge->T, sub.edge_u1.edge->T);
|
||||
const int Mv = max(sub.edge_v0.edge->T, sub.edge_v1.edge->T);
|
||||
|
||||
/* Vertex coordinates for sides. */
|
||||
set_sides(sub);
|
||||
|
||||
if (Mv == 1) {
|
||||
/* No inner grid, stitch triangles from side to side. */
|
||||
add_triangle_strip(sub, 2, 0);
|
||||
@@ -390,9 +397,21 @@ void EdgeDice::dice(const SubPatch &sub)
|
||||
void EdgeDice::dice(const DiagSplit &split)
|
||||
{
|
||||
const size_t num_subpatches = split.get_num_subpatches();
|
||||
for (size_t i = 0; i < num_subpatches; i++) {
|
||||
dice(split.get_subpatch(i));
|
||||
}
|
||||
|
||||
/* Vertex coordinates for sides. Needs to be done first because tessellation depends
|
||||
* on these coordinates and they are unique assigned to a subpatch for determinism. */
|
||||
parallel_for(blocked_range<size_t>(0, num_subpatches, 8), [&](const blocked_range<size_t> &r) {
|
||||
for (size_t i = r.begin(); i != r.end(); i++) {
|
||||
set_sides(split.get_subpatch(i));
|
||||
}
|
||||
});
|
||||
|
||||
/* Inner vertex coordinates and triangles. */
|
||||
parallel_for(blocked_range<size_t>(0, num_subpatches, 8), [&](const blocked_range<size_t> &r) {
|
||||
for (size_t i = r.begin(); i != r.end(); i++) {
|
||||
dice(split.get_subpatch(i));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
@@ -19,6 +19,7 @@ CCL_NAMESPACE_BEGIN
|
||||
class Camera;
|
||||
class Mesh;
|
||||
class Patch;
|
||||
class SubdAttributeInterpolation;
|
||||
class DiagSplit;
|
||||
|
||||
struct SubdParams {
|
||||
@@ -38,6 +39,7 @@ struct SubdParams {
|
||||
class EdgeDice {
|
||||
public:
|
||||
SubdParams params;
|
||||
SubdAttributeInterpolation &interpolation;
|
||||
int *mesh_triangles = nullptr;
|
||||
int *mesh_shader = nullptr;
|
||||
bool *mesh_smooth = nullptr;
|
||||
@@ -46,7 +48,10 @@ class EdgeDice {
|
||||
float *mesh_ptex_face_id = nullptr;
|
||||
float2 *mesh_ptex_uv = nullptr;
|
||||
|
||||
explicit EdgeDice(const SubdParams ¶ms, const int num_verts, const int num_triangles);
|
||||
explicit EdgeDice(const SubdParams ¶ms,
|
||||
const int num_verts,
|
||||
const int num_triangles,
|
||||
SubdAttributeInterpolation &interpolation);
|
||||
|
||||
void dice(const DiagSplit &split);
|
||||
|
||||
|
||||
@@ -42,6 +42,37 @@ struct SubdByte {
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
SubdAttributeInterpolation::SubdAttributeInterpolation(Mesh &mesh,
|
||||
OsdMesh &osd_mesh,
|
||||
OsdData &osd_data)
|
||||
#else
|
||||
SubdAttributeInterpolation::SubdAttributeInterpolation(Mesh &mesh)
|
||||
#endif
|
||||
: mesh(mesh)
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
,
|
||||
osd_mesh(osd_mesh),
|
||||
osd_data(osd_data)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
void SubdAttributeInterpolation::setup()
|
||||
{
|
||||
if (mesh.get_num_subd_faces() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const Attribute &subd_attr : mesh.subd_attributes.attributes) {
|
||||
if (!support_interp_attribute(subd_attr)) {
|
||||
continue;
|
||||
}
|
||||
Attribute &mesh_attr = mesh.attributes.copy(subd_attr);
|
||||
setup_attribute(subd_attr, mesh_attr);
|
||||
}
|
||||
}
|
||||
|
||||
bool SubdAttributeInterpolation::support_interp_attribute(const Attribute &attr) const
|
||||
{
|
||||
// TODO: Recompute UV tangent
|
||||
@@ -76,69 +107,50 @@ bool SubdAttributeInterpolation::support_interp_attribute(const Attribute &attr)
|
||||
return true;
|
||||
}
|
||||
|
||||
void SubdAttributeInterpolation::interp_attribute(const Attribute &subd_attr, Attribute &mesh_attr)
|
||||
void SubdAttributeInterpolation::setup_attribute(const Attribute &subd_attr, Attribute &mesh_attr)
|
||||
{
|
||||
if (subd_attr.element == ATTR_ELEMENT_CORNER_BYTE) {
|
||||
interp_attribute_type<SubdByte>(subd_attr, mesh_attr);
|
||||
setup_attribute_type<SubdByte>(subd_attr, mesh_attr);
|
||||
}
|
||||
else if (Attribute::same_storage(subd_attr.type, TypeFloat)) {
|
||||
interp_attribute_type<SubdFloat<float>>(subd_attr, mesh_attr);
|
||||
setup_attribute_type<SubdFloat<float>>(subd_attr, mesh_attr);
|
||||
}
|
||||
else if (Attribute::same_storage(subd_attr.type, TypeFloat2)) {
|
||||
interp_attribute_type<SubdFloat<float2>>(subd_attr, mesh_attr);
|
||||
setup_attribute_type<SubdFloat<float2>>(subd_attr, mesh_attr);
|
||||
}
|
||||
else if (Attribute::same_storage(subd_attr.type, TypeVector)) {
|
||||
interp_attribute_type<SubdFloat<float3>>(subd_attr, mesh_attr);
|
||||
setup_attribute_type<SubdFloat<float3>>(subd_attr, mesh_attr);
|
||||
}
|
||||
else if (Attribute::same_storage(subd_attr.type, TypeFloat4)) {
|
||||
interp_attribute_type<SubdFloat<float4>>(subd_attr, mesh_attr);
|
||||
setup_attribute_type<SubdFloat<float4>>(subd_attr, mesh_attr);
|
||||
}
|
||||
}
|
||||
const int *SubdAttributeInterpolation::get_ptex_face_mapping()
|
||||
{
|
||||
if (ptex_face_to_base_face.empty()) {
|
||||
ptex_face_to_base_face.resize(num_patches);
|
||||
|
||||
int *ptex_face_to_base_face_data = ptex_face_to_base_face.data();
|
||||
const size_t num_faces = mesh.get_num_subd_faces();
|
||||
int i = 0;
|
||||
|
||||
for (size_t f = 0; f < num_faces; f++) {
|
||||
Mesh::SubdFace face = mesh.get_subd_face(f);
|
||||
const int num_ptex_faces = (face.is_quad()) ? 1 : face.num_corners;
|
||||
for (int j = 0; j < num_ptex_faces; j++) {
|
||||
ptex_face_to_base_face_data[i++] = f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ptex_face_to_base_face.data();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SubdAttributeInterpolation::interp_attribute_vertex_linear(const Attribute &subd_attr,
|
||||
Attribute &mesh_attr,
|
||||
const int motion_step)
|
||||
void SubdAttributeInterpolation::setup_attribute_vertex_linear(const Attribute &subd_attr,
|
||||
Attribute &mesh_attr,
|
||||
const int motion_step)
|
||||
{
|
||||
const int *ptex_face_to_base_face_data = get_ptex_face_mapping();
|
||||
const int num_base_verts = mesh.get_num_subd_base_verts();
|
||||
SubdAttribute attr;
|
||||
|
||||
/* Interpolate values at vertices. */
|
||||
const size_t triangles_size = mesh.num_triangles();
|
||||
const int *patch_index = mesh.subd_triangle_patch_index.data();
|
||||
const float2 *patch_uv = mesh.subd_corner_patch_uv.data();
|
||||
const typename T::Type *subd_data = reinterpret_cast<const typename T::Type *>(
|
||||
subd_attr.data()) +
|
||||
motion_step * num_base_verts;
|
||||
motion_step * mesh.get_num_subd_base_verts();
|
||||
typename T::Type *mesh_data = reinterpret_cast<typename T::Type *>(mesh_attr.data()) +
|
||||
motion_step * mesh.get_verts().size();
|
||||
const int *subd_face_corners = mesh.get_subd_face_corners().data();
|
||||
|
||||
for (size_t i = 0; i < triangles_size; i++) {
|
||||
const int p = patch_index[i];
|
||||
const int f = ptex_face_to_base_face_data[p];
|
||||
Mesh::SubdFace face = mesh.get_subd_face(f);
|
||||
Mesh::Triangle triangle = mesh.get_triangle(i);
|
||||
assert(mesh_data != nullptr);
|
||||
|
||||
attr.interp = [this, subd_data, mesh_data](const int /*patch_index*/,
|
||||
const int face_index,
|
||||
const int corner,
|
||||
const int *vert_index,
|
||||
const float2 *vert_uv,
|
||||
const int vert_num) {
|
||||
/* Interpolate values at vertices. */
|
||||
const int *subd_face_corners = mesh.get_subd_face_corners().data();
|
||||
|
||||
Mesh::SubdFace face = mesh.get_subd_face(face_index);
|
||||
|
||||
if (face.is_quad()) {
|
||||
/* Simple case for quads. */
|
||||
@@ -151,16 +163,15 @@ void SubdAttributeInterpolation::interp_attribute_vertex_linear(const Attribute
|
||||
const typename T::AccumType value3 = T::read(
|
||||
subd_data[subd_face_corners[face.start_corner + 3]]);
|
||||
|
||||
for (int j = 0; j < 3; j++) {
|
||||
const float2 uv = patch_uv[(i * 3) + j];
|
||||
for (int i = 0; i < vert_num; i++) {
|
||||
const float2 uv = vert_uv[i];
|
||||
const typename T::AccumType value = interp(
|
||||
interp(value0, value1, uv.x), interp(value3, value2, uv.x), uv.y);
|
||||
mesh_data[triangle.v[j]] = T::output(value);
|
||||
mesh_data[vert_index[i]] = T::output(value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Other n-gons are split into n quads. */
|
||||
const int corner = p - face.ptex_offset;
|
||||
|
||||
/* Compute value at center of polygon. */
|
||||
typename T::AccumType value_center = T::read(
|
||||
@@ -170,37 +181,41 @@ void SubdAttributeInterpolation::interp_attribute_vertex_linear(const Attribute
|
||||
}
|
||||
value_center /= (float)face.num_corners;
|
||||
|
||||
for (int j = 0; j < 3; j++) {
|
||||
const float2 uv = patch_uv[(i * 3) + j];
|
||||
/* Compute value at corner at adjacent vertices. */
|
||||
const typename T::AccumType value_corner = T::read(
|
||||
subd_data[subd_face_corners[face.start_corner + corner]]);
|
||||
const typename T::AccumType value_prev =
|
||||
0.5f * (value_corner +
|
||||
T::read(subd_data[subd_face_corners[face.start_corner +
|
||||
mod(corner - 1, face.num_corners)]]));
|
||||
const typename T::AccumType value_next =
|
||||
0.5f * (value_corner +
|
||||
T::read(subd_data[subd_face_corners[face.start_corner +
|
||||
mod(corner + 1, face.num_corners)]]));
|
||||
|
||||
/* Compute value at corner at adjacent vertices. */
|
||||
const typename T::AccumType value_corner = T::read(
|
||||
subd_data[subd_face_corners[face.start_corner + corner]]);
|
||||
const typename T::AccumType value_prev =
|
||||
0.5f * (value_corner +
|
||||
T::read(subd_data[subd_face_corners[face.start_corner +
|
||||
mod(corner - 1, face.num_corners)]]));
|
||||
const typename T::AccumType value_next =
|
||||
0.5f * (value_corner +
|
||||
T::read(subd_data[subd_face_corners[face.start_corner +
|
||||
mod(corner + 1, face.num_corners)]]));
|
||||
for (int i = 0; i < vert_num; i++) {
|
||||
const float2 uv = vert_uv[i];
|
||||
|
||||
/* Interpolate. */
|
||||
const typename T::AccumType value = interp(
|
||||
interp(value_corner, value_next, uv.x), interp(value_prev, value_center, uv.x), uv.y);
|
||||
|
||||
mesh_data[triangle.v[j]] = T::output(value);
|
||||
mesh_data[vert_index[i]] = T::output(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
vertex_attributes.push_back(std::move(attr));
|
||||
}
|
||||
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
template<typename T>
|
||||
void SubdAttributeInterpolation::interp_attribute_vertex_smooth(const Attribute &subd_attr,
|
||||
Attribute &mesh_attr,
|
||||
const int motion_step)
|
||||
void SubdAttributeInterpolation::setup_attribute_vertex_smooth(const Attribute &subd_attr,
|
||||
Attribute &mesh_attr,
|
||||
const int motion_step)
|
||||
{
|
||||
SubdAttribute attr;
|
||||
|
||||
// TODO: Avoid computing derivative weights when not needed
|
||||
// TODO: overhead of FindPatch and EvaluateBasis with vertex position
|
||||
const int num_refiner_verts = osd_data.refiner->GetNumVerticesTotal();
|
||||
@@ -208,17 +223,19 @@ void SubdAttributeInterpolation::interp_attribute_vertex_smooth(const Attribute
|
||||
const int num_base_verts = mesh.get_num_subd_base_verts();
|
||||
|
||||
/* Refine attribute data to get patch coordinates. */
|
||||
array<typename T::AccumType> refined_array(num_refiner_verts + num_local_points);
|
||||
attr.refined_data.resize((num_refiner_verts + num_local_points) * sizeof(typename T::AccumType));
|
||||
typename T::AccumType *subd_data = reinterpret_cast<typename T::AccumType *>(
|
||||
attr.refined_data.data());
|
||||
|
||||
const typename T::Type *base_src = reinterpret_cast<const typename T::Type *>(subd_attr.data()) +
|
||||
num_base_verts * motion_step;
|
||||
typename T::AccumType *base_dst = refined_array.data();
|
||||
typename T::AccumType *base_dst = subd_data;
|
||||
for (int i = 0; i < num_base_verts; i++) {
|
||||
base_dst[i] = T::read(base_src[i]);
|
||||
}
|
||||
|
||||
Far::PrimvarRefiner primvar_refiner(*osd_data.refiner);
|
||||
typename T::AccumType *src = refined_array.data();
|
||||
typename T::AccumType *src = subd_data;
|
||||
for (int i = 0; i < osd_data.refiner->GetMaxLevel(); i++) {
|
||||
typename T::AccumType *dest = src + osd_data.refiner->GetLevel(i).GetNumVertices();
|
||||
primvar_refiner.Interpolate(
|
||||
@@ -228,18 +245,16 @@ void SubdAttributeInterpolation::interp_attribute_vertex_smooth(const Attribute
|
||||
|
||||
if (num_local_points) {
|
||||
osd_data.patch_table->ComputeLocalPointValues(
|
||||
(OsdValue<typename T::AccumType> *)refined_array.data(),
|
||||
(OsdValue<typename T::AccumType> *)(refined_array.data() + num_refiner_verts));
|
||||
(OsdValue<typename T::AccumType> *)subd_data,
|
||||
(OsdValue<typename T::AccumType> *)(subd_data + num_refiner_verts));
|
||||
}
|
||||
|
||||
/* Evaluate patches at limit. */
|
||||
const size_t triangles_size = mesh.num_triangles();
|
||||
const int *patch_index = mesh.subd_triangle_patch_index.data();
|
||||
const float2 *patch_uv = mesh.subd_corner_patch_uv.data();
|
||||
const typename T::AccumType *subd_data = refined_array.data();
|
||||
typename T::Type *mesh_data = reinterpret_cast<typename T::Type *>(mesh_attr.data()) +
|
||||
mesh.get_verts().size() * motion_step;
|
||||
|
||||
assert(mesh_data != nullptr);
|
||||
|
||||
/* Compute motion normals alongside positions. */
|
||||
float3 *mesh_normal_data = nullptr;
|
||||
if constexpr (std::is_same_v<typename T::Type, float3>) {
|
||||
@@ -249,15 +264,18 @@ void SubdAttributeInterpolation::interp_attribute_vertex_smooth(const Attribute
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < triangles_size; i++) {
|
||||
const int p = patch_index[i];
|
||||
Mesh::Triangle triangle = mesh.get_triangle(i);
|
||||
|
||||
for (int j = 0; j < 3; j++) {
|
||||
attr.interp = [this, subd_data, mesh_data, mesh_normal_data](const int patch_index,
|
||||
const int /*face_index*/,
|
||||
const int /*corner*/,
|
||||
const int *vert_index,
|
||||
const float2 *vert_uv,
|
||||
const int vert_num) {
|
||||
for (int i = 0; i < vert_num; i++) {
|
||||
/* Compute patch weights. */
|
||||
const float2 uv = patch_uv[(i * 3) + j];
|
||||
const float2 uv = vert_uv[i];
|
||||
|
||||
const Far::PatchTable::PatchHandle &handle = *osd_data.patch_map->FindPatch(
|
||||
p, (double)uv.x, (double)uv.y);
|
||||
patch_index, (double)uv.x, (double)uv.y);
|
||||
|
||||
float p_weights[20], du_weights[20], dv_weights[20];
|
||||
osd_data.patch_table->EvaluateBasis(handle, uv.x, uv.y, p_weights, du_weights, dv_weights);
|
||||
@@ -268,11 +286,11 @@ void SubdAttributeInterpolation::interp_attribute_vertex_smooth(const Attribute
|
||||
for (int k = 1; k < cv.size(); k++) {
|
||||
value += subd_data[cv[k]] * p_weights[k];
|
||||
}
|
||||
mesh_data[triangle.v[j]] = T::output(value);
|
||||
mesh_data[vert_index[i]] = T::output(value);
|
||||
|
||||
/* Optionally compute normal. */
|
||||
if constexpr (std::is_same_v<typename T::Type, float3>) {
|
||||
if (mesh_normal_data) {
|
||||
if (mesh_normal_data) {
|
||||
if constexpr (std::is_same_v<typename T::Type, float3>) {
|
||||
float3 du = zero_float3();
|
||||
float3 dv = zero_float3();
|
||||
for (int k = 0; k < cv.size(); k++) {
|
||||
@@ -280,32 +298,37 @@ void SubdAttributeInterpolation::interp_attribute_vertex_smooth(const Attribute
|
||||
du += p * du_weights[k];
|
||||
dv += p * dv_weights[k];
|
||||
}
|
||||
mesh_normal_data[triangle.v[j]] = safe_normalize_fallback(cross(du, dv),
|
||||
mesh_normal_data[vert_index[i]] = safe_normalize_fallback(cross(du, dv),
|
||||
make_float3(0.0f, 0.0f, 1.0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
vertex_attributes.push_back(std::move(attr));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
void SubdAttributeInterpolation::interp_attribute_corner_linear(const Attribute &subd_attr,
|
||||
Attribute &mesh_attr)
|
||||
void SubdAttributeInterpolation::setup_attribute_corner_linear(const Attribute &subd_attr,
|
||||
Attribute &mesh_attr)
|
||||
{
|
||||
const int *ptex_face_to_base_face_data = get_ptex_face_mapping();
|
||||
SubdAttribute attr;
|
||||
|
||||
/* Interpolate values at corners. */
|
||||
const size_t triangles_size = mesh.num_triangles();
|
||||
const int *patch_index = mesh.subd_triangle_patch_index.data();
|
||||
const float2 *patch_uv = mesh.subd_corner_patch_uv.data();
|
||||
const typename T::Type *subd_data = reinterpret_cast<const typename T::Type *>(subd_attr.data());
|
||||
typename T::Type *mesh_data = reinterpret_cast<typename T::Type *>(mesh_attr.data());
|
||||
|
||||
for (size_t i = 0; i < triangles_size; i++) {
|
||||
const int p = patch_index[i];
|
||||
const int f = ptex_face_to_base_face_data[p];
|
||||
Mesh::SubdFace face = mesh.get_subd_face(f);
|
||||
assert(mesh_data != nullptr);
|
||||
|
||||
attr.interp = [this, subd_data, mesh_data](const int /*patch_index*/,
|
||||
const int face_index,
|
||||
const int corner,
|
||||
const int *triangle_index,
|
||||
const float2 *triangle_uv,
|
||||
const int triangle_num) {
|
||||
Mesh::SubdFace face = mesh.get_subd_face(face_index);
|
||||
|
||||
if (face.is_quad()) {
|
||||
/* Simple case for quads. */
|
||||
@@ -314,16 +337,17 @@ void SubdAttributeInterpolation::interp_attribute_corner_linear(const Attribute
|
||||
const typename T::AccumType value2 = T::read(subd_data[face.start_corner + 2]);
|
||||
const typename T::AccumType value3 = T::read(subd_data[face.start_corner + 3]);
|
||||
|
||||
for (int j = 0; j < 3; j++) {
|
||||
const float2 uv = patch_uv[(i * 3) + j];
|
||||
const typename T::AccumType value = interp(
|
||||
interp(value0, value1, uv.x), interp(value3, value2, uv.x), uv.y);
|
||||
mesh_data[(i * 3) + j] = T::output(value);
|
||||
for (size_t i = 0; i < triangle_num; i++) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
const float2 uv = triangle_uv[(i * 3) + j];
|
||||
const typename T::AccumType value = interp(
|
||||
interp(value0, value1, uv.x), interp(value3, value2, uv.x), uv.y);
|
||||
mesh_data[triangle_index[i] * 3 + j] = T::output(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Other n-gons are split into n quads. */
|
||||
const int corner = p - face.ptex_offset;
|
||||
|
||||
/* Compute value at center of polygon. */
|
||||
typename T::AccumType value_center = T::read(subd_data[face.start_corner]);
|
||||
@@ -331,51 +355,60 @@ void SubdAttributeInterpolation::interp_attribute_corner_linear(const Attribute
|
||||
value_center += T::read(subd_data[face.start_corner + j]);
|
||||
}
|
||||
|
||||
for (int j = 0; j < 3; j++) {
|
||||
const float2 uv = patch_uv[(i * 3) + j];
|
||||
/* Compute value at corner at adjacent vertices. */
|
||||
const typename T::AccumType value_corner = T::read(subd_data[face.start_corner + corner]);
|
||||
const typename T::AccumType value_prev =
|
||||
0.5f * (value_corner +
|
||||
T::read(subd_data[face.start_corner + mod(corner - 1, face.num_corners)]));
|
||||
const typename T::AccumType value_next =
|
||||
0.5f * (value_corner +
|
||||
T::read(subd_data[face.start_corner + mod(corner + 1, face.num_corners)]));
|
||||
|
||||
/* Compute value at corner at adjacent vertices. */
|
||||
const typename T::AccumType value_corner = T::read(subd_data[face.start_corner + corner]);
|
||||
const typename T::AccumType value_prev =
|
||||
0.5f * (value_corner +
|
||||
T::read(subd_data[face.start_corner + mod(corner - 1, face.num_corners)]));
|
||||
const typename T::AccumType value_next =
|
||||
0.5f * (value_corner +
|
||||
T::read(subd_data[face.start_corner + mod(corner + 1, face.num_corners)]));
|
||||
for (size_t i = 0; i < triangle_num; i++) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
const float2 uv = triangle_uv[(i * 3) + j];
|
||||
|
||||
/* Interpolate. */
|
||||
const typename T::AccumType value = interp(
|
||||
interp(value_corner, value_next, uv.x), interp(value_prev, value_center, uv.x), uv.y);
|
||||
/* Interpolate. */
|
||||
const typename T::AccumType value = interp(interp(value_corner, value_next, uv.x),
|
||||
interp(value_prev, value_center, uv.x),
|
||||
uv.y);
|
||||
|
||||
mesh_data[(i * 3) + j] = T::output(value);
|
||||
mesh_data[triangle_index[i] * 3 + j] = T::output(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
triangle_attributes.push_back(std::move(attr));
|
||||
}
|
||||
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
template<typename T>
|
||||
void SubdAttributeInterpolation::interp_attribute_corner_smooth(Attribute &mesh_attr,
|
||||
const int channel,
|
||||
const vector<char> &merged_values)
|
||||
void SubdAttributeInterpolation::setup_attribute_corner_smooth(Attribute &mesh_attr,
|
||||
const int channel,
|
||||
const vector<char> &merged_values)
|
||||
{
|
||||
SubdAttribute attr;
|
||||
|
||||
// TODO: Avoid computing derivative weights when not needed
|
||||
const int num_refiner_fvars = osd_data.refiner->GetNumFVarValuesTotal(channel);
|
||||
const int num_local_points = osd_data.patch_table->GetNumLocalPointsFaceVarying(channel);
|
||||
const int num_base_fvars = osd_data.refiner->GetLevel(0).GetNumFVarValues(channel);
|
||||
|
||||
/* Refine attribute data to get patch coordinates. */
|
||||
array<typename T::AccumType> refined_array(num_refiner_fvars + num_local_points);
|
||||
attr.refined_data.resize((num_refiner_fvars + num_local_points) * sizeof(typename T::AccumType));
|
||||
typename T::AccumType *refined_data = reinterpret_cast<typename T::AccumType *>(
|
||||
attr.refined_data.data());
|
||||
|
||||
const typename T::Type *base_src = reinterpret_cast<const typename T::Type *>(
|
||||
merged_values.data());
|
||||
typename T::AccumType *base_dst = refined_array.data();
|
||||
typename T::AccumType *base_dst = refined_data;
|
||||
for (int i = 0; i < num_base_fvars; i++) {
|
||||
base_dst[i] = T::read(base_src[i]);
|
||||
}
|
||||
|
||||
Far::PrimvarRefiner primvar_refiner(*osd_data.refiner);
|
||||
typename T::AccumType *src = refined_array.data();
|
||||
typename T::AccumType *src = refined_data;
|
||||
for (int i = 0; i < osd_data.refiner->GetMaxLevel(); i++) {
|
||||
typename T::AccumType *dest = src + osd_data.refiner->GetLevel(i).GetNumFVarValues(channel);
|
||||
primvar_refiner.InterpolateFaceVarying(i + 1,
|
||||
@@ -387,73 +420,85 @@ void SubdAttributeInterpolation::interp_attribute_corner_smooth(Attribute &mesh_
|
||||
|
||||
if (num_local_points) {
|
||||
osd_data.patch_table->ComputeLocalPointValuesFaceVarying(
|
||||
(OsdValue<typename T::AccumType> *)refined_array.data(),
|
||||
(OsdValue<typename T::AccumType> *)(refined_array.data() + num_refiner_fvars),
|
||||
(OsdValue<typename T::AccumType> *)refined_data,
|
||||
(OsdValue<typename T::AccumType> *)(refined_data + num_refiner_fvars),
|
||||
channel);
|
||||
}
|
||||
|
||||
/* Evaluate patches at limit. */
|
||||
const size_t triangles_size = mesh.num_triangles();
|
||||
const int *patch_index = mesh.subd_triangle_patch_index.data();
|
||||
const float2 *patch_uv = mesh.subd_corner_patch_uv.data();
|
||||
const typename T::AccumType *subd_data = refined_array.data();
|
||||
const typename T::AccumType *subd_data = refined_data;
|
||||
typename T::Type *mesh_data = reinterpret_cast<typename T::Type *>(mesh_attr.data());
|
||||
|
||||
for (size_t i = 0; i < triangles_size; i++) {
|
||||
const int p = patch_index[i];
|
||||
assert(mesh_data != nullptr);
|
||||
|
||||
for (int j = 0; j < 3; j++) {
|
||||
/* Compute patch weights. */
|
||||
const float2 uv = patch_uv[(i * 3) + j];
|
||||
const Far::PatchTable::PatchHandle &handle = *osd_data.patch_map->FindPatch(
|
||||
p, (double)uv.x, (double)uv.y);
|
||||
attr.interp = [this, subd_data, mesh_data, channel](const int patch_index,
|
||||
const int /*face_index*/,
|
||||
const int /*corner*/,
|
||||
const int *triangle_index,
|
||||
const float2 *triangle_uv,
|
||||
const int triangle_num) {
|
||||
for (size_t i = 0; i < triangle_num; i++) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
/* Compute patch weights. */
|
||||
const float2 uv = triangle_uv[(i * 3) + j];
|
||||
const Far::PatchTable::PatchHandle &handle = *osd_data.patch_map->FindPatch(
|
||||
patch_index, (double)uv.x, (double)uv.y);
|
||||
|
||||
float p_weights[20], du_weights[20], dv_weights[20];
|
||||
osd_data.patch_table->EvaluateBasisFaceVarying(handle,
|
||||
uv.x,
|
||||
uv.y,
|
||||
p_weights,
|
||||
du_weights,
|
||||
dv_weights,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
channel);
|
||||
Far::ConstIndexArray cv = osd_data.patch_table->GetPatchFVarValues(handle, channel);
|
||||
float p_weights[20], du_weights[20], dv_weights[20];
|
||||
osd_data.patch_table->EvaluateBasisFaceVarying(handle,
|
||||
uv.x,
|
||||
uv.y,
|
||||
p_weights,
|
||||
du_weights,
|
||||
dv_weights,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
channel);
|
||||
Far::ConstIndexArray cv = osd_data.patch_table->GetPatchFVarValues(handle, channel);
|
||||
|
||||
/* Compution position. */
|
||||
typename T::AccumType value = subd_data[cv[0]] * p_weights[0];
|
||||
for (int k = 1; k < cv.size(); k++) {
|
||||
value += subd_data[cv[k]] * p_weights[k];
|
||||
/* Compution position. */
|
||||
typename T::AccumType value = subd_data[cv[0]] * p_weights[0];
|
||||
for (int k = 1; k < cv.size(); k++) {
|
||||
value += subd_data[cv[k]] * p_weights[k];
|
||||
}
|
||||
mesh_data[triangle_index[i] * 3 + j] = T::output(value);
|
||||
}
|
||||
mesh_data[(i * 3) + j] = T::output(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
triangle_attributes.push_back(std::move(attr));
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
void SubdAttributeInterpolation::interp_attribute_face(const Attribute &subd_attr,
|
||||
Attribute &mesh_attr)
|
||||
void SubdAttributeInterpolation::setup_attribute_face(const Attribute &subd_attr,
|
||||
Attribute &mesh_attr)
|
||||
{
|
||||
const int *ptex_face_to_base_face_data = get_ptex_face_mapping();
|
||||
|
||||
/* Interpolate values at corners. */
|
||||
const size_t triangles_size = mesh.num_triangles();
|
||||
const int *patch_index = mesh.subd_triangle_patch_index.data();
|
||||
/* Copy value from face to triangle .*/
|
||||
SubdAttribute attr;
|
||||
const typename T::Type *subd_data = reinterpret_cast<const typename T::Type *>(subd_attr.data());
|
||||
typename T::Type *mesh_data = reinterpret_cast<typename T::Type *>(mesh_attr.data());
|
||||
|
||||
for (size_t i = 0; i < triangles_size; i++) {
|
||||
const int p = patch_index[i];
|
||||
const int f = ptex_face_to_base_face_data[p];
|
||||
mesh_data[i] = subd_data[f];
|
||||
}
|
||||
assert(mesh_data != nullptr);
|
||||
|
||||
attr.interp = [subd_data, mesh_data](const int /*patch_index*/,
|
||||
const int face_index,
|
||||
const int /*corner*/,
|
||||
const int *triangle_index,
|
||||
const float2 * /*triangle_uv*/,
|
||||
const int triangle_num) {
|
||||
for (int i = 0; i < triangle_num; i++) {
|
||||
mesh_data[triangle_index[i]] = subd_data[face_index];
|
||||
}
|
||||
};
|
||||
|
||||
triangle_attributes.push_back(std::move(attr));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SubdAttributeInterpolation::interp_attribute_type(const Attribute &subd_attr,
|
||||
Attribute &mesh_attr)
|
||||
void SubdAttributeInterpolation::setup_attribute_type(const Attribute &subd_attr,
|
||||
Attribute &mesh_attr)
|
||||
{
|
||||
switch (subd_attr.element) {
|
||||
case ATTR_ELEMENT_VERTEX: {
|
||||
@@ -464,17 +509,17 @@ void SubdAttributeInterpolation::interp_attribute_type(const Attribute &subd_att
|
||||
case ATTR_STD_GENERATED:
|
||||
case ATTR_STD_POSITION_UNDEFORMED:
|
||||
case ATTR_STD_POSITION_UNDISPLACED:
|
||||
interp_attribute_vertex_smooth<T>(subd_attr, mesh_attr);
|
||||
setup_attribute_vertex_smooth<T>(subd_attr, mesh_attr);
|
||||
break;
|
||||
default:
|
||||
interp_attribute_vertex_linear<T>(subd_attr, mesh_attr);
|
||||
setup_attribute_vertex_linear<T>(subd_attr, mesh_attr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
interp_attribute_vertex_linear<T>(subd_attr, mesh_attr);
|
||||
setup_attribute_vertex_linear<T>(subd_attr, mesh_attr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -485,15 +530,14 @@ void SubdAttributeInterpolation::interp_attribute_type(const Attribute &subd_att
|
||||
for (const auto &merged_fvar : osd_mesh.merged_fvars) {
|
||||
if (&merged_fvar.attr == &subd_attr) {
|
||||
if constexpr (std::is_same_v<typename T::Type, float2>) {
|
||||
interp_attribute_corner_smooth<T>(
|
||||
mesh_attr, merged_fvar.channel, merged_fvar.values);
|
||||
setup_attribute_corner_smooth<T>(mesh_attr, merged_fvar.channel, merged_fvar.values);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
interp_attribute_corner_linear<T>(subd_attr, mesh_attr);
|
||||
setup_attribute_corner_linear<T>(subd_attr, mesh_attr);
|
||||
break;
|
||||
}
|
||||
case ATTR_ELEMENT_VERTEX_MOTION: {
|
||||
@@ -501,18 +545,18 @@ void SubdAttributeInterpolation::interp_attribute_type(const Attribute &subd_att
|
||||
for (int step = 0; step < mesh.get_motion_steps() - 1; step++) {
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
if (mesh.get_subdivision_type() == Mesh::SUBDIVISION_CATMULL_CLARK) {
|
||||
interp_attribute_vertex_smooth<T>(subd_attr, mesh_attr, step);
|
||||
setup_attribute_vertex_smooth<T>(subd_attr, mesh_attr, step);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
interp_attribute_vertex_linear<T>(subd_attr, mesh_attr, step);
|
||||
setup_attribute_vertex_linear<T>(subd_attr, mesh_attr, step);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ATTR_ELEMENT_FACE: {
|
||||
interp_attribute_face<T>(subd_attr, mesh_attr);
|
||||
setup_attribute_face<T>(subd_attr, mesh_attr);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
|
||||
#include "util/vector.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class Mesh;
|
||||
@@ -15,11 +18,15 @@ class Attribute;
|
||||
|
||||
/* Attribute Interpolation. */
|
||||
|
||||
struct SubdAttribute {
|
||||
std::function<void(const int, const int, const int, const int *, const float2 *, const int)>
|
||||
interp;
|
||||
vector<char> refined_data;
|
||||
};
|
||||
|
||||
class SubdAttributeInterpolation {
|
||||
protected:
|
||||
Mesh &mesh;
|
||||
int num_patches;
|
||||
vector<int> ptex_face_to_base_face;
|
||||
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
OsdMesh &osd_mesh;
|
||||
@@ -27,52 +34,44 @@ class SubdAttributeInterpolation {
|
||||
#endif
|
||||
|
||||
public:
|
||||
SubdAttributeInterpolation(Mesh &mesh,
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
OsdMesh &osd_mesh,
|
||||
OsdData &osd_data,
|
||||
#endif
|
||||
const int num_patches)
|
||||
: mesh(mesh),
|
||||
num_patches(num_patches)
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
,
|
||||
osd_mesh(osd_mesh),
|
||||
osd_data(osd_data)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
vector<SubdAttribute> vertex_attributes;
|
||||
vector<SubdAttribute> triangle_attributes;
|
||||
|
||||
bool support_interp_attribute(const Attribute &attr) const;
|
||||
void interp_attribute(const Attribute &subd_attr, Attribute &mesh_attr);
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
SubdAttributeInterpolation(Mesh &mesh, OsdMesh &osd_mesh, OsdData &osd_data);
|
||||
#else
|
||||
SubdAttributeInterpolation(Mesh &mesh);
|
||||
#endif
|
||||
|
||||
void setup();
|
||||
|
||||
protected:
|
||||
/* PTex face id to base mesh face mapping. */
|
||||
const int *get_ptex_face_mapping();
|
||||
bool support_interp_attribute(const Attribute &attr) const;
|
||||
void setup_attribute(const Attribute &subd_attr, Attribute &mesh_attr);
|
||||
|
||||
template<typename T>
|
||||
void interp_attribute_vertex_linear(const Attribute &subd_attr,
|
||||
Attribute &mesh_attr,
|
||||
const int motion_step = 0);
|
||||
void setup_attribute_vertex_linear(const Attribute &subd_attr,
|
||||
Attribute &mesh_attr,
|
||||
const int motion_step = 0);
|
||||
|
||||
template<typename T>
|
||||
void interp_attribute_corner_linear(const Attribute &subd_attr, Attribute &mesh_attr);
|
||||
void setup_attribute_corner_linear(const Attribute &subd_attr, Attribute &mesh_attr);
|
||||
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
template<typename T>
|
||||
void interp_attribute_vertex_smooth(const Attribute &subd_attr,
|
||||
Attribute &mesh_attr,
|
||||
const int motion_step = 0);
|
||||
void setup_attribute_vertex_smooth(const Attribute &subd_attr,
|
||||
Attribute &mesh_attr,
|
||||
const int motion_step = 0);
|
||||
|
||||
template<typename T>
|
||||
void interp_attribute_corner_smooth(Attribute &mesh_attr,
|
||||
const int channel,
|
||||
const vector<char> &merged_values);
|
||||
void setup_attribute_corner_smooth(Attribute &mesh_attr,
|
||||
const int channel,
|
||||
const vector<char> &merged_values);
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
void interp_attribute_face(const Attribute &subd_attr, Attribute &mesh_attr);
|
||||
template<typename T> void setup_attribute_face(const Attribute &subd_attr, Attribute &mesh_attr);
|
||||
|
||||
template<typename T>
|
||||
void interp_attribute_type(const Attribute &subd_attr, Attribute &mesh_attr);
|
||||
template<typename T> void setup_attribute_type(const Attribute &subd_attr, Attribute &mesh_attr);
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
@@ -554,17 +554,14 @@ void DiagSplit::split_patches(const Patch *patches, const size_t patches_byte_st
|
||||
owned_verts.resize(num_verts, false);
|
||||
|
||||
/* Split all faces in the mesh. */
|
||||
int patch_index = 0;
|
||||
for (int f = 0; f < params.mesh->get_num_subd_faces(); f++) {
|
||||
Mesh::SubdFace face = params.mesh->get_subd_face(f);
|
||||
const Patch *patch = (const Patch *)(((char *)patches) + (patch_index * patches_byte_stride));
|
||||
|
||||
const Patch *patch = (const Patch *)(((char *)patches) +
|
||||
(face.ptex_offset * patches_byte_stride));
|
||||
if (face.is_quad()) {
|
||||
patch_index++;
|
||||
split_quad(face, f, patch);
|
||||
}
|
||||
else {
|
||||
patch_index += face.num_corners;
|
||||
split_ngon(face, f, patch, patches_byte_stride);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user