Refactor: Cycles: Tweaks to adaptive subdivision dicing

* Move dicing out of DiagSplit, caller now uses EdgeDice
* Merge, rename and reorder various EdgeDice functions
* Compute triangle indices for subpatches in advance

Pull Request: https://projects.blender.org/blender/blender/pulls/136411
This commit is contained in:
Brecht Van Lommel 2025-03-23 19:35:33 +01:00
parent 17fbfbc2d5
commit 4e16c4bd7d
8 changed files with 357 additions and 351 deletions

View file

@ -792,8 +792,7 @@ void GeometryManager::device_update(Device *device,
subd_params.objecttoworld = mesh->get_subd_objecttoworld();
subd_params.camera = dicing_camera;
DiagSplit dsplit(subd_params);
mesh->tessellate(&dsplit);
mesh->tessellate(subd_params);
i++;

View file

@ -225,7 +225,7 @@ class Mesh : public Geometry {
bool has_motion_blur() const override;
PrimitiveType primitive_type() const override;
void tessellate(DiagSplit *split);
void tessellate(SubdParams &params);
SubdFace get_subd_face(const size_t index) const;
size_t get_num_subd_faces() const

View file

@ -15,7 +15,7 @@
CCL_NAMESPACE_BEGIN
void Mesh::tessellate(DiagSplit *split)
void Mesh::tessellate(SubdParams &params)
{
/* reset the number of subdivision vertices, in case the Mesh was not cleared
* between calls or data updates */
@ -79,7 +79,12 @@ void Mesh::tessellate(DiagSplit *split)
}
/* Split patches. */
split->split_patches(osd_patches.data(), sizeof(OsdPatch));
DiagSplit split(params);
split.split_patches(osd_patches.data(), sizeof(OsdPatch));
/* Dice patches. */
EdgeDice dice(params, split.get_num_verts(), split.get_num_triangles());
dice.dice(split);
}
else
#endif
@ -138,7 +143,12 @@ void Mesh::tessellate(DiagSplit *split)
}
/* Split patches. */
split->split_patches(linear_patches.data(), sizeof(LinearQuadPatch));
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()) {

View file

@ -7,244 +7,38 @@
#include "subd/dice.h"
#include "subd/patch.h"
#include "subd/split.h"
#include "util/tbb.h"
CCL_NAMESPACE_BEGIN
/* EdgeDice Base */
EdgeDice::EdgeDice(const SubdParams &params_) : params(params_)
{
params.mesh->attributes.add(ATTR_STD_VERTEX_NORMAL);
if (params.ptex) {
params.mesh->attributes.add(ATTR_STD_PTEX_FACE_ID);
params.mesh->attributes.add(ATTR_STD_PTEX_UV);
}
}
void EdgeDice::reserve(const int num_verts, const int num_triangles)
EdgeDice::EdgeDice(const SubdParams &params_, const int num_verts, const int num_triangles)
: params(params_)
{
Mesh *mesh = params.mesh;
mesh->num_subd_added_verts = num_verts - mesh->get_verts().size();
mesh->resize_mesh(num_verts, 0);
mesh->reserve_mesh(num_verts, num_triangles);
mesh->resize_mesh(num_verts, num_triangles);
Attribute *attr_vN = mesh->attributes.add(ATTR_STD_VERTEX_NORMAL);
mesh_triangles = mesh->triangles.data();
mesh_shader = mesh->shader.data();
mesh_smooth = mesh->smooth.data();
mesh_P = mesh->verts.data();
mesh_N = attr_vN->data_float3();
Attribute *attr_ptex_face_id = mesh->attributes.find(ATTR_STD_PTEX_FACE_ID);
if (attr_ptex_face_id) {
if (params.ptex) {
Attribute *attr_ptex_face_id = params.mesh->attributes.add(ATTR_STD_PTEX_FACE_ID);
Attribute *attr_ptex_uv = params.mesh->attributes.add(ATTR_STD_PTEX_UV);
mesh_ptex_face_id = attr_ptex_face_id->data_float();
}
Attribute *attr_ptex_uv = mesh->attributes.find(ATTR_STD_PTEX_UV);
if (attr_ptex_uv) {
mesh_ptex_uv = attr_ptex_uv->data_float2();
}
}
void EdgeDice::set_vert(const Patch *patch, const int index, const float2 uv)
{
float3 P;
float3 N;
patch->eval(&P, nullptr, nullptr, &N, uv.x, uv.y);
assert(index < params.mesh->verts.size());
mesh_P[index] = P;
mesh_N[index] = N;
}
void EdgeDice::add_triangle(const Patch *patch,
const int v0,
const int v1,
const int v2,
const float2 uv0,
const float2 uv1,
const float2 uv2)
{
Mesh *mesh = params.mesh;
mesh->add_triangle(v0, v1, v2, patch->shader, true);
const int triangle_offset = params.mesh->num_triangles() - 1;
params.mesh->subd_triangle_patch_index[triangle_offset] = patch->patch_index;
if (mesh_ptex_face_id) {
mesh_ptex_face_id[triangle_offset] = patch->patch_index;
}
params.mesh->subd_corner_patch_uv[(triangle_offset * 3) + 0] = uv0;
params.mesh->subd_corner_patch_uv[(triangle_offset * 3) + 1] = uv1;
params.mesh->subd_corner_patch_uv[(triangle_offset * 3) + 2] = uv2;
if (mesh_ptex_uv) {
mesh_ptex_uv[(triangle_offset * 3) + 0] = uv0;
mesh_ptex_uv[(triangle_offset * 3) + 0] = uv1;
mesh_ptex_uv[(triangle_offset * 3) + 0] = uv2;
}
}
void EdgeDice::stitch_triangles_to_inner_grid(SubPatch &sub,
const int edge,
const int Mu,
const int Mv)
{
const int outer_T = sub.edges[edge].edge->T;
const int inner_T = ((edge % 2) == 0) ? Mu - 2 : Mv - 2;
const float du = 1.0f / (float)Mu;
const float dv = 1.0f / (float)Mv;
float2 inner_uv, outer_uv, inner_uv_step, outer_uv_step;
switch (edge) {
case 0:
inner_uv = make_float2(du, dv);
outer_uv = make_float2(0.0f, 0.0f);
inner_uv_step = make_float2(du, 0.0f);
outer_uv_step = make_float2(1.0f / (float)outer_T, 0.0f);
break;
case 1:
inner_uv = make_float2(1.0f - du, dv);
outer_uv = make_float2(1.0f, 0.0f);
inner_uv_step = make_float2(0.0f, dv);
outer_uv_step = make_float2(0.0f, 1.0f / (float)outer_T);
break;
case 2:
inner_uv = make_float2(1.0f - du, 1.0f - dv);
outer_uv = make_float2(1.0f, 1.0f);
inner_uv_step = make_float2(-du, 0.0f);
outer_uv_step = make_float2(-1.0f / (float)outer_T, 0.0f);
break;
case 3:
default:
inner_uv = make_float2(du, 1.0f - dv);
outer_uv = make_float2(0.0f, 1.0f);
inner_uv_step = make_float2(0.0f, -dv);
outer_uv_step = make_float2(0.0f, -1.0f / (float)outer_T);
break;
}
/* Stitch together two arrays of verts with triangles. at each step, we compare using the next
* verts on both sides, to find the split direction with the smallest diagonal, and use that
* in order to keep the triangle shape reasonable. */
for (size_t i = 0, j = 0; i < inner_T || j < outer_T;) {
const int v0 = sub.get_vert_along_grid_edge(edge, i);
const int v1 = sub.get_vert_along_edge(edge, j);
int v2;
const float2 uv0 = sub.map_uv(inner_uv);
const float2 uv1 = sub.map_uv(outer_uv);
float2 uv2;
if (j == outer_T) {
v2 = sub.get_vert_along_grid_edge(edge, ++i);
inner_uv += inner_uv_step;
uv2 = sub.map_uv(inner_uv);
}
else if (i == inner_T) {
v2 = sub.get_vert_along_edge(edge, ++j);
outer_uv += outer_uv_step;
uv2 = sub.map_uv(outer_uv);
}
else {
/* Length of diagonals. */
const float len1 = len_squared(mesh_P[sub.get_vert_along_grid_edge(edge, i)] -
mesh_P[sub.get_vert_along_edge(edge, j + 1)]);
const float len2 = len_squared(mesh_P[sub.get_vert_along_edge(edge, j)] -
mesh_P[sub.get_vert_along_grid_edge(edge, i + 1)]);
/* Use smallest diagonal. */
if (len1 < len2) {
v2 = sub.get_vert_along_edge(edge, ++j);
outer_uv += outer_uv_step;
uv2 = sub.map_uv(outer_uv);
}
else {
v2 = sub.get_vert_along_grid_edge(edge, ++i);
inner_uv += inner_uv_step;
uv2 = sub.map_uv(inner_uv);
}
}
add_triangle(sub.patch, v0, v1, v2, uv0, uv1, uv2);
}
}
void EdgeDice::stitch_triangles_across(SubPatch &sub, const int left_edge, const int right_edge)
{
/* Stitch triangles from side to side, edge in the other direction has T = 1. */
const int left_T = sub.edges[left_edge].edge->T;
const int right_T = sub.edges[right_edge].edge->T;
float2 left_uv, right_uv, left_uv_step, right_uv_step;
if (right_edge == 0) {
left_uv = make_float2(0.0f, 1.0f);
right_uv = make_float2(0.0f, 0.0f);
left_uv_step = make_float2(1.0f / (float)left_T, 0.0f);
right_uv_step = make_float2(1.0f / (float)right_T, 0.0f);
}
else {
left_uv = make_float2(0.0f, 0.0f);
right_uv = make_float2(1.0f, 0.0f);
left_uv_step = make_float2(0.0f, 1.0f / (float)left_T);
right_uv_step = make_float2(0.0f, 1.0f / (float)right_T);
}
/* Stitch together two arrays of verts with triangles. at each step, we compare using the next
* verts on both sides, to find the split direction with the smallest diagonal, and use that
* in order to keep the triangle shape reasonable. */
for (size_t i = 0, j = 0; i < left_T || j < right_T;) {
const int v0 = sub.get_vert_along_edge_reverse(left_edge, i);
const int v1 = sub.get_vert_along_edge(right_edge, j);
int v2;
const float2 uv0 = sub.map_uv(left_uv);
const float2 uv1 = sub.map_uv(right_uv);
float2 uv2;
if (j == right_T) {
v2 = sub.get_vert_along_edge_reverse(left_edge, ++i);
left_uv += left_uv_step;
uv2 = sub.map_uv(left_uv);
}
else if (i == left_T) {
v2 = sub.get_vert_along_edge(right_edge, ++j);
right_uv += right_uv_step;
uv2 = sub.map_uv(right_uv);
}
else {
/* Length of diagonals. */
const float len1 = len_squared(mesh_P[sub.get_vert_along_edge_reverse(left_edge, i)] -
mesh_P[sub.get_vert_along_edge(right_edge, j + 1)]);
const float len2 = len_squared(mesh_P[sub.get_vert_along_edge(right_edge, j)] -
mesh_P[sub.get_vert_along_edge_reverse(left_edge, i + 1)]);
/* Use smallest diagonal. */
if (len1 < len2) {
v2 = sub.get_vert_along_edge(right_edge, ++j);
right_uv += right_uv_step;
uv2 = sub.map_uv(right_uv);
}
else {
v2 = sub.get_vert_along_edge_reverse(left_edge, ++i);
left_uv += left_uv_step;
uv2 = sub.map_uv(left_uv);
}
}
add_triangle(sub.patch, v0, v1, v2, uv0, uv1, uv2);
}
}
/* QuadDice */
QuadDice::QuadDice(const SubdParams &params_) : EdgeDice(params_) {}
float3 QuadDice::eval_projected(SubPatch &sub, const float2 uv)
float3 EdgeDice::eval_projected(const SubPatch &sub, const float2 uv)
{
float3 P;
@ -256,48 +50,12 @@ float3 QuadDice::eval_projected(SubPatch &sub, const float2 uv)
return P;
}
void QuadDice::set_vert(SubPatch &sub, const int index, const float2 uv)
{
EdgeDice::set_vert(sub.patch, index, sub.map_uv(uv));
}
void QuadDice::set_side(SubPatch &sub, const int edge)
{
const int t = sub.edges[edge].edge->T;
const int i_start = (sub.edges[edge].own_vertex) ? 0 : 1;
const int i_end = (sub.edges[edge].own_edge) ? t : 1;
/* set verts on the edge of the patch */
for (int i = i_start; i < i_end; i++) {
const float f = i / (float)t;
float2 uv;
switch (edge) {
case 0:
uv = make_float2(f, 0.0f);
break;
case 1:
uv = make_float2(1.0f, f);
break;
case 2:
uv = make_float2(1.0f - f, 1.0f);
break;
case 3:
default:
uv = make_float2(0.0f, 1.0f - f);
break;
}
set_vert(sub, sub.get_vert_along_edge(edge, i), uv);
}
}
float QuadDice::quad_area(const float3 &a, const float3 &b, const float3 &c, const float3 &d)
float EdgeDice::quad_area(const float3 &a, const float3 &b, const float3 &c, const float3 &d)
{
return triangle_area(a, b, d) + triangle_area(a, d, c);
}
float QuadDice::scale_factor(SubPatch &sub, const int Mu, const int Mv)
float EdgeDice::scale_factor(const SubPatch &sub, const int Mu, const int Mv)
{
/* estimate area as 4x largest of 4 quads */
float3 P[3][3];
@ -329,81 +87,311 @@ float QuadDice::scale_factor(SubPatch &sub, const int Mu, const int Mv)
return S;
}
void QuadDice::add_grid(SubPatch &sub, const int Mu, const int Mv, const int offset)
void EdgeDice::set_vertex(const SubPatch &sub, const int index, const float2 uv)
{
/* no inner grid? */
if (Mu == 1 || Mv == 1) {
return;
assert(index < params.mesh->verts.size());
float3 P;
float3 N;
sub.patch->eval(&P, nullptr, nullptr, &N, uv.x, uv.y);
mesh_P[index] = P;
mesh_N[index] = N;
}
void EdgeDice::add_triangle(const SubPatch &sub,
const int triangle_index,
const int v0,
const int v1,
const int v2,
const float2 uv0,
const float2 uv1,
const float2 uv2)
{
assert(triangle_index * 3 < params.mesh->triangles.size());
const Patch *patch = sub.patch;
mesh_triangles[triangle_index * 3 + 0] = v0;
mesh_triangles[triangle_index * 3 + 1] = v1;
mesh_triangles[triangle_index * 3 + 2] = v2;
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;
}
/* create inner grid */
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;
}
}
void EdgeDice::add_grid_triangles_and_stitch(const SubPatch &sub, const int Mu, const int Mv)
{
const float du = 1.0f / (float)Mu;
const float dv = 1.0f / (float)Mv;
const int grid_vertex_offset = sub.inner_grid_vert_offset;
int triangle_index = sub.triangles_offset;
/* Create inner grid. */
for (int j = 1; j < Mv; j++) {
for (int i = 1; i < Mu; i++) {
const float u = i * du;
const float v = j * dv;
const int center_i = offset + (i - 1) + (j - 1) * (Mu - 1);
const int center_i = grid_vertex_offset + (i - 1) + (j - 1) * (Mu - 1);
set_vert(sub, center_i, make_float2(u, v));
set_vertex(sub, center_i, sub.map_uv(make_float2(u, v)));
if (i < Mu - 1 && j < Mv - 1) {
const int i1 = offset + (i - 1) + (j - 1) * (Mu - 1);
const int i2 = offset + i + (j - 1) * (Mu - 1);
const int i3 = offset + i + j * (Mu - 1);
const int i4 = offset + (i - 1) + j * (Mu - 1);
const int i1 = grid_vertex_offset + (i - 1) + (j - 1) * (Mu - 1);
const int i2 = grid_vertex_offset + i + (j - 1) * (Mu - 1);
const int i3 = grid_vertex_offset + i + j * (Mu - 1);
const int i4 = grid_vertex_offset + (i - 1) + j * (Mu - 1);
const float2 uv1 = sub.map_uv(make_float2(u, v));
const float2 uv2 = sub.map_uv(make_float2(u + du, v));
const float2 uv3 = sub.map_uv(make_float2(u + du, v + dv));
const float2 uv4 = sub.map_uv(make_float2(u, v + dv));
add_triangle(sub.patch, i1, i2, i3, uv1, uv2, uv3);
add_triangle(sub.patch, i1, i3, i4, uv1, uv3, uv4);
add_triangle(sub, triangle_index++, i1, i2, i3, uv1, uv2, uv3);
add_triangle(sub, triangle_index++, i1, i3, i4, uv1, uv3, uv4);
}
}
}
/* Stitch inner grid to edges. */
for (int edge = 0; edge < 4; edge++) {
const int outer_T = sub.edges[edge].edge->T;
const int inner_T = ((edge % 2) == 0) ? Mu - 2 : Mv - 2;
float2 inner_uv, outer_uv, inner_uv_step, outer_uv_step;
switch (edge) {
case 0:
inner_uv = make_float2(du, dv);
outer_uv = make_float2(0.0f, 0.0f);
inner_uv_step = make_float2(du, 0.0f);
outer_uv_step = make_float2(1.0f / (float)outer_T, 0.0f);
break;
case 1:
inner_uv = make_float2(1.0f - du, dv);
outer_uv = make_float2(1.0f, 0.0f);
inner_uv_step = make_float2(0.0f, dv);
outer_uv_step = make_float2(0.0f, 1.0f / (float)outer_T);
break;
case 2:
inner_uv = make_float2(1.0f - du, 1.0f - dv);
outer_uv = make_float2(1.0f, 1.0f);
inner_uv_step = make_float2(-du, 0.0f);
outer_uv_step = make_float2(-1.0f / (float)outer_T, 0.0f);
break;
case 3:
default:
inner_uv = make_float2(du, 1.0f - dv);
outer_uv = make_float2(0.0f, 1.0f);
inner_uv_step = make_float2(0.0f, -dv);
outer_uv_step = make_float2(0.0f, -1.0f / (float)outer_T);
break;
}
/* Stitch together two arrays of verts with triangles. At each step, we compare using
* the next verts on both sides, to find the split direction with the smallest
* diagonal, and use that in order to keep the triangle shape reasonable. */
for (size_t i = 0, j = 0; i < inner_T || j < outer_T;) {
const int v0 = sub.get_vert_along_grid_edge(edge, i);
const int v1 = sub.get_vert_along_edge(edge, j);
int v2;
const float2 uv0 = sub.map_uv(inner_uv);
const float2 uv1 = sub.map_uv(outer_uv);
float2 uv2;
if (j == outer_T) {
v2 = sub.get_vert_along_grid_edge(edge, ++i);
inner_uv += inner_uv_step;
uv2 = sub.map_uv(inner_uv);
}
else if (i == inner_T) {
v2 = sub.get_vert_along_edge(edge, ++j);
outer_uv += outer_uv_step;
uv2 = sub.map_uv(outer_uv);
}
else {
/* Length of diagonals. */
const int v2_a = sub.get_vert_along_edge(edge, j + 1);
const int v2_b = sub.get_vert_along_grid_edge(edge, i + 1);
const float len_a = len_squared(mesh_P[v0] - mesh_P[v2_a]);
const float len_b = len_squared(mesh_P[v1] - mesh_P[v2_b]);
/* Use smallest diagonal. */
if (len_a < len_b) {
v2 = v2_a;
outer_uv += outer_uv_step;
uv2 = sub.map_uv(outer_uv);
j++;
}
else {
v2 = v2_b;
inner_uv += inner_uv_step;
uv2 = sub.map_uv(inner_uv);
i++;
}
}
add_triangle(sub, triangle_index++, v0, v1, v2, uv0, uv1, uv2);
}
}
}
void QuadDice::dice(SubPatch &sub)
void EdgeDice::add_triangle_strip(const SubPatch &sub, const int left_edge, const int right_edge)
{
/* Stitch triangles from side to side, edge in the other direction has T = 1. */
const int left_T = sub.edges[left_edge].edge->T;
const int right_T = sub.edges[right_edge].edge->T;
float2 left_uv, right_uv, left_uv_step, right_uv_step;
if (right_edge == 0) {
left_uv = make_float2(0.0f, 1.0f);
right_uv = make_float2(0.0f, 0.0f);
left_uv_step = make_float2(1.0f / (float)left_T, 0.0f);
right_uv_step = make_float2(1.0f / (float)right_T, 0.0f);
}
else {
left_uv = make_float2(0.0f, 0.0f);
right_uv = make_float2(1.0f, 0.0f);
left_uv_step = make_float2(0.0f, 1.0f / (float)left_T);
right_uv_step = make_float2(0.0f, 1.0f / (float)right_T);
}
/* Stitch together two arrays of verts with triangles. at each step, we compare using the next
* verts on both sides, to find the split direction with the smallest diagonal, and use that
* in order to keep the triangle shape reasonable. */
int triangle_index = sub.triangles_offset;
for (size_t i = 0, j = 0; i < left_T || j < right_T;) {
const int v0 = sub.get_vert_along_edge_reverse(left_edge, i);
const int v1 = sub.get_vert_along_edge(right_edge, j);
int v2;
const float2 uv0 = sub.map_uv(left_uv);
const float2 uv1 = sub.map_uv(right_uv);
float2 uv2;
if (j == right_T) {
v2 = sub.get_vert_along_edge_reverse(left_edge, ++i);
left_uv += left_uv_step;
uv2 = sub.map_uv(left_uv);
}
else if (i == left_T) {
v2 = sub.get_vert_along_edge(right_edge, ++j);
right_uv += right_uv_step;
uv2 = sub.map_uv(right_uv);
}
else {
/* Length of diagonals. */
const int v2_a = sub.get_vert_along_edge(right_edge, j + 1);
const int v2_b = sub.get_vert_along_edge_reverse(left_edge, i + 1);
const float len_a = len_squared(mesh_P[v0] - mesh_P[v2_a]);
const float len_b = len_squared(mesh_P[v1] - mesh_P[v2_b]);
/* Use smallest diagonal. */
if (len_a < len_b) {
v2 = v2_a;
right_uv += right_uv_step;
uv2 = sub.map_uv(right_uv);
j++;
}
else {
v2 = v2_b;
left_uv += left_uv_step;
uv2 = sub.map_uv(left_uv);
i++;
}
}
add_triangle(sub, triangle_index++, v0, v1, v2, uv0, uv1, uv2);
}
}
void EdgeDice::set_sides(const SubPatch &sub)
{
for (int edge = 0; edge < 4; edge++) {
const int t = sub.edges[edge].edge->T;
const int i_start = (sub.edges[edge].own_vertex) ? 0 : 1;
const int i_end = (sub.edges[edge].own_edge) ? t : 1;
/* set verts on the edge of the patch */
for (int i = i_start; i < i_end; i++) {
const float f = i / (float)t;
float2 uv;
switch (edge) {
case 0:
uv = make_float2(f, 0.0f);
break;
case 1:
uv = make_float2(1.0f, f);
break;
case 2:
uv = make_float2(1.0f - f, 1.0f);
break;
case 3:
default:
uv = make_float2(0.0f, 1.0f - f);
break;
}
const int vert_index = sub.get_vert_along_edge(edge, i);
set_vertex(sub, vert_index, sub.map_uv(uv));
}
}
}
void EdgeDice::dice(const SubPatch &sub)
{
/* Compute inner grid size with scale factor. */
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_side(sub, 0);
set_side(sub, 1);
set_side(sub, 2);
set_side(sub, 3);
set_sides(sub);
if (Mv == 1) {
/* No inner grid, stitch triangles from side to side. */
stitch_triangles_across(sub, 2, 0);
add_triangle_strip(sub, 2, 0);
}
else if (Mu == 1) {
/* No inner grid, stitch triangles from side to side. */
stitch_triangles_across(sub, 3, 1);
add_triangle_strip(sub, 3, 1);
}
else {
#if 0 /* Doesn't work very well, especially at grazing angles. */
#if 0
/* Doesn't work very well, especially at grazing angles. */
const float S = scale_factor(sub, ef, Mu, Mv);
#else
const float S = 1.0f;
#endif
const int grid_Mu = max((int)ceilf(S * Mu), 1); // XXX handle 0 & 1?
const int grid_Mv = max((int)ceilf(S * Mv), 1); // XXX handle 0 & 1?
add_grid_triangles_and_stitch(sub, grid_Mu, grid_Mv);
#else
add_grid_triangles_and_stitch(sub, Mu, Mv);
#endif
}
}
/* Inner grid. */
add_grid(sub, grid_Mu, grid_Mv, sub.inner_grid_vert_offset);
/* Stitch triangles to inner grid. */
stitch_triangles_to_inner_grid(sub, 0, Mu, Mv);
stitch_triangles_to_inner_grid(sub, 1, Mu, Mv);
stitch_triangles_to_inner_grid(sub, 2, Mu, Mv);
stitch_triangles_to_inner_grid(sub, 3, Mu, Mv);
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));
}
}

