There is a corner case where one side of a quad needs splitting and the other side has only one segment. Previously this would produce either gaps or after recent changes to stitch together geometry, uninitialized memory. Now solve this by splitting into triangular patches, as suggested in the DiagSplit paper. These triangular patches can be further subdivided themselves. Dicing has special cases for 1 or 2 segments on edges. For more segments it works the same as: quad dicing: A regular inner triangle grid stitched to the outer edges. Fix #136973: Inconsistent results with adaptive subdivision Pull Request: https://projects.blender.org/blender/blender/pulls/139062
696 lines
22 KiB
C++
696 lines
22 KiB
C++
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 */
|
|
|
|
#include "scene/camera.h"
|
|
#include "scene/mesh.h"
|
|
|
|
#include "subd/dice.h"
|
|
#include "subd/interpolation.h"
|
|
#include "subd/patch.h"
|
|
#include "subd/split.h"
|
|
|
|
#include "util/tbb.h"
|
|
|
|
CCL_NAMESPACE_BEGIN
|
|
|
|
EdgeDice::EdgeDice(const SubdParams ¶ms_,
|
|
const int num_verts,
|
|
const int num_triangles,
|
|
SubdAttributeInterpolation &interpolation)
|
|
: params(params_), interpolation(interpolation)
|
|
{
|
|
Mesh *mesh = params.mesh;
|
|
|
|
mesh->num_subd_added_verts = num_verts - mesh->get_verts().size();
|
|
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();
|
|
|
|
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();
|
|
mesh_ptex_uv = attr_ptex_uv->data_float2();
|
|
}
|
|
|
|
interpolation.setup();
|
|
}
|
|
|
|
float3 EdgeDice::eval_projected(const SubPatch &sub, const float2 uv)
|
|
{
|
|
float3 P;
|
|
|
|
sub.patch->eval(&P, nullptr, nullptr, nullptr, uv.x, uv.y);
|
|
if (params.camera) {
|
|
P = transform_perspective(¶ms.camera->worldtoraster, P);
|
|
}
|
|
|
|
return P;
|
|
}
|
|
|
|
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 EdgeDice::scale_factor(const SubPatch &sub, const int Mu, const int Mv)
|
|
{
|
|
/* estimate area as 4x largest of 4 quads */
|
|
float3 P[3][3];
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
for (int j = 0; j < 3; j++) {
|
|
P[i][j] = eval_projected(sub, sub.map_uv(make_float2(i * 0.5f, j * 0.5f)));
|
|
}
|
|
}
|
|
|
|
const float A1 = quad_area(P[0][0], P[1][0], P[0][1], P[1][1]);
|
|
const float A2 = quad_area(P[1][0], P[2][0], P[1][1], P[2][1]);
|
|
const float A3 = quad_area(P[0][1], P[1][1], P[0][2], P[1][2]);
|
|
const float A4 = quad_area(P[1][1], P[2][1], P[1][2], P[2][2]);
|
|
const float Apatch = max(A1, max(A2, max(A3, A4))) * 4.0f;
|
|
|
|
/* solve for scaling factor */
|
|
const float Atri = params.dicing_rate * params.dicing_rate * 0.5f;
|
|
const float Ntris = Apatch / Atri;
|
|
|
|
// XXX does the -sqrt solution matter
|
|
// XXX max(D, 0.0) is highly suspicious, need to test cases
|
|
// where D goes negative
|
|
const float N = 0.5f * (Ntris - (sub.edges[0].edge->T + sub.edges[2].edge->T +
|
|
sub.edges[3].edge->T + sub.edges[1].edge->T));
|
|
const float D = (4.0f * N * Mu * Mv) + ((Mu + Mv) * (Mu + Mv));
|
|
const float S = (Mu + Mv + sqrtf(max(D, 0.0f))) / (2 * Mu * Mv);
|
|
|
|
return S;
|
|
}
|
|
|
|
void EdgeDice::set_vertex(const SubPatch &sub, const int index, const float2 uv)
|
|
{
|
|
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;
|
|
|
|
for (const SubdAttribute &attr : interpolation.vertex_attributes) {
|
|
attr.interp(sub.patch->patch_index, sub.face_index, sub.corner, &index, &uv, 1);
|
|
}
|
|
}
|
|
|
|
void EdgeDice::set_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] = patch->smooth;
|
|
|
|
if (mesh_ptex_face_id) {
|
|
mesh_ptex_face_id[triangle_index] = patch->patch_index;
|
|
}
|
|
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)
|
|
{
|
|
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 = grid_vertex_offset + (i - 1) + (j - 1) * (Mu - 1);
|
|
|
|
set_vertex(sub, center_i, sub.map_uv(make_float2(u, v)));
|
|
|
|
if (i < Mu - 1 && j < Mv - 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));
|
|
|
|
set_triangle(sub, triangle_index++, i1, i2, i3, uv1, uv2, uv3);
|
|
set_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++;
|
|
}
|
|
}
|
|
|
|
set_triangle(sub, triangle_index++, v0, v1, v2, uv0, uv1, uv2);
|
|
}
|
|
}
|
|
}
|
|
|
|
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++;
|
|
}
|
|
}
|
|
|
|
set_triangle(sub, triangle_index++, v0, v1, v2, uv0, uv1, uv2);
|
|
}
|
|
}
|
|
|
|
void EdgeDice::quad_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::quad_dice(const SubPatch &sub)
|
|
{
|
|
/* Compute inner grid size with scale factor. */
|
|
const int Mu = max(sub.edges[0].edge->T, sub.edges[2].edge->T);
|
|
const int Mv = max(sub.edges[3].edge->T, sub.edges[1].edge->T);
|
|
|
|
if (Mv == 1) {
|
|
/* No inner grid, stitch triangles from side to side. */
|
|
add_triangle_strip(sub, 2, 0);
|
|
}
|
|
else if (Mu == 1) {
|
|
/* No inner grid, stitch triangles from side to side. */
|
|
add_triangle_strip(sub, 3, 1);
|
|
}
|
|
else {
|
|
#if 0
|
|
/* Doesn't work very well, especially at grazing angles. */
|
|
const float S = scale_factor(sub, ef, Mu, Mv);
|
|
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
|
|
}
|
|
}
|
|
|
|
void EdgeDice::tri_set_sides(const SubPatch &sub)
|
|
{
|
|
for (int edge = 0; edge < 3; 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, f);
|
|
break;
|
|
case 2:
|
|
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::tri_dice(const SubPatch &sub)
|
|
{
|
|
const int M = max(max(sub.edges[0].edge->T, sub.edges[1].edge->T), sub.edges[2].edge->T);
|
|
const float d = 1.0f / (float)(M + 1);
|
|
|
|
int triangle_index = sub.triangles_offset;
|
|
|
|
if (M == 1) {
|
|
/* Single triangle. */
|
|
set_triangle(sub,
|
|
triangle_index++,
|
|
sub.edges[0].start_vert_index(),
|
|
sub.edges[1].start_vert_index(),
|
|
sub.edges[2].start_vert_index(),
|
|
sub.map_uv(make_float2(0.0f, 0.0f)),
|
|
sub.map_uv(make_float2(1.0f, 0.0f)),
|
|
sub.map_uv(make_float2(0.0f, 1.0f)));
|
|
assert(triangle_index == sub.triangles_offset + sub.calc_num_triangles());
|
|
return;
|
|
}
|
|
if (M == 2) {
|
|
/* Edges have 2 segments or less. */
|
|
int num_split = 0;
|
|
int split_0 = -1;
|
|
for (int i = 0; i < 3; i++) {
|
|
if (sub.edges[i].edge->T == 2) {
|
|
num_split++;
|
|
if (split_0 == -1) {
|
|
split_0 = i;
|
|
}
|
|
}
|
|
}
|
|
/* When two edges have 2 segments, we assume split_0 is the first of two consecutive edges. */
|
|
if (split_0 == 0 && sub.edges[2].edge->T == 2) {
|
|
split_0 = 2;
|
|
}
|
|
|
|
const int split_1 = (split_0 + 1) % 3;
|
|
const int split_2 = (split_0 + 2) % 3;
|
|
|
|
const int v[3] = {sub.edges[0].start_vert_index(),
|
|
sub.edges[1].start_vert_index(),
|
|
sub.edges[2].start_vert_index()};
|
|
const int mid_v[3] = {sub.get_vert_along_edge(0, 1),
|
|
sub.get_vert_along_edge(1, 1),
|
|
sub.get_vert_along_edge(2, 1)};
|
|
const float2 uv[3] = {sub.map_uv(make_float2(0.0f, 0.0f)),
|
|
sub.map_uv(make_float2(1.0f, 0.0f)),
|
|
sub.map_uv(make_float2(0.0f, 1.0f))};
|
|
const float2 mid_uv[3] = {sub.map_uv(make_float2(0.5f, 0.0f)),
|
|
sub.map_uv(make_float2(0.5f, 0.5f)),
|
|
sub.map_uv(make_float2(0.0f, 0.5f))};
|
|
|
|
if (num_split == 3) {
|
|
/* All edges have two segments
|
|
* /\
|
|
* /--\
|
|
* / \/ \
|
|
* ------- */
|
|
set_triangle(sub, triangle_index++, v[0], mid_v[0], mid_v[2], uv[0], mid_uv[0], mid_uv[2]);
|
|
set_triangle(sub, triangle_index++, v[1], mid_v[1], mid_v[0], uv[1], mid_uv[1], mid_uv[0]);
|
|
set_triangle(sub, triangle_index++, v[2], mid_v[2], mid_v[1], uv[2], mid_uv[2], mid_uv[1]);
|
|
set_triangle(
|
|
sub, triangle_index++, mid_v[0], mid_v[1], mid_v[2], mid_uv[0], mid_uv[1], mid_uv[2]);
|
|
}
|
|
else {
|
|
/* One edge has two segments.
|
|
* / \
|
|
* / | \
|
|
* / | \
|
|
* ------- */
|
|
set_triangle(sub,
|
|
triangle_index++,
|
|
v[split_0],
|
|
mid_v[split_0],
|
|
v[split_2],
|
|
uv[split_0],
|
|
mid_uv[split_0],
|
|
uv[split_2]);
|
|
if (num_split == 1) {
|
|
set_triangle(sub,
|
|
triangle_index++,
|
|
mid_v[split_0],
|
|
v[split_1],
|
|
v[split_2],
|
|
mid_uv[split_0],
|
|
uv[split_1],
|
|
uv[split_2]);
|
|
}
|
|
else {
|
|
/* Two edges have two segments.
|
|
* /|\
|
|
* / | \
|
|
* / |/ \
|
|
* ------- */
|
|
set_triangle(sub,
|
|
triangle_index++,
|
|
mid_v[split_0],
|
|
v[split_1],
|
|
mid_v[split_1],
|
|
mid_uv[split_0],
|
|
uv[split_1],
|
|
mid_uv[split_1]);
|
|
set_triangle(sub,
|
|
triangle_index++,
|
|
mid_v[split_0],
|
|
mid_v[split_1],
|
|
v[split_2],
|
|
mid_uv[split_0],
|
|
mid_uv[split_1],
|
|
uv[split_2]);
|
|
}
|
|
}
|
|
assert(triangle_index == sub.triangles_offset + sub.calc_num_triangles());
|
|
return;
|
|
}
|
|
|
|
const int inner_M = M - 2;
|
|
|
|
for (int j = 0; j < inner_M; j++) {
|
|
for (int i = 0; i < j + 1; i++) {
|
|
const int i_next = i + 1;
|
|
const int j_next = j + 1;
|
|
|
|
const float2 inner_uv = make_float2(d, d);
|
|
|
|
const int v0 = sub.get_inner_grid_vert_triangle(i, j);
|
|
const int v1 = sub.get_inner_grid_vert_triangle(i_next, j_next);
|
|
const int v2 = sub.get_inner_grid_vert_triangle(i, j_next);
|
|
|
|
const float2 uv0 = sub.map_uv(inner_uv + make_float2(i, j - i) * d);
|
|
const float2 uv1 = sub.map_uv(inner_uv + make_float2(i_next, j - i) * d);
|
|
const float2 uv2 = sub.map_uv(inner_uv + make_float2(i, j_next - i) * d);
|
|
|
|
set_vertex(sub, v0, uv0);
|
|
if (j == inner_M - 1) {
|
|
set_vertex(sub, v1, uv1);
|
|
set_vertex(sub, v2, uv2);
|
|
}
|
|
|
|
set_triangle(sub, triangle_index++, v0, v1, v2, uv0, uv1, uv2);
|
|
|
|
if (i < j) {
|
|
const int v3 = sub.get_inner_grid_vert_triangle(i_next, j);
|
|
const float2 uv3 = sub.map_uv(inner_uv + make_float2(i_next, j - i_next) * d);
|
|
|
|
set_vertex(sub, v3, uv3);
|
|
|
|
set_triangle(sub, triangle_index++, v0, v3, v1, uv0, uv3, uv1);
|
|
}
|
|
}
|
|
}
|
|
|
|
assert(triangle_index == sub.triangles_offset + inner_M * inner_M);
|
|
|
|
/* Stitch inner grid to edges. */
|
|
for (int edge = 0; edge < 3; edge++) {
|
|
const int outer_T = sub.edges[edge].edge->T;
|
|
const int inner_T = inner_M;
|
|
|
|
float2 inner_uv, outer_uv, inner_uv_step, outer_uv_step;
|
|
switch (edge) {
|
|
case 0:
|
|
inner_uv = make_float2(d, d);
|
|
outer_uv = make_float2(0.0f, 0.0f);
|
|
inner_uv_step = make_float2(d, 0.0f);
|
|
outer_uv_step = make_float2(1.0f / (float)outer_T, 0.0f);
|
|
break;
|
|
case 1:
|
|
inner_uv = make_float2(1.0f - 2.0f * d, d);
|
|
outer_uv = make_float2(1.0f, 0.0f);
|
|
inner_uv_step = make_float2(-d, d);
|
|
outer_uv_step = make_float2(-1.0f / (float)outer_T, 1.0f / (float)outer_T);
|
|
break;
|
|
case 2:
|
|
default:
|
|
inner_uv = make_float2(d, 1.0f - 2.0f * d);
|
|
outer_uv = make_float2(0.0f, 1.0f);
|
|
inner_uv_step = make_float2(0.0f, -d);
|
|
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++;
|
|
}
|
|
}
|
|
|
|
set_triangle(sub, triangle_index++, v0, v1, v2, uv0, uv1, uv2);
|
|
}
|
|
}
|
|
|
|
assert(triangle_index == sub.triangles_offset + sub.calc_num_triangles());
|
|
}
|
|
|
|
void EdgeDice::dice(const DiagSplit &split)
|
|
{
|
|
const size_t num_subpatches = split.get_num_subpatches();
|
|
|
|
/* 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++) {
|
|
const SubPatch &subpatch = split.get_subpatch(i);
|
|
if (subpatch.shape == SubPatch::TRIANGLE) {
|
|
tri_set_sides(subpatch);
|
|
}
|
|
else {
|
|
quad_set_sides(subpatch);
|
|
}
|
|
}
|
|
});
|
|
|
|
/* 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++) {
|
|
const SubPatch &subpatch = split.get_subpatch(i);
|
|
if (subpatch.shape == SubPatch::TRIANGLE) {
|
|
tri_dice(subpatch);
|
|
}
|
|
else {
|
|
quad_dice(subpatch);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
CCL_NAMESPACE_END
|