View file

@ -19,6 +19,7 @@ CCL_NAMESPACE_BEGIN
class Camera;
class Mesh;
class Patch;
class DiagSplit;
struct SubdParams {
Mesh *mesh = nullptr;
@ -34,23 +35,27 @@ struct SubdParams {
SubdParams(Mesh *mesh_, bool ptex_ = false) : mesh(mesh_), ptex(ptex_) {}
};
/* EdgeDice Base */
class EdgeDice {
public:
SubdParams params;
int *mesh_triangles = nullptr;
int *mesh_shader = nullptr;
bool *mesh_smooth = nullptr;
float3 *mesh_P = nullptr;
float3 *mesh_N = nullptr;
float *mesh_ptex_face_id = nullptr;
float2 *mesh_ptex_uv = nullptr;
explicit EdgeDice(const SubdParams &params);
explicit EdgeDice(const SubdParams &params, const int num_verts, const int num_triangles);
void reserve(const int num_verts, const int num_triangles);
void dice(const DiagSplit &split);
protected:
void set_vert(const Patch *patch, const int index, const float2 uv);
void add_triangle(const Patch *patch,
void dice(const SubPatch &sub);
void set_vertex(const SubPatch &sub, const int index, const float2 uv);
void add_triangle(const SubPatch &sub,
const int triangle_index,
const int v0,
const int v1,
const int v2,
@ -58,29 +63,15 @@ class EdgeDice {
const float2 uv1,
const float2 uv2);
void stitch_triangles_to_inner_grid(SubPatch &sub, const int edge, const int Mu, const int Mv);
void stitch_triangles_across(SubPatch &sub, const int left_edge, const int right_edge);
};
void add_grid_triangles_and_stitch(const SubPatch &sub, const int Mu, const int Mv);
void add_triangle_strip(const SubPatch &sub, const int left_edge, const int right_edge);
/* Quad EdgeDice */
float3 eval_projected(const SubPatch &sub, const float2 uv);
class QuadDice : public EdgeDice {
public:
explicit QuadDice(const SubdParams &params);
void dice(SubPatch &sub);
protected:
float3 eval_projected(SubPatch &sub, const float2 uv);
void set_vert(SubPatch &sub, const int index, const float2 uv);
void add_grid(SubPatch &sub, const int Mu, const int Mv, const int offset);
void set_side(SubPatch &sub, const int edge);
void set_sides(const SubPatch &sub);
float quad_area(const float3 &a, const float3 &b, const float3 &c, const float3 &d);
float scale_factor(SubPatch &sub, const int Mu, const int Mv);
float scale_factor(const SubPatch &sub, const int Mu, const int Mv);
};
CCL_NAMESPACE_END

View file

@ -77,6 +77,7 @@ void DiagSplit::alloc_subpatch(SubPatch &&sub)
assert(sub.edge_v0.edge->T >= 1);
sub.inner_grid_vert_offset = alloc_verts(sub.calc_num_inner_verts());
sub.triangles_offset = num_triangles;
num_triangles += sub.calc_num_triangles();
subpatches.push_back(std::move(sub));
@ -449,7 +450,7 @@ void DiagSplit::split(SubPatch &&sub, const int depth)
split(std::move(sub_b), depth + 1);
}
void DiagSplit::split_quad(const Mesh::SubdFace &face, const Patch *patch)
void DiagSplit::split_quad(const Mesh::SubdFace &face, const int face_index, const Patch *patch)
{
/*
* edge_u1
@ -467,7 +468,7 @@ void DiagSplit::split_quad(const Mesh::SubdFace &face, const Patch *patch)
const int v11 = subd_face_corners[face.start_corner + 2];
const int v01 = subd_face_corners[face.start_corner + 3];
SubPatch subpatch(patch);
SubPatch subpatch(patch, face_index);
alloc_edge(&subpatch.edge_u0, v00, v10, true, true);
alloc_edge(&subpatch.edge_v1, v10, v11, true, true);
alloc_edge(&subpatch.edge_u1, v11, v01, true, true);
@ -483,6 +484,7 @@ void DiagSplit::split_quad(const Mesh::SubdFace &face, const Patch *patch)
}
void DiagSplit::split_ngon(const Mesh::SubdFace &face,
const int face_index,
const Patch *patches,
const size_t patches_byte_stride)
{
@ -523,7 +525,7 @@ void DiagSplit::split_ngon(const Mesh::SubdFace &face,
const int v10 = edge_u0.mid_vert_index();
const int v01 = edge_v0.mid_vert_index();
SubPatch subpatch(patch);
SubPatch subpatch(patch, face_index, corner);
alloc_edge(&subpatch.edge_u0, v00, v10, false, false);
alloc_edge(&subpatch.edge_v1, v10, v11, true, false);
alloc_edge(&subpatch.edge_u1, v11, v01, true, corner == 0);
@ -541,7 +543,11 @@ void DiagSplit::split_ngon(const Mesh::SubdFace &face,
void DiagSplit::split_patches(const Patch *patches, const size_t patches_byte_stride)
{
/* Keep base mesh vertices, create new triangles. */
// TODO: reuse edge factor vertex position computations
// TODO: support not splitting n-gons if not needed
// TODO: multithreading
/* Keep base mesh vertices, create new triangels. */
num_verts = params.mesh->get_num_subd_base_verts();
num_triangles = 0;
@ -555,31 +561,13 @@ void DiagSplit::split_patches(const Patch *patches, const size_t patches_byte_st
if (face.is_quad()) {
patch_index++;
split_quad(face, patch);
split_quad(face, f, patch);
}
else {
patch_index += face.num_corners;
split_ngon(face, patch, patches_byte_stride);
split_ngon(face, f, patch, patches_byte_stride);
}
}
// TODO: reuse edge factor vertex position computations
// TODO: avoid multiple write for linear vert attributes
// TODO: avoid multiple write for smooth vert attributes
// TODO: support not splitting n-gons if not needed
// TODO: multithreading
/* Dice all patches. */
QuadDice dice(params);
dice.reserve(num_verts, num_triangles);
for (SubPatch &sub : subpatches) {
dice.dice(sub);
}
/* Cleanup */
subpatches.clear();
edges.clear();
}
CCL_NAMESPACE_END

View file

@ -22,6 +22,7 @@ CCL_NAMESPACE_BEGIN
class Mesh;
class Patch;
class SubdAttributeInterpolation;
class DiagSplit {
private:
@ -62,8 +63,9 @@ class DiagSplit {
float2 Pend,
const int depth);
void split(SubPatch &&sub, const int depth = 0);
void split_quad(const Mesh::SubdFace &face, const Patch *patch);
void split_quad(const Mesh::SubdFace &face, const int face_index, const Patch *patch);
void split_ngon(const Mesh::SubdFace &face,
const int face_index,
const Patch *patches,
const size_t patches_byte_stride);
@ -71,6 +73,26 @@ class DiagSplit {
explicit DiagSplit(const SubdParams &params);
void split_patches(const Patch *patches, const size_t patches_byte_stride);
size_t get_num_subpatches() const
{
return subpatches.size();
}
const SubPatch &get_subpatch(const size_t i) const
{
return subpatches[i];
}
int get_num_verts() const
{
return num_verts;
}
int get_num_triangles() const
{
return num_triangles;
}
};
CCL_NAMESPACE_END

View file

@ -75,8 +75,13 @@ class SubPatch {
public:
/* Patch this is a subpatch of. */
const Patch *patch = nullptr;
/* Face and corner. */
int face_index = 0;
int corner = 0;
/* Vertex indices for inner grid start at this index. */
int inner_grid_vert_offset = 0;
/* Triangle indices. */
int triangles_offset = 0;
/* Edge of patch. */
struct Edge {
@ -139,7 +144,10 @@ class SubPatch {
};
};
explicit SubPatch(const Patch *patch = nullptr) : patch(patch) {}
explicit SubPatch(const Patch *patch, const int face_index, const int corner = 0)
: patch(patch), face_index(face_index), corner(corner)
{
}
int calc_num_inner_verts() const
{
@ -154,10 +162,10 @@ class SubPatch {
const int Mv = max(edge_v0.edge->T, edge_v1.edge->T);
if (Mu == 1) {
return Mv * 2;
return edge_v0.edge->T + edge_v1.edge->T;
}
if (Mv == 1) {
return Mu * 2;
return edge_u0.edge->T + edge_u1.edge->T;
}
const int inner_triangles = (Mu - 2) * (Mv - 2) * 2;
@ -207,7 +215,7 @@ class SubPatch {
return -1;
}
float2 map_uv(float2 uv)
float2 map_uv(float2 uv) const
{
/* Map UV from subpatch to patch parametric coordinates. */
const float2 d0 = interp(uv00, uv01, uv.y);