Add Chromium-only Blender WebEngine parity work
This commit is contained in:
39
blender-5.2.0/intern/cycles/subd/CMakeLists.txt
Normal file
39
blender-5.2.0/intern/cycles/subd/CMakeLists.txt
Normal file
@@ -0,0 +1,39 @@
|
||||
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
set(INC
|
||||
..
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
|
||||
)
|
||||
|
||||
set(SRC
|
||||
dice.cpp
|
||||
interpolation.cpp
|
||||
osd.cpp
|
||||
patch.cpp
|
||||
split.cpp
|
||||
)
|
||||
|
||||
set(SRC_HEADERS
|
||||
dice.h
|
||||
interpolation.h
|
||||
osd.h
|
||||
patch.h
|
||||
split.h
|
||||
subpatch.h
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PUBLIC cycles_scene
|
||||
PRIVATE bf::dependencies::openimageio
|
||||
PUBLIC bf::dependencies::optional::opensubdiv
|
||||
)
|
||||
|
||||
include_directories(${INC})
|
||||
include_directories(SYSTEM ${INC_SYS})
|
||||
|
||||
cycles_add_library(cycles_subd "${LIB}" ${SRC} ${SRC_HEADERS})
|
||||
698
blender-5.2.0/intern/cycles/subd/dice.cpp
Normal file
698
blender-5.2.0/intern/cycles/subd/dice.cpp
Normal file
@@ -0,0 +1,698 @@
|
||||
/* 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->num_verts();
|
||||
mesh->resize_mesh(num_verts, num_triangles);
|
||||
|
||||
mesh->attributes.add(ATTR_STD_VERTEX_NORMAL);
|
||||
|
||||
interpolation.setup();
|
||||
|
||||
/* Get pointers after interpolation.setup() since it may reallocate
|
||||
* attribute buffers when setting motion steps. */
|
||||
Attribute *attr_vN = mesh->attributes.find(ATTR_STD_VERTEX_NORMAL);
|
||||
mesh_triangles = mesh->triangles.data();
|
||||
mesh_shader = mesh->shader.data();
|
||||
mesh_smooth = mesh->smooth.data();
|
||||
mesh_P = mesh->get_position_for_write();
|
||||
mesh_N = attr_vN->data_for_write<packed_normal>();
|
||||
|
||||
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_for_write<float>();
|
||||
mesh_ptex_uv = attr_ptex_uv->data_for_write<float2>();
|
||||
}
|
||||
}
|
||||
|
||||
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->num_verts());
|
||||
|
||||
float3 P;
|
||||
float3 N;
|
||||
|
||||
sub.patch->eval(&P, nullptr, nullptr, &N, uv.x, uv.y);
|
||||
|
||||
mesh_P[index] = P;
|
||||
mesh_N[index] = packed_normal(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
|
||||
84
blender-5.2.0/intern/cycles/subd/dice.h
Normal file
84
blender-5.2.0/intern/cycles/subd/dice.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
/* DX11 like EdgeDice implementation, with different tessellation factors for
|
||||
* each edge for watertight tessellation, with subpatch remapping to work with
|
||||
* DiagSplit. For more algorithm details, see the DiagSplit paper or the
|
||||
* ARB_tessellation_shader OpenGL extension, Section 2.X.2. */
|
||||
|
||||
#include "util/transform.h"
|
||||
#include "util/types.h"
|
||||
|
||||
#include "subd/subpatch.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class Camera;
|
||||
class Mesh;
|
||||
class Patch;
|
||||
class SubdAttributeInterpolation;
|
||||
class DiagSplit;
|
||||
|
||||
struct SubdParams {
|
||||
Mesh *mesh = nullptr;
|
||||
bool ptex = false;
|
||||
|
||||
int test_steps = 3;
|
||||
int split_threshold = 1;
|
||||
float dicing_rate = 1.0f;
|
||||
int max_level = 12;
|
||||
Camera *camera = nullptr;
|
||||
Transform objecttoworld = transform_identity();
|
||||
|
||||
SubdParams(Mesh *mesh_, bool ptex_ = false) : mesh(mesh_), ptex(ptex_) {}
|
||||
};
|
||||
|
||||
class EdgeDice {
|
||||
public:
|
||||
SubdParams params;
|
||||
SubdAttributeInterpolation &interpolation;
|
||||
int *mesh_triangles = nullptr;
|
||||
int *mesh_shader = nullptr;
|
||||
bool *mesh_smooth = nullptr;
|
||||
packed_float3 *mesh_P = nullptr;
|
||||
packed_normal *mesh_N = nullptr;
|
||||
float *mesh_ptex_face_id = nullptr;
|
||||
float2 *mesh_ptex_uv = nullptr;
|
||||
|
||||
explicit EdgeDice(const SubdParams ¶ms,
|
||||
const int num_verts,
|
||||
const int num_triangles,
|
||||
SubdAttributeInterpolation &interpolation);
|
||||
|
||||
void dice(const DiagSplit &split);
|
||||
|
||||
protected:
|
||||
void tri_dice(const SubPatch &sub);
|
||||
void quad_dice(const SubPatch &sub);
|
||||
|
||||
void set_vertex(const SubPatch &sub, const int index, const float2 uv);
|
||||
void 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);
|
||||
|
||||
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);
|
||||
|
||||
float3 eval_projected(const SubPatch &sub, const float2 uv);
|
||||
|
||||
void tri_set_sides(const SubPatch &sub);
|
||||
void quad_set_sides(const SubPatch &sub);
|
||||
|
||||
float quad_area(const float3 &a, const float3 &b, const float3 &c, const float3 &d);
|
||||
float scale_factor(const SubPatch &sub, const int Mu, const int Mv);
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
637
blender-5.2.0/intern/cycles/subd/interpolation.cpp
Normal file
637
blender-5.2.0/intern/cycles/subd/interpolation.cpp
Normal file
@@ -0,0 +1,637 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2024 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "subd/interpolation.h"
|
||||
|
||||
#include "kernel/types.h"
|
||||
#include "scene/attribute.h"
|
||||
#include "scene/mesh.h"
|
||||
|
||||
#include "util/color.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Classes for interpolation to use float math for byte value, for precision. */
|
||||
template<typename T> struct SubdFloat {
|
||||
using Type = T;
|
||||
using AccumType = T;
|
||||
|
||||
static Type read(const Type &value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
static Type output(const Type &value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
struct SubdByte {
|
||||
using Type = uchar4;
|
||||
using AccumType = float4;
|
||||
|
||||
static AccumType read(const Type &value)
|
||||
{
|
||||
return color_uchar4_to_float4(value);
|
||||
}
|
||||
|
||||
static Type output(const AccumType &value)
|
||||
{
|
||||
return color_float4_to_uchar4(value);
|
||||
}
|
||||
};
|
||||
|
||||
struct SubdNormal {
|
||||
using Type = packed_normal;
|
||||
using AccumType = float3;
|
||||
|
||||
static AccumType read(const Type &value)
|
||||
{
|
||||
return value.decode();
|
||||
}
|
||||
|
||||
static Type output(const AccumType &value)
|
||||
{
|
||||
return packed_normal(value);
|
||||
}
|
||||
};
|
||||
|
||||
struct SubdPackedFloat3 {
|
||||
using Type = packed_float3;
|
||||
using AccumType = float3;
|
||||
|
||||
static AccumType read(const Type &value)
|
||||
{
|
||||
return float3(value);
|
||||
}
|
||||
|
||||
static Type output(const AccumType &value)
|
||||
{
|
||||
return packed_float3(value);
|
||||
}
|
||||
};
|
||||
|
||||
#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
|
||||
{
|
||||
switch (attr.std) {
|
||||
/* Smooth normals are computed from derivatives, for linear interpolate. */
|
||||
case ATTR_STD_VERTEX_NORMAL:
|
||||
case ATTR_STD_CORNER_NORMAL:
|
||||
if (mesh.get_subdivision_type() == Mesh::SUBDIVISION_CATMULL_CLARK) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
/* Center step position is computed by patch evaluation during dicing.
|
||||
* Only interpolate position when there are motion steps. */
|
||||
case ATTR_STD_POSITION:
|
||||
if (!attr.has_motion()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
/* PTex coordinates will be computed by subdivision. */
|
||||
case ATTR_STD_PTEX_FACE_ID:
|
||||
case ATTR_STD_PTEX_UV:
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Skip element types that should not exist for subd attributes anyway. */
|
||||
switch (attr.element) {
|
||||
case ATTR_ELEMENT_OBJECT:
|
||||
case ATTR_ELEMENT_MESH:
|
||||
case ATTR_ELEMENT_VERTEX:
|
||||
case ATTR_ELEMENT_VERTEX_NORMAL:
|
||||
case ATTR_ELEMENT_CORNER:
|
||||
case ATTR_ELEMENT_CORNER_BYTE:
|
||||
case ATTR_ELEMENT_CORNER_NORMAL:
|
||||
case ATTR_ELEMENT_FACE:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SubdAttributeInterpolation::setup_attribute(const Attribute &subd_attr, Attribute &mesh_attr)
|
||||
{
|
||||
if (subd_attr.element & ATTR_ELEMENT_IS_BYTE) {
|
||||
setup_attribute_type<SubdByte>(subd_attr, mesh_attr);
|
||||
}
|
||||
else if (subd_attr.element & ATTR_ELEMENT_IS_NORMAL) {
|
||||
setup_attribute_type<SubdNormal>(subd_attr, mesh_attr);
|
||||
}
|
||||
else if (Attribute::same_storage(subd_attr.type, TypeFloat)) {
|
||||
setup_attribute_type<SubdFloat<float>>(subd_attr, mesh_attr);
|
||||
}
|
||||
else if (Attribute::same_storage(subd_attr.type, TypeFloat2)) {
|
||||
setup_attribute_type<SubdFloat<float2>>(subd_attr, mesh_attr);
|
||||
}
|
||||
else if (Attribute::same_storage(subd_attr.type, TypeVector)) {
|
||||
setup_attribute_type<SubdPackedFloat3>(subd_attr, mesh_attr);
|
||||
}
|
||||
else if (Attribute::same_storage(subd_attr.type, TypeFloat4) ||
|
||||
Attribute::same_storage(subd_attr.type, TypeRGBA))
|
||||
{
|
||||
setup_attribute_type<SubdFloat<float4>>(subd_attr, mesh_attr);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SubdAttributeInterpolation::setup_attribute_vertex_linear(const Attribute &subd_attr,
|
||||
Attribute &mesh_attr,
|
||||
const int motion_step)
|
||||
{
|
||||
SubdAttribute attr;
|
||||
|
||||
/* motion_step -1 selects the center step, other select motion steps. */
|
||||
const int attr_step = motion_step + 1;
|
||||
assert(attr_step == 0 || subd_attr.has_motion());
|
||||
const typename T::Type *subd_data = subd_attr.data<typename T::Type>(attr_step);
|
||||
typename T::Type *mesh_data = mesh_attr.data_for_write<typename T::Type>(attr_step);
|
||||
|
||||
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. */
|
||||
const typename T::AccumType value0 = T::read(
|
||||
subd_data[subd_face_corners[face.start_corner + 0]]);
|
||||
const typename T::AccumType value1 = T::read(
|
||||
subd_data[subd_face_corners[face.start_corner + 1]]);
|
||||
const typename T::AccumType value2 = T::read(
|
||||
subd_data[subd_face_corners[face.start_corner + 2]]);
|
||||
const typename T::AccumType value3 = T::read(
|
||||
subd_data[subd_face_corners[face.start_corner + 3]]);
|
||||
|
||||
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[vert_index[i]] = T::output(value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Other n-gons are split into n quads. */
|
||||
|
||||
/* Compute value at center of polygon. */
|
||||
typename T::AccumType value_center = T::read(
|
||||
subd_data[subd_face_corners[face.start_corner]]);
|
||||
for (int j = 1; j < face.num_corners; j++) {
|
||||
value_center += T::read(subd_data[subd_face_corners[face.start_corner + j]]);
|
||||
}
|
||||
value_center /= (float)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[vert_index[i]] = T::output(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
vertex_attributes.push_back(std::move(attr));
|
||||
}
|
||||
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
template<typename T>
|
||||
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();
|
||||
const int num_local_points = osd_data.patch_table->GetNumLocalPoints();
|
||||
const int num_base_verts = mesh.get_num_subd_base_verts();
|
||||
|
||||
/* Refine attribute data to get patch coordinates. */
|
||||
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 int attr_step = motion_step + 1;
|
||||
assert(attr_step == 0 || subd_attr.has_motion());
|
||||
const typename T::Type *base_src = subd_attr.data<typename T::Type>(attr_step);
|
||||
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 = 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(
|
||||
i + 1, (OsdValue<typename T::AccumType> *)src, (OsdValue<typename T::AccumType> *&)dest);
|
||||
src = dest;
|
||||
}
|
||||
|
||||
if (num_local_points) {
|
||||
osd_data.patch_table->ComputeLocalPointValues(
|
||||
(OsdValue<typename T::AccumType> *)subd_data,
|
||||
(OsdValue<typename T::AccumType> *)(subd_data + num_refiner_verts));
|
||||
}
|
||||
|
||||
/* Evaluate patches at limit. */
|
||||
assert(attr_step == 0 || mesh_attr.has_motion());
|
||||
typename T::Type *mesh_data = mesh_attr.data_for_write<typename T::Type>(attr_step);
|
||||
assert(mesh_data != nullptr);
|
||||
|
||||
/* Compute motion normals alongside positions. */
|
||||
packed_normal *mesh_normal_data = nullptr;
|
||||
if constexpr (std::is_same_v<typename T::AccumType, float3>) {
|
||||
if (motion_step >= 0 && mesh_attr.std == ATTR_STD_POSITION && mesh_attr.has_motion()) {
|
||||
Attribute *attr_normal = mesh.attributes.find(ATTR_STD_VERTEX_NORMAL);
|
||||
if (attr_normal) {
|
||||
attr_normal->add_motion(&mesh);
|
||||
mesh_normal_data = attr_normal->data_for_write<packed_normal>(attr_step);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 = vert_uv[i];
|
||||
|
||||
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->EvaluateBasis(handle, uv.x, uv.y, p_weights, du_weights, dv_weights);
|
||||
Far::ConstIndexArray cv = osd_data.patch_table->GetPatchVertices(handle);
|
||||
|
||||
/* 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[vert_index[i]] = T::output(value);
|
||||
|
||||
/* Optionally compute normal. */
|
||||
if (mesh_normal_data) {
|
||||
if constexpr (std::is_same_v<typename T::AccumType, float3>) {
|
||||
float3 du = zero_float3();
|
||||
float3 dv = zero_float3();
|
||||
for (int k = 0; k < cv.size(); k++) {
|
||||
const float3 p = subd_data[cv[k]];
|
||||
du += p * du_weights[k];
|
||||
dv += p * dv_weights[k];
|
||||
}
|
||||
mesh_normal_data[vert_index[i]] = packed_normal(
|
||||
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::setup_attribute_corner_linear(const Attribute &subd_attr,
|
||||
Attribute &mesh_attr,
|
||||
const int motion_step)
|
||||
{
|
||||
SubdAttribute attr;
|
||||
|
||||
/* Interpolate values at corners. */
|
||||
const int attr_step = motion_step + 1;
|
||||
assert(attr_step == 0 || subd_attr.has_motion());
|
||||
const typename T::Type *subd_data = subd_attr.data<typename T::Type>(attr_step);
|
||||
typename T::Type *mesh_data = mesh_attr.data_for_write<typename T::Type>(attr_step);
|
||||
|
||||
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. */
|
||||
const typename T::AccumType value0 = T::read(subd_data[face.start_corner + 0]);
|
||||
const typename T::AccumType value1 = T::read(subd_data[face.start_corner + 1]);
|
||||
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 (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. */
|
||||
|
||||
/* Compute value at center of polygon. */
|
||||
typename T::AccumType value_center = T::read(subd_data[face.start_corner]);
|
||||
for (int j = 1; j < face.num_corners; j++) {
|
||||
value_center += T::read(subd_data[face.start_corner + j]);
|
||||
}
|
||||
value_center /= (float)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);
|
||||
|
||||
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::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. */
|
||||
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_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_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,
|
||||
(OsdValue<typename T::AccumType> *)src,
|
||||
(OsdValue<typename T::AccumType> *&)dest,
|
||||
channel);
|
||||
src = dest;
|
||||
}
|
||||
|
||||
if (num_local_points) {
|
||||
osd_data.patch_table->ComputeLocalPointValuesFaceVarying(
|
||||
(OsdValue<typename T::AccumType> *)refined_data,
|
||||
(OsdValue<typename T::AccumType> *)(refined_data + num_refiner_fvars),
|
||||
channel);
|
||||
}
|
||||
|
||||
/* Evaluate patches at limit. */
|
||||
const typename T::AccumType *subd_data = refined_data;
|
||||
typename T::Type *mesh_data = reinterpret_cast<typename T::Type *>(mesh_attr.data_for_write());
|
||||
|
||||
assert(mesh_data != nullptr);
|
||||
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
triangle_attributes.push_back(std::move(attr));
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
void SubdAttributeInterpolation::setup_attribute_face(const Attribute &subd_attr,
|
||||
Attribute &mesh_attr)
|
||||
{
|
||||
/* 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_write());
|
||||
|
||||
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::setup_attribute_type(const Attribute &subd_attr,
|
||||
Attribute &mesh_attr)
|
||||
{
|
||||
switch (subd_attr.element) {
|
||||
case ATTR_ELEMENT_OBJECT:
|
||||
case ATTR_ELEMENT_MESH: {
|
||||
/* Uniform attributes don't need interpolation, just copy data. */
|
||||
assert(mesh_attr.size == subd_attr.size);
|
||||
memcpy(mesh_attr.data_for_write(), subd_attr.data(), subd_attr.data_sizeof());
|
||||
break;
|
||||
}
|
||||
case ATTR_ELEMENT_VERTEX:
|
||||
case ATTR_ELEMENT_VERTEX_NORMAL: {
|
||||
/* Center step. Position center is computed by patch evaluation
|
||||
* during dicing, so skip it here. */
|
||||
if (subd_attr.std != ATTR_STD_POSITION) {
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
if (mesh.get_subdivision_type() == Mesh::SUBDIVISION_CATMULL_CLARK) {
|
||||
/* Only smoothly interpolation known position-like attributes. */
|
||||
switch (subd_attr.std) {
|
||||
case ATTR_STD_GENERATED:
|
||||
case ATTR_STD_POSITION_UNDEFORMED:
|
||||
case ATTR_STD_POSITION_UNDISPLACED:
|
||||
setup_attribute_vertex_smooth<T>(subd_attr, mesh_attr);
|
||||
break;
|
||||
default:
|
||||
setup_attribute_vertex_linear<T>(subd_attr, mesh_attr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
setup_attribute_vertex_linear<T>(subd_attr, mesh_attr);
|
||||
}
|
||||
}
|
||||
/* Motion steps. */
|
||||
if (subd_attr.has_motion()) {
|
||||
for (int step = 0; step < subd_attr.motion.size(); step++) {
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
if (mesh.get_subdivision_type() == Mesh::SUBDIVISION_CATMULL_CLARK) {
|
||||
setup_attribute_vertex_smooth<T>(subd_attr, mesh_attr, step);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
setup_attribute_vertex_linear<T>(subd_attr, mesh_attr, step);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ATTR_ELEMENT_CORNER:
|
||||
case ATTR_ELEMENT_CORNER_BYTE:
|
||||
case ATTR_ELEMENT_CORNER_NORMAL: {
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
if (osd_mesh.use_smooth_fvar(subd_attr)) {
|
||||
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>) {
|
||||
setup_attribute_corner_smooth<T>(mesh_attr, merged_fvar.channel, merged_fvar.values);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
setup_attribute_corner_linear<T>(subd_attr, mesh_attr);
|
||||
/* Motion steps. */
|
||||
if (subd_attr.has_motion()) {
|
||||
for (int step = 0; step < subd_attr.motion.size(); step++) {
|
||||
setup_attribute_corner_linear<T>(subd_attr, mesh_attr, step);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ATTR_ELEMENT_FACE: {
|
||||
setup_attribute_face<T>(subd_attr, mesh_attr);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
80
blender-5.2.0/intern/cycles/subd/interpolation.h
Normal file
80
blender-5.2.0/intern/cycles/subd/interpolation.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2024 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "subd/osd.h"
|
||||
|
||||
#include "util/types.h"
|
||||
#include "util/vector.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class Mesh;
|
||||
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;
|
||||
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
OsdMesh &osd_mesh;
|
||||
OsdData &osd_data;
|
||||
#endif
|
||||
|
||||
public:
|
||||
vector<SubdAttribute> vertex_attributes;
|
||||
vector<SubdAttribute> triangle_attributes;
|
||||
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
SubdAttributeInterpolation(Mesh &mesh, OsdMesh &osd_mesh, OsdData &osd_data);
|
||||
#else
|
||||
SubdAttributeInterpolation(Mesh &mesh);
|
||||
#endif
|
||||
|
||||
void setup();
|
||||
|
||||
protected:
|
||||
bool support_interp_attribute(const Attribute &attr) const;
|
||||
void setup_attribute(const Attribute &subd_attr, Attribute &mesh_attr);
|
||||
|
||||
template<typename T>
|
||||
void setup_attribute_vertex_linear(const Attribute &subd_attr,
|
||||
Attribute &mesh_attr,
|
||||
const int motion_step = -1);
|
||||
|
||||
template<typename T>
|
||||
void setup_attribute_corner_linear(const Attribute &subd_attr,
|
||||
Attribute &mesh_attr,
|
||||
const int motion_step = -1);
|
||||
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
template<typename T>
|
||||
void setup_attribute_vertex_smooth(const Attribute &subd_attr,
|
||||
Attribute &mesh_attr,
|
||||
const int motion_step = -1);
|
||||
|
||||
template<typename T>
|
||||
void setup_attribute_corner_smooth(Attribute &mesh_attr,
|
||||
const int channel,
|
||||
const vector<char> &merged_values);
|
||||
#endif
|
||||
|
||||
template<typename T> void setup_attribute_face(const Attribute &subd_attr, Attribute &mesh_attr);
|
||||
|
||||
template<typename T> void setup_attribute_type(const Attribute &subd_attr, Attribute &mesh_attr);
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
406
blender-5.2.0/intern/cycles/subd/osd.cpp
Normal file
406
blender-5.2.0/intern/cycles/subd/osd.cpp
Normal file
@@ -0,0 +1,406 @@
|
||||
/* 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/mesh.h"
|
||||
|
||||
# include "util/log.h"
|
||||
|
||||
/* Specialization of TopologyRefinerFactory for OsdMesh */
|
||||
|
||||
namespace OpenSubdiv::OPENSUBDIV_VERSION::Far {
|
||||
|
||||
using namespace ccl;
|
||||
|
||||
template<>
|
||||
bool TopologyRefinerFactory<OsdMesh>::resizeComponentTopology(TopologyRefiner &refiner,
|
||||
OsdMesh const &osd_mesh)
|
||||
{
|
||||
const Mesh &mesh = osd_mesh.mesh;
|
||||
|
||||
const int num_base_verts = mesh.get_num_subd_base_verts();
|
||||
const int num_base_faces = mesh.get_num_subd_faces();
|
||||
const int *subd_num_corners = mesh.get_subd_num_corners().data();
|
||||
|
||||
setNumBaseVertices(refiner, num_base_verts);
|
||||
setNumBaseFaces(refiner, num_base_faces);
|
||||
|
||||
for (int i = 0; i < num_base_faces; i++) {
|
||||
setNumBaseFaceVertices(refiner, i, subd_num_corners[i]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
bool TopologyRefinerFactory<OsdMesh>::assignComponentTopology(TopologyRefiner &refiner,
|
||||
OsdMesh const &osd_mesh)
|
||||
{
|
||||
const Mesh &mesh = osd_mesh.mesh;
|
||||
|
||||
const int num_base_faces = mesh.get_num_subd_faces();
|
||||
|
||||
const int *subd_face_corners = mesh.get_subd_face_corners().data();
|
||||
const int *subd_start_corner = mesh.get_subd_start_corner().data();
|
||||
const int *subd_num_corners = mesh.get_subd_num_corners().data();
|
||||
|
||||
for (int i = 0; i < num_base_faces; i++) {
|
||||
IndexArray face_verts = getBaseFaceVertices(refiner, i);
|
||||
|
||||
const int start_corner = subd_start_corner[i];
|
||||
const 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<OsdMesh>::assignComponentTags(TopologyRefiner &refiner,
|
||||
OsdMesh const &osd_mesh)
|
||||
{
|
||||
const Mesh &mesh = osd_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 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;
|
||||
}
|
||||
|
||||
const int num_base_verts = mesh.get_num_subd_base_verts();
|
||||
|
||||
for (int i = 0; i < num_base_verts; 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 += min(sharpness0, sharpness1);
|
||||
sharpness = min(sharpness, CREASE_SCALE);
|
||||
}
|
||||
|
||||
if (sharpness != 0.0f) {
|
||||
setBaseVertexSharpness(refiner, i, sharpness);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void merge_smooth_fvar(const Mesh &mesh,
|
||||
const Attribute &subd_attr,
|
||||
OsdMesh::MergedFVar &merged_fvar,
|
||||
vector<int> &merged_next,
|
||||
vector<int> &merged_face_corners)
|
||||
{
|
||||
const int num_base_verts = mesh.get_num_subd_base_verts();
|
||||
const int num_base_faces = mesh.get_num_subd_faces();
|
||||
const int *subd_face_corners = mesh.get_subd_face_corners().data();
|
||||
|
||||
const T *values = reinterpret_cast<const T *>(subd_attr.data());
|
||||
|
||||
merged_fvar.values.resize(num_base_verts * sizeof(T));
|
||||
|
||||
// Merge identical corner values with the same vertex. The first value is stored at the vertex
|
||||
// index, and any different values are pushed backed onto the array. merged_next creates a
|
||||
// linked list between all values for the same vertex.
|
||||
const int state_uninitialized = 0;
|
||||
const int state_end = -1;
|
||||
merged_next.resize(num_base_verts, state_uninitialized);
|
||||
|
||||
for (int f = 0, i = 0; f < num_base_faces; f++) {
|
||||
Mesh::SubdFace face = mesh.get_subd_face(f);
|
||||
|
||||
for (int corner = 0; corner < face.num_corners; corner++) {
|
||||
int v = subd_face_corners[face.start_corner + corner];
|
||||
const T value = values[i++];
|
||||
|
||||
if (merged_next[v] == state_uninitialized) {
|
||||
// First corner to initialize vertex.
|
||||
reinterpret_cast<T *>(merged_fvar.values.data())[v] = value;
|
||||
merged_next[v] = state_end;
|
||||
merged_face_corners.push_back(v);
|
||||
}
|
||||
else {
|
||||
// Find vertex with matching value, following linked list per vertex.
|
||||
int v_prev = v;
|
||||
for (; v != state_end; v_prev = v, v = merged_next[v]) {
|
||||
if (reinterpret_cast<T *>(merged_fvar.values.data())[v] == value) {
|
||||
// Matching value found, reuse merged vertex.
|
||||
merged_face_corners.push_back(v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (v == state_end) {
|
||||
// Non-matching value, add new merged vertex and add to linked list.
|
||||
const int next = merged_next.size();
|
||||
merged_fvar.values.resize((next + 1) * sizeof(T));
|
||||
reinterpret_cast<T *>(merged_fvar.values.data())[next] = value;
|
||||
merged_next.push_back(state_end);
|
||||
merged_next[v_prev] = next;
|
||||
merged_face_corners.push_back(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
bool TopologyRefinerFactory<OsdMesh>::assignFaceVaryingTopology(TopologyRefiner &refiner,
|
||||
OsdMesh const &osd_mesh)
|
||||
{
|
||||
const Mesh &mesh = osd_mesh.mesh;
|
||||
auto &merged_fvars = const_cast<OsdMesh &>(osd_mesh).merged_fvars;
|
||||
|
||||
for (const Attribute &subd_attr : mesh.subd_attributes.attributes) {
|
||||
if (!osd_mesh.use_smooth_fvar(subd_attr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Created merged FVar, for use in subdivide_attribute_corner_smooth.
|
||||
OsdMesh::MergedFVar merged_fvar{subd_attr};
|
||||
|
||||
vector<int> merged_next;
|
||||
vector<int> merged_face_corners;
|
||||
|
||||
if (subd_attr.element == ATTR_ELEMENT_CORNER_BYTE) {
|
||||
merge_smooth_fvar<uchar4>(mesh, subd_attr, merged_fvar, merged_next, merged_face_corners);
|
||||
}
|
||||
else if (Attribute::same_storage(subd_attr.type, TypeFloat)) {
|
||||
merge_smooth_fvar<float>(mesh, subd_attr, merged_fvar, merged_next, merged_face_corners);
|
||||
}
|
||||
else if (Attribute::same_storage(subd_attr.type, TypeFloat2)) {
|
||||
merge_smooth_fvar<float2>(mesh, subd_attr, merged_fvar, merged_next, merged_face_corners);
|
||||
}
|
||||
else if (Attribute::same_storage(subd_attr.type, TypeVector)) {
|
||||
merge_smooth_fvar<float3>(mesh, subd_attr, merged_fvar, merged_next, merged_face_corners);
|
||||
}
|
||||
else if (Attribute::same_storage(subd_attr.type, TypeFloat4)) {
|
||||
merge_smooth_fvar<float4>(mesh, subd_attr, merged_fvar, merged_next, merged_face_corners);
|
||||
}
|
||||
|
||||
// Create FVar channel and topology for OpenUSD.
|
||||
merged_fvar.channel = createBaseFVarChannel(refiner, merged_next.size());
|
||||
|
||||
const int num_base_faces = mesh.get_num_subd_faces();
|
||||
for (int f = 0, i = 0; f < num_base_faces; f++) {
|
||||
Far::IndexArray dst_face_uvs = getBaseFaceFVarValues(refiner, f, merged_fvar.channel);
|
||||
const int num_corners = dst_face_uvs.size();
|
||||
for (int corner = 0; corner < num_corners; corner++) {
|
||||
dst_face_uvs[corner] = merged_face_corners[i++];
|
||||
}
|
||||
}
|
||||
|
||||
merged_fvars.push_back(std::move(merged_fvar));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<>
|
||||
void TopologyRefinerFactory<OsdMesh>::reportInvalidTopology(TopologyError /*err_code*/,
|
||||
char const *msg,
|
||||
OsdMesh const &osd_mesh)
|
||||
{
|
||||
const Mesh &mesh = osd_mesh.mesh;
|
||||
LOG_WARNING << "Invalid subdivision topology for '" << mesh.name.c_str() << "': " << msg;
|
||||
}
|
||||
} // namespace OpenSubdiv::OPENSUBDIV_VERSION::Far
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* OsdMesh */
|
||||
|
||||
Sdc::Options OsdMesh::sdc_options()
|
||||
{
|
||||
Sdc::Options options;
|
||||
switch (mesh.get_subdivision_fvar_interpolation()) {
|
||||
case Mesh::SUBDIVISION_FVAR_LINEAR_NONE:
|
||||
options.SetFVarLinearInterpolation(Sdc::Options::FVAR_LINEAR_NONE);
|
||||
break;
|
||||
case Mesh::SUBDIVISION_FVAR_LINEAR_CORNERS_ONLY:
|
||||
options.SetFVarLinearInterpolation(Sdc::Options::FVAR_LINEAR_CORNERS_ONLY);
|
||||
break;
|
||||
case Mesh::SUBDIVISION_FVAR_LINEAR_CORNERS_PLUS1:
|
||||
options.SetFVarLinearInterpolation(Sdc::Options::FVAR_LINEAR_CORNERS_PLUS1);
|
||||
break;
|
||||
case Mesh::SUBDIVISION_FVAR_LINEAR_CORNERS_PLUS2:
|
||||
options.SetFVarLinearInterpolation(Sdc::Options::FVAR_LINEAR_CORNERS_PLUS2);
|
||||
break;
|
||||
case Mesh::SUBDIVISION_FVAR_LINEAR_BOUNDARIES:
|
||||
options.SetFVarLinearInterpolation(Sdc::Options::FVAR_LINEAR_BOUNDARIES);
|
||||
break;
|
||||
case Mesh::SUBDIVISION_FVAR_LINEAR_ALL:
|
||||
options.SetFVarLinearInterpolation(Sdc::Options::FVAR_LINEAR_ALL);
|
||||
break;
|
||||
}
|
||||
switch (mesh.get_subdivision_boundary_interpolation()) {
|
||||
case Mesh::SUBDIVISION_BOUNDARY_NONE:
|
||||
options.SetVtxBoundaryInterpolation(Sdc::Options::VTX_BOUNDARY_NONE);
|
||||
break;
|
||||
case Mesh::SUBDIVISION_BOUNDARY_EDGE_ONLY:
|
||||
options.SetVtxBoundaryInterpolation(Sdc::Options::VTX_BOUNDARY_EDGE_ONLY);
|
||||
break;
|
||||
case Mesh::SUBDIVISION_BOUNDARY_EDGE_AND_CORNER:
|
||||
options.SetVtxBoundaryInterpolation(Sdc::Options::VTX_BOUNDARY_EDGE_AND_CORNER);
|
||||
break;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
bool OsdMesh::use_smooth_fvar(const Attribute &attr) const
|
||||
{
|
||||
return mesh.get_subdivision_fvar_interpolation() != Mesh::SUBDIVISION_FVAR_LINEAR_ALL &&
|
||||
attr.element == ATTR_ELEMENT_CORNER &&
|
||||
(attr.std == ATTR_STD_UV || (attr.flags & ATTR_SUBDIVIDE_SMOOTH_FVAR));
|
||||
}
|
||||
|
||||
bool OsdMesh::use_smooth_fvar() const
|
||||
{
|
||||
for (const Attribute &attr : mesh.subd_attributes.attributes) {
|
||||
if (use_smooth_fvar(attr)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* OsdData */
|
||||
|
||||
void OsdData::build(OsdMesh &osd_mesh)
|
||||
{
|
||||
/* create refiner */
|
||||
refiner.reset(Far::TopologyRefinerFactory<OsdMesh>::Create(
|
||||
osd_mesh,
|
||||
Far::TopologyRefinerFactory<OsdMesh>::Options(Sdc::SCHEME_CATMARK, osd_mesh.sdc_options())));
|
||||
|
||||
/* adaptive refinement */
|
||||
const bool has_fvar = osd_mesh.use_smooth_fvar();
|
||||
const int max_isolation = 3; // TODO: get from Blender
|
||||
|
||||
Far::TopologyRefiner::AdaptiveOptions adaptive_options(max_isolation);
|
||||
adaptive_options.considerFVarChannels = has_fvar;
|
||||
adaptive_options.useInfSharpPatch = true;
|
||||
refiner->RefineAdaptive(adaptive_options);
|
||||
|
||||
/* create patch table */
|
||||
Far::PatchTableFactory::Options patch_options;
|
||||
patch_options.endCapType = Far::PatchTableFactory::Options::ENDCAP_GREGORY_BASIS;
|
||||
patch_options.generateFVarTables = has_fvar;
|
||||
patch_options.generateFVarLegacyLinearPatches = false;
|
||||
patch_options.useInfSharpPatch = true;
|
||||
|
||||
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();
|
||||
const int num_base_verts = osd_mesh.mesh.get_num_subd_base_verts();
|
||||
const Attribute *attr_P = osd_mesh.mesh.subd_attributes.find(ATTR_STD_POSITION);
|
||||
const packed_float3 *verts_data = attr_P->data<packed_float3>();
|
||||
|
||||
refined_verts.resize(num_refiner_verts + num_local_points);
|
||||
for (int i = 0; i < num_base_verts; i++) {
|
||||
refined_verts[i].value = float3(verts_data[i]);
|
||||
}
|
||||
|
||||
OsdValue<packed_float3> *src = refined_verts.data();
|
||||
for (int i = 0; i < refiner->GetMaxLevel(); i++) {
|
||||
OsdValue<packed_float3> *dest = src + refiner->GetLevel(i).GetNumVertices();
|
||||
Far::PrimvarRefiner(*refiner).Interpolate(i + 1, src, dest);
|
||||
src = dest;
|
||||
}
|
||||
|
||||
if (num_local_points) {
|
||||
patch_table->ComputeLocalPointValues(refined_verts.data(), &refined_verts[num_refiner_verts]);
|
||||
}
|
||||
|
||||
/* Create patch map */
|
||||
patch_map = make_unique<Far::PatchMap>(*patch_table);
|
||||
}
|
||||
|
||||
/* OsdPatch */
|
||||
|
||||
void OsdPatch::eval(
|
||||
float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, const float u, const float v) const
|
||||
{
|
||||
const Far::PatchTable::PatchHandle &handle = *osd_data.patch_map->FindPatch(
|
||||
patch_index, (double)u, (double)v);
|
||||
|
||||
float p_weights[20], du_weights[20], 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);
|
||||
|
||||
if (P) {
|
||||
*P = zero_float3();
|
||||
}
|
||||
float3 du = zero_float3();
|
||||
float3 dv = zero_float3();
|
||||
|
||||
for (int i = 0; i < cv.size(); i++) {
|
||||
const float3 p = osd_data.refined_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 = safe_normalize_fallback(cross(du, dv), make_float3(0.0f, 0.0f, 1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
104
blender-5.2.0/intern/cycles/subd/osd.h
Normal file
104
blender-5.2.0/intern/cycles/subd/osd.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2024 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
|
||||
/* OpenSubdiv headers use M_PI. */
|
||||
# if defined(_MSC_VER) && !defined(_USE_MATH_DEFINES)
|
||||
# define _USE_MATH_DEFINES
|
||||
# endif
|
||||
|
||||
# include <opensubdiv/far/patchMap.h>
|
||||
# include <opensubdiv/far/patchTableFactory.h>
|
||||
# include <opensubdiv/far/primvarRefiner.h>
|
||||
# include <opensubdiv/far/topologyRefinerFactory.h>
|
||||
|
||||
# include "subd/patch.h"
|
||||
|
||||
# include "util/types.h"
|
||||
# include "util/unique_ptr.h"
|
||||
# include "util/vector.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* Directly use some OpenSubdiv namespaces for brevity. */
|
||||
namespace Far = OpenSubdiv::Far;
|
||||
namespace Sdc = OpenSubdiv::Sdc;
|
||||
|
||||
class Attribute;
|
||||
class Mesh;
|
||||
|
||||
/* OpenSubdiv interface for vertex and attribute values. */
|
||||
|
||||
template<typename T> struct OsdValue {
|
||||
T value;
|
||||
|
||||
OsdValue() = default;
|
||||
|
||||
void Clear(void *unused = nullptr)
|
||||
{
|
||||
(void)unused;
|
||||
memset((void *)&value, 0, sizeof(T));
|
||||
}
|
||||
|
||||
void AddWithWeight(OsdValue<T> const &src, float weight)
|
||||
{
|
||||
if constexpr (std::is_same_v<T, packed_float3>) {
|
||||
value = float3(value) + float3(src.value) * weight;
|
||||
}
|
||||
else {
|
||||
value += src.value * weight;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* Wrapper around Mesh for TopologyRefinerFactory. */
|
||||
|
||||
class OsdMesh {
|
||||
public:
|
||||
/* Face-varying attribute that requires merging of corners with the same value, typically a UV
|
||||
* map. The resulting topology after merging is stored in a topology refiner fvar channel. The
|
||||
* merged attribute values are stored here, in a generic buffer used for different data types. */
|
||||
struct MergedFVar {
|
||||
const Attribute &attr;
|
||||
int channel = -1;
|
||||
vector<char> values;
|
||||
};
|
||||
|
||||
Mesh &mesh;
|
||||
vector<MergedFVar> merged_fvars;
|
||||
|
||||
explicit OsdMesh(Mesh &mesh) : mesh(mesh) {}
|
||||
|
||||
Sdc::Options sdc_options();
|
||||
bool use_smooth_fvar(const Attribute &attr) const;
|
||||
bool use_smooth_fvar() const;
|
||||
};
|
||||
|
||||
/* OpenSubdiv refiner and patch data structures. */
|
||||
|
||||
struct OsdData {
|
||||
unique_ptr<Far::TopologyRefiner> refiner;
|
||||
unique_ptr<Far::PatchTable> patch_table;
|
||||
unique_ptr<Far::PatchMap> patch_map;
|
||||
vector<OsdValue<packed_float3>> refined_verts;
|
||||
|
||||
void build(OsdMesh &osd_mesh);
|
||||
};
|
||||
|
||||
/* Patch with OpenSubdiv evaluation. */
|
||||
|
||||
struct OsdPatch final : Patch {
|
||||
OsdData &osd_data;
|
||||
|
||||
explicit OsdPatch(OsdData &data) : osd_data(data) {}
|
||||
void eval(float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, const float u, const float v)
|
||||
const override;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif
|
||||
119
blender-5.2.0/intern/cycles/subd/patch.cpp
Normal file
119
blender-5.2.0/intern/cycles/subd/patch.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
/* Parts adapted from code in the public domain in NVidia Mesh Tools. */
|
||||
|
||||
#include "subd/patch.h"
|
||||
|
||||
#include "util/math.h"
|
||||
#include "util/types.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* De Casteljau Evaluation */
|
||||
|
||||
static void decasteljau_cubic(float3 *P, float3 *dt, const float t, const float3 cp[4])
|
||||
{
|
||||
float3 d0 = cp[0] + t * (cp[1] - cp[0]);
|
||||
float3 d1 = cp[1] + t * (cp[2] - cp[1]);
|
||||
const float3 d2 = cp[2] + t * (cp[3] - cp[2]);
|
||||
|
||||
d0 += t * (d1 - d0);
|
||||
d1 += t * (d2 - d1);
|
||||
|
||||
*P = d0 + t * (d1 - d0);
|
||||
if (dt) {
|
||||
*dt = d1 - d0;
|
||||
}
|
||||
}
|
||||
|
||||
static void decasteljau_bicubic(
|
||||
float3 *P, float3 *du, float3 *dv, const float3 cp[16], float u, const float v)
|
||||
{
|
||||
float3 ucp[4];
|
||||
float3 utn[4];
|
||||
|
||||
/* interpolate over u */
|
||||
decasteljau_cubic(ucp + 0, utn + 0, u, cp);
|
||||
decasteljau_cubic(ucp + 1, utn + 1, u, cp + 4);
|
||||
decasteljau_cubic(ucp + 2, utn + 2, u, cp + 8);
|
||||
decasteljau_cubic(ucp + 3, utn + 3, u, cp + 12);
|
||||
|
||||
/* interpolate over v */
|
||||
decasteljau_cubic(P, dv, v, ucp);
|
||||
if (du) {
|
||||
decasteljau_cubic(du, nullptr, v, utn);
|
||||
}
|
||||
}
|
||||
|
||||
/* Linear Quad Patch */
|
||||
|
||||
void LinearQuadPatch::eval(
|
||||
float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, const float u, float v) const
|
||||
{
|
||||
const float3 d0 = interp(hull[0], hull[1], u);
|
||||
const float3 d1 = interp(hull[2], hull[3], u);
|
||||
|
||||
*P = interp(d0, d1, v);
|
||||
|
||||
if (N || (dPdu && dPdv)) {
|
||||
const float3 dPdu_ = interp(hull[1] - hull[0], hull[3] - hull[2], v);
|
||||
const float3 dPdv_ = interp(hull[2] - hull[0], hull[3] - hull[1], u);
|
||||
|
||||
if (dPdu && dPdv) {
|
||||
*dPdu = dPdu_;
|
||||
*dPdv = dPdv_;
|
||||
}
|
||||
|
||||
if (N) {
|
||||
*N = normalize(cross(dPdu_, dPdv_));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BoundBox LinearQuadPatch::bound()
|
||||
{
|
||||
BoundBox bbox = BoundBox::empty;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
bbox.grow(hull[i]);
|
||||
}
|
||||
|
||||
return bbox;
|
||||
}
|
||||
|
||||
/* Bicubic Patch */
|
||||
|
||||
void BicubicPatch::eval(
|
||||
float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, const float u, const float v) const
|
||||
{
|
||||
if (N) {
|
||||
float3 dPdu_;
|
||||
float3 dPdv_;
|
||||
decasteljau_bicubic(P, &dPdu_, &dPdv_, hull, u, v);
|
||||
|
||||
if (dPdu && dPdv) {
|
||||
*dPdu = dPdu_;
|
||||
*dPdv = dPdv_;
|
||||
}
|
||||
|
||||
*N = normalize(cross(dPdu_, dPdv_));
|
||||
}
|
||||
else {
|
||||
decasteljau_bicubic(P, dPdu, dPdv, hull, u, v);
|
||||
}
|
||||
}
|
||||
|
||||
BoundBox BicubicPatch::bound()
|
||||
{
|
||||
BoundBox bbox = BoundBox::empty;
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
bbox.grow(hull[i]);
|
||||
}
|
||||
|
||||
return bbox;
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
48
blender-5.2.0/intern/cycles/subd/patch.h
Normal file
48
blender-5.2.0/intern/cycles/subd/patch.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/boundbox.h"
|
||||
#include "util/types.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class Patch {
|
||||
public:
|
||||
Patch() = default;
|
||||
virtual ~Patch() = default;
|
||||
|
||||
virtual void eval(
|
||||
float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, const float u, float v) const = 0;
|
||||
|
||||
int patch_index = 0;
|
||||
int shader = 0;
|
||||
bool smooth = true;
|
||||
bool from_ngon = false;
|
||||
};
|
||||
|
||||
/* Linear Quad Patch */
|
||||
|
||||
class LinearQuadPatch final : public Patch {
|
||||
public:
|
||||
float3 hull[4];
|
||||
|
||||
void eval(
|
||||
float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, const float u, float v) const override;
|
||||
BoundBox bound();
|
||||
};
|
||||
|
||||
/* Bicubic Patch */
|
||||
|
||||
class BicubicPatch final : public Patch {
|
||||
public:
|
||||
float3 hull[16];
|
||||
|
||||
void eval(
|
||||
float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, const float u, float v) const override;
|
||||
BoundBox bound();
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
782
blender-5.2.0/intern/cycles/subd/split.cpp
Normal file
782
blender-5.2.0/intern/cycles/subd/split.cpp
Normal file
@@ -0,0 +1,782 @@
|
||||
/* 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/patch.h"
|
||||
#include "subd/split.h"
|
||||
|
||||
#include "subd/subpatch.h"
|
||||
#include "util/algorithm.h"
|
||||
|
||||
#include "util/math.h"
|
||||
#include "util/types.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* DiagSplit */
|
||||
|
||||
DiagSplit::DiagSplit(const SubdParams ¶ms_) : params(params_) {}
|
||||
|
||||
int DiagSplit::alloc_verts(int num)
|
||||
{
|
||||
const int index = num_verts;
|
||||
num_verts += num;
|
||||
return index;
|
||||
}
|
||||
|
||||
SubEdge *DiagSplit::alloc_edge(const int v0, const int v1, const int depth, bool &was_missing)
|
||||
{
|
||||
const SubEdge edge(v0, v1, depth);
|
||||
const auto it = edges.find(edge);
|
||||
was_missing = (it == edges.end());
|
||||
return const_cast<SubEdge *>(was_missing ? &*(edges.emplace(edge).first) : &*it);
|
||||
}
|
||||
|
||||
void DiagSplit::alloc_edge(SubPatch::Edge *sub_edge,
|
||||
const int v0,
|
||||
const int v1,
|
||||
const int depth,
|
||||
const bool want_to_own_edge,
|
||||
const bool want_to_own_vertex)
|
||||
{
|
||||
bool was_missing;
|
||||
sub_edge->edge = (v0 < v1) ? alloc_edge(v0, v1, depth, was_missing) :
|
||||
alloc_edge(v1, v0, depth, was_missing);
|
||||
sub_edge->own_vertex = false;
|
||||
sub_edge->own_edge = was_missing && want_to_own_edge;
|
||||
sub_edge->reversed = sub_edge->edge->start_vert_index != v0;
|
||||
|
||||
if (want_to_own_vertex) {
|
||||
if (v0 < owned_verts.size()) {
|
||||
/* Vertex in original mesh. */
|
||||
if (!owned_verts[v0]) {
|
||||
owned_verts[v0] = true;
|
||||
sub_edge->own_vertex = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Mid edge vertex. */
|
||||
sub_edge->own_vertex = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DiagSplit::alloc_subpatch(SubPatch &&sub)
|
||||
{
|
||||
assert(sub.edges[0].edge->T >= 1);
|
||||
assert(sub.edges[1].edge->T >= 1);
|
||||
assert(sub.edges[2].edge->T >= 1);
|
||||
if (sub.shape == SubPatch::QUAD) {
|
||||
assert(sub.edges[3].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));
|
||||
}
|
||||
|
||||
float3 DiagSplit::to_world(const Patch *patch, const float2 uv)
|
||||
{
|
||||
float3 P;
|
||||
|
||||
patch->eval(&P, nullptr, nullptr, nullptr, uv.x, uv.y);
|
||||
if (params.camera) {
|
||||
P = transform_point(¶ms.objecttoworld, P);
|
||||
}
|
||||
|
||||
return P;
|
||||
}
|
||||
|
||||
std::pair<int, float> DiagSplit::T(const Patch *patch,
|
||||
float2 uv_start,
|
||||
float2 uv_end,
|
||||
const int depth,
|
||||
const bool recursive_resolve)
|
||||
{
|
||||
/* May not be necessary, but better to be safe. */
|
||||
if (uv_end.x < uv_start.x || uv_end.y < uv_start.y) {
|
||||
swap(uv_start, uv_end);
|
||||
}
|
||||
|
||||
float Lsum = 0.0f;
|
||||
float Lmax = 0.0f;
|
||||
float Lsum_world = 0.0f;
|
||||
|
||||
float3 Plast = to_world(patch, uv_start);
|
||||
|
||||
for (int i = 1; i < params.test_steps; i++) {
|
||||
const float t = i / (float)(params.test_steps - 1);
|
||||
|
||||
const float3 P = to_world(patch, uv_start + t * (uv_end - uv_start));
|
||||
|
||||
float L = len(P - Plast);
|
||||
Lsum_world += L;
|
||||
|
||||
if (params.camera) {
|
||||
Camera *cam = params.camera;
|
||||
|
||||
const float pixel_width = cam->world_to_raster_size((P + Plast) * 0.5f);
|
||||
L /= pixel_width;
|
||||
}
|
||||
|
||||
Lsum += L;
|
||||
Lmax = max(L, Lmax);
|
||||
|
||||
Plast = P;
|
||||
}
|
||||
|
||||
const int tmin = (int)ceilf(Lsum / params.dicing_rate);
|
||||
const int tmax = (int)ceilf(
|
||||
(params.test_steps - 1) * Lmax /
|
||||
params.dicing_rate); // XXX paper says N instead of N-1, seems wrong?
|
||||
int res = max(tmax, 1);
|
||||
|
||||
if (tmax - tmin > params.split_threshold) {
|
||||
if (!recursive_resolve) {
|
||||
res = DSPLIT_NON_UNIFORM;
|
||||
}
|
||||
else {
|
||||
const float2 uv_mid = (uv_start + uv_end) * 0.5f;
|
||||
const auto result_a = T(patch, uv_start, uv_mid, depth, true);
|
||||
const auto result_b = T(patch, uv_mid, uv_end, depth, true);
|
||||
res = result_a.first + result_b.first;
|
||||
Lsum_world = result_a.second + result_b.second;
|
||||
}
|
||||
}
|
||||
|
||||
if (!recursive_resolve && res > DSPLIT_MAX_SEGMENTS) {
|
||||
res = DSPLIT_NON_UNIFORM;
|
||||
}
|
||||
|
||||
res = limit_edge_factor(patch, uv_start, uv_end, res);
|
||||
|
||||
/* Limit edge factor so we don't go beyond max depth. -3 is so that
|
||||
* for triangle patches, all 3 edges get an opportunity to get split. */
|
||||
if (depth >= DSPLIT_MAX_DEPTH - 3 && res == DSPLIT_NON_UNIFORM) {
|
||||
res = DSPLIT_MAX_SEGMENTS;
|
||||
}
|
||||
|
||||
return std::make_pair(res, Lsum_world);
|
||||
}
|
||||
|
||||
int DiagSplit::limit_edge_factor(const Patch *patch,
|
||||
const float2 uv_start,
|
||||
const float2 uv_end,
|
||||
const int T)
|
||||
{
|
||||
const int max_t = 1 << params.max_level;
|
||||
int max_t_for_edge = int(max_t * len(uv_start - uv_end));
|
||||
|
||||
if (patch->from_ngon) {
|
||||
max_t_for_edge >>= 1; /* Initial split of ngon causes edges to extend half the distance. */
|
||||
}
|
||||
|
||||
const int limit_T = (max_t_for_edge <= 1) ? 1 : min(T, max_t_for_edge);
|
||||
|
||||
assert(limit_T != 0);
|
||||
return limit_T;
|
||||
}
|
||||
|
||||
void DiagSplit::assign_edge_factor(SubEdge *edge,
|
||||
const Patch *patch,
|
||||
float2 uv_start,
|
||||
float2 uv_end,
|
||||
const bool recursive_resolve)
|
||||
{
|
||||
assert(edge->T <= 0);
|
||||
|
||||
const auto result = T(patch, uv_start, uv_end, edge->depth, recursive_resolve);
|
||||
edge->T = result.first;
|
||||
edge->length = result.second;
|
||||
|
||||
/* Ensure we can always split at depth - 1. */
|
||||
if (edge->depth == -1 && edge->T == 1) {
|
||||
edge->T = 2;
|
||||
}
|
||||
|
||||
if (edge->T > 0) {
|
||||
edge->second_vert_index = alloc_verts(edge->T - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void DiagSplit::resolve_edge_factors(const SubPatch &sub)
|
||||
{
|
||||
SubEdge *edge0 = sub.edges[0].edge;
|
||||
SubEdge *edge1 = sub.edges[1].edge;
|
||||
SubEdge *edge2 = sub.edges[2].edge;
|
||||
|
||||
/* Compute edge factor if not already set. */
|
||||
if (edge0->T == 0) {
|
||||
assign_edge_factor(edge0, sub.patch, sub.uvs[0], sub.uvs[1], true);
|
||||
}
|
||||
if (edge1->T == 0) {
|
||||
assign_edge_factor(edge1, sub.patch, sub.uvs[1], sub.uvs[2], true);
|
||||
}
|
||||
|
||||
if (sub.shape == SubPatch::TRIANGLE) {
|
||||
if (edge2->T == 0) {
|
||||
assign_edge_factor(edge2, sub.patch, sub.uvs[2], sub.uvs[0], true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
SubEdge *edge3 = sub.edges[3].edge;
|
||||
|
||||
if (edge2->T == 0) {
|
||||
assign_edge_factor(edge2, sub.patch, sub.uvs[2], sub.uvs[3], true);
|
||||
}
|
||||
if (edge3->T == 0) {
|
||||
assign_edge_factor(edge3, sub.patch, sub.uvs[3], sub.uvs[0], true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float2 DiagSplit::split_edge(const Patch *patch,
|
||||
SubPatch::Edge *subedge,
|
||||
SubPatch::Edge *subedge_a,
|
||||
SubPatch::Edge *subedge_b,
|
||||
float2 uv_start,
|
||||
float2 uv_end)
|
||||
{
|
||||
/* This splits following the direction of the edge itself, not subpatch edge direction. */
|
||||
if (subedge->reversed) {
|
||||
swap(uv_start, uv_end);
|
||||
}
|
||||
|
||||
SubEdge *edge = subedge->edge;
|
||||
|
||||
if (edge->must_split()) {
|
||||
/* Split down the middle. */
|
||||
const float2 P = 0.5f * (uv_start + uv_end);
|
||||
if (edge->mid_vert_index == -1) {
|
||||
/* Allocate mid vertex and edges. */
|
||||
edge->mid_vert_index = alloc_verts(1);
|
||||
|
||||
bool unused;
|
||||
SubEdge *edge_a = alloc_edge(
|
||||
edge->start_vert_index, edge->mid_vert_index, edge->depth + 1, unused);
|
||||
SubEdge *edge_b = alloc_edge(
|
||||
edge->mid_vert_index, edge->end_vert_index, edge->depth + 1, unused);
|
||||
assign_edge_factor(edge_a, patch, uv_start, P);
|
||||
assign_edge_factor(edge_b, patch, P, uv_end);
|
||||
}
|
||||
|
||||
/* Allocate sub edges and set ownership. */
|
||||
alloc_edge(subedge_a,
|
||||
subedge->start_vert_index(),
|
||||
subedge->mid_vert_index(),
|
||||
edge->depth + 1,
|
||||
false,
|
||||
false);
|
||||
alloc_edge(subedge_b,
|
||||
subedge->mid_vert_index(),
|
||||
subedge->end_vert_index(),
|
||||
edge->depth + 1,
|
||||
false,
|
||||
false);
|
||||
|
||||
subedge_a->own_edge = subedge->own_edge;
|
||||
subedge_b->own_edge = subedge->own_edge;
|
||||
subedge_a->own_vertex = subedge->own_vertex;
|
||||
subedge_b->own_vertex = subedge->own_edge;
|
||||
|
||||
assert(P.x >= 0 && P.x <= 1.0f && P.y >= 0.0f && P.y <= 1.0f);
|
||||
return P;
|
||||
}
|
||||
|
||||
assert(edge->T >= 2);
|
||||
const int mid = edge->T / 2;
|
||||
|
||||
/* T is final and edge vertices are already allocated. An adjacent subpatch may not
|
||||
* split this edge. So we ensure T and vertex indices match up with the non-split edge. */
|
||||
if (edge->mid_vert_index == -1) {
|
||||
/* Allocate mid vertex and edges. */
|
||||
edge->mid_vert_index = edge->second_vert_index - 1 + mid;
|
||||
|
||||
bool unused;
|
||||
SubEdge *edge_a = alloc_edge(
|
||||
edge->start_vert_index, edge->mid_vert_index, edge->depth + 1, unused);
|
||||
SubEdge *edge_b = alloc_edge(
|
||||
edge->mid_vert_index, edge->end_vert_index, edge->depth + 1, unused);
|
||||
edge_a->T = mid;
|
||||
edge_b->T = edge->T - mid;
|
||||
edge_a->second_vert_index = edge->second_vert_index;
|
||||
edge_b->second_vert_index = edge->second_vert_index + edge_a->T;
|
||||
}
|
||||
|
||||
/* Allocate sub edges and set ownership. */
|
||||
alloc_edge(subedge_a,
|
||||
subedge->start_vert_index(),
|
||||
subedge->mid_vert_index(),
|
||||
edge->depth + 1,
|
||||
false,
|
||||
false);
|
||||
alloc_edge(subedge_b,
|
||||
subedge->mid_vert_index(),
|
||||
subedge->end_vert_index(),
|
||||
edge->depth + 1,
|
||||
false,
|
||||
false);
|
||||
|
||||
subedge_a->own_edge = subedge->own_edge;
|
||||
subedge_b->own_edge = subedge->own_edge;
|
||||
subedge_a->own_vertex = subedge->own_vertex;
|
||||
subedge_b->own_vertex = subedge->own_edge;
|
||||
|
||||
const float2 P = interp(uv_start, uv_end, mid / (float)edge->T);
|
||||
assert(P.x >= 0 && P.x <= 1.0f && P.y >= 0.0f && P.y <= 1.0f);
|
||||
|
||||
return P;
|
||||
}
|
||||
|
||||
void DiagSplit::split_quad(SubPatch &&sub)
|
||||
{
|
||||
/* Set edge factors if we haven't already. */
|
||||
resolve_edge_factors(sub);
|
||||
|
||||
/* Split subpatch if edges are marked as must split,
|
||||
* or if the following conditions are met:
|
||||
* - Both edges have at least 2 segments.
|
||||
* - Either edge has more than DSPLIT_MAX_SEGMENTS segments.
|
||||
* - The ratio of segments for opposite edges doesn't exceed 1.5.
|
||||
* This reduces over tessellation for some patches. */
|
||||
const int min_T_u = min(sub.edges[0].edge->T, sub.edges[2].edge->T);
|
||||
const int max_T_u = max(sub.edges[0].edge->T, sub.edges[2].edge->T);
|
||||
const int min_T_v = min(sub.edges[3].edge->T, sub.edges[1].edge->T);
|
||||
const int max_T_v = max(sub.edges[3].edge->T, sub.edges[1].edge->T);
|
||||
|
||||
bool split_u = sub.edges[0].edge->must_split() || sub.edges[2].edge->must_split() ||
|
||||
(min_T_u >= 2 && min_T_v > DSPLIT_MAX_SEGMENTS && max_T_v / min_T_v > 1.5f);
|
||||
bool split_v = sub.edges[3].edge->must_split() || sub.edges[1].edge->must_split() ||
|
||||
(min_T_v >= 2 && min_T_u > DSPLIT_MAX_SEGMENTS && max_T_u / min_T_u > 1.5f);
|
||||
|
||||
/* If both need to split, pick longest axis. */
|
||||
if (split_u && split_v) {
|
||||
/* Slight bias so that for square quads, we get consistent results across
|
||||
* platforms rather than choice being decided by precision. */
|
||||
const float bias = 1.00012345f;
|
||||
if ((sub.edges[0].edge->length + sub.edges[2].edge->length) * bias >=
|
||||
sub.edges[1].edge->length + sub.edges[3].edge->length)
|
||||
{
|
||||
split_u = true;
|
||||
split_v = false;
|
||||
}
|
||||
else {
|
||||
split_u = false;
|
||||
split_v = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!split_u && !split_v) {
|
||||
/* Add the unsplit subpatch. */
|
||||
alloc_subpatch(std::move(sub));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Split into triangles if one side must the split, and the opposite side has
|
||||
* only a single segment. Then we can't do an even split across the quad. */
|
||||
if ((split_u && (sub.edges[0].edge->T == 1 || sub.edges[2].edge->T == 1)) ||
|
||||
(!split_u && (sub.edges[1].edge->T == 1 || sub.edges[3].edge->T == 1)))
|
||||
{
|
||||
split_quad_into_triangles(std::move(sub));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Copy into new subpatches. */
|
||||
SubPatch sub_a(sub);
|
||||
SubPatch sub_b(sub);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
sub_a.edges[i].own_edge = false;
|
||||
sub_a.edges[i].own_vertex = false;
|
||||
sub_b.edges[i].own_edge = false;
|
||||
sub_b.edges[i].own_vertex = false;
|
||||
}
|
||||
|
||||
/* Pointers to various subpatch elements. */
|
||||
SubPatch::Edge *sub_across_0;
|
||||
SubPatch::Edge *sub_across_1;
|
||||
SubPatch::Edge *sub_a_across_0;
|
||||
SubPatch::Edge *sub_a_across_1;
|
||||
SubPatch::Edge *sub_b_across_0;
|
||||
SubPatch::Edge *sub_b_across_1;
|
||||
|
||||
SubPatch::Edge *sub_a_split;
|
||||
SubPatch::Edge *sub_b_split;
|
||||
|
||||
float2 *uv_a;
|
||||
float2 *uv_b;
|
||||
float2 *uv_c;
|
||||
float2 *uv_d;
|
||||
|
||||
/* Set pointers based on split axis. */
|
||||
if (split_u) {
|
||||
/*
|
||||
* sub_across_1
|
||||
* -------uv_a uv_c-------
|
||||
* | | | |
|
||||
* | A | | B |
|
||||
* | | | |
|
||||
* -------uv_b uv_d-------
|
||||
* sub_across_0
|
||||
*/
|
||||
sub_across_0 = &sub.edges[0];
|
||||
sub_across_1 = &sub.edges[2];
|
||||
sub_a_across_0 = &sub_a.edges[0];
|
||||
sub_a_across_1 = &sub_a.edges[2];
|
||||
sub_b_across_0 = &sub_b.edges[0];
|
||||
sub_b_across_1 = &sub_b.edges[2];
|
||||
|
||||
sub_a.edges[3].own_edge = sub.edges[3].own_edge;
|
||||
sub_a.edges[3].own_vertex = sub.edges[3].own_vertex;
|
||||
sub_b.edges[1].own_edge = sub.edges[1].own_edge;
|
||||
sub_b.edges[1].own_vertex = sub.edges[1].own_vertex;
|
||||
|
||||
sub_a_split = &sub_a.edges[1];
|
||||
sub_b_split = &sub_b.edges[3];
|
||||
|
||||
uv_a = &sub_a.uvs[2];
|
||||
uv_b = &sub_a.uvs[1];
|
||||
uv_c = &sub_b.uvs[3];
|
||||
uv_d = &sub_b.uvs[0];
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* --------------------
|
||||
* | A |
|
||||
* uv_b------------uv_a
|
||||
* sub_across_0 sub_across_1
|
||||
* uv_d------------uv_c
|
||||
* | B |
|
||||
* --------------------
|
||||
*/
|
||||
sub_across_0 = &sub.edges[3];
|
||||
sub_across_1 = &sub.edges[1];
|
||||
sub_a_across_0 = &sub_a.edges[3];
|
||||
sub_a_across_1 = &sub_a.edges[1];
|
||||
sub_b_across_0 = &sub_b.edges[3];
|
||||
sub_b_across_1 = &sub_b.edges[1];
|
||||
|
||||
sub_a.edges[2].own_edge = sub.edges[2].own_edge;
|
||||
sub_a.edges[2].own_vertex = sub.edges[2].own_vertex;
|
||||
sub_b.edges[0].own_edge = sub.edges[0].own_edge;
|
||||
sub_b.edges[0].own_vertex = sub.edges[0].own_vertex;
|
||||
|
||||
sub_a_split = &sub_a.edges[0];
|
||||
sub_b_split = &sub_b.edges[2];
|
||||
|
||||
uv_a = &sub_a.uvs[1];
|
||||
uv_b = &sub_a.uvs[0];
|
||||
uv_c = &sub_b.uvs[2];
|
||||
uv_d = &sub_b.uvs[3];
|
||||
}
|
||||
|
||||
/* Allocate new edges and vertices. */
|
||||
const float2 uv0 = split_edge(
|
||||
sub.patch, sub_across_0, sub_a_across_0, sub_b_across_0, *uv_d, *uv_b);
|
||||
const float2 uv1 = split_edge(
|
||||
sub.patch, sub_across_1, sub_b_across_1, sub_a_across_1, *uv_a, *uv_c);
|
||||
|
||||
assert(sub_a_across_0->edge->T != 0);
|
||||
assert(sub_b_across_0->edge->T != 0);
|
||||
assert(sub_a_across_1->edge->T != 0);
|
||||
assert(sub_b_across_1->edge->T != 0);
|
||||
|
||||
/* Split */
|
||||
*uv_a = uv1;
|
||||
*uv_b = uv0;
|
||||
|
||||
*uv_c = uv1;
|
||||
*uv_d = uv0;
|
||||
|
||||
/* Create new edge */
|
||||
const int split_edge_depth = (split_u) ?
|
||||
max(sub.edges[1].edge->depth, sub.edges[3].edge->depth) :
|
||||
max(sub.edges[0].edge->depth, sub.edges[2].edge->depth);
|
||||
alloc_edge(sub_a_split,
|
||||
sub_across_0->mid_vert_index(),
|
||||
sub_across_1->mid_vert_index(),
|
||||
split_edge_depth,
|
||||
true,
|
||||
false);
|
||||
alloc_edge(sub_b_split,
|
||||
sub_across_1->mid_vert_index(),
|
||||
sub_across_0->mid_vert_index(),
|
||||
split_edge_depth,
|
||||
true,
|
||||
false);
|
||||
|
||||
/* Set T for split edge. */
|
||||
assign_edge_factor(sub_a_split->edge, sub.patch, uv0, uv1);
|
||||
|
||||
/* Recurse */
|
||||
split_quad(std::move(sub_a));
|
||||
split_quad(std::move(sub_b));
|
||||
}
|
||||
|
||||
void DiagSplit::split_quad_into_triangles(SubPatch &&sub)
|
||||
{
|
||||
assert(sub.shape == SubPatch::QUAD);
|
||||
|
||||
/* Copy into new subpatches. */
|
||||
SubPatch sub_a(sub);
|
||||
SubPatch sub_b(sub);
|
||||
|
||||
sub_a.shape = SubPatch::TRIANGLE;
|
||||
sub_b.shape = SubPatch::TRIANGLE;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
sub_a.edges[i].own_edge = false;
|
||||
sub_a.edges[i].own_vertex = false;
|
||||
sub_b.edges[i].own_edge = false;
|
||||
sub_b.edges[i].own_vertex = false;
|
||||
}
|
||||
|
||||
const int split_edge_depth = std::max({sub.edges[0].edge->depth,
|
||||
sub.edges[1].edge->depth,
|
||||
sub.edges[2].edge->depth,
|
||||
sub.edges[3].edge->depth});
|
||||
|
||||
sub_a.edges[0] = sub.edges[0];
|
||||
sub_a.edges[1] = sub.edges[1];
|
||||
sub_a.uvs[0] = sub.uvs[0];
|
||||
sub_a.uvs[1] = sub.uvs[1];
|
||||
sub_a.uvs[2] = sub.uvs[2];
|
||||
alloc_edge(&sub_a.edges[2],
|
||||
sub.edges[2].start_vert_index(),
|
||||
sub.edges[0].start_vert_index(),
|
||||
split_edge_depth,
|
||||
true,
|
||||
false);
|
||||
|
||||
sub_b.edges[1] = sub.edges[2];
|
||||
sub_b.edges[2] = sub.edges[3];
|
||||
sub_b.uvs[0] = sub.uvs[0];
|
||||
sub_b.uvs[1] = sub.uvs[2];
|
||||
sub_b.uvs[2] = sub.uvs[3];
|
||||
alloc_edge(&sub_b.edges[0],
|
||||
sub.edges[0].start_vert_index(),
|
||||
sub.edges[2].start_vert_index(),
|
||||
split_edge_depth,
|
||||
true,
|
||||
false);
|
||||
|
||||
/* Set T for new edge. */
|
||||
assign_edge_factor(sub_b.edges[0].edge, sub.patch, sub.uvs[0], sub.uvs[2]);
|
||||
|
||||
/* Recurse */
|
||||
split_triangle(std::move(sub_a));
|
||||
split_triangle(std::move(sub_b));
|
||||
}
|
||||
|
||||
void DiagSplit::split_triangle(SubPatch &&sub)
|
||||
{
|
||||
assert(sub.shape == SubPatch::TRIANGLE);
|
||||
|
||||
/* Set edge factors if we haven't already. */
|
||||
resolve_edge_factors(sub);
|
||||
|
||||
const bool do_split = sub.edges[0].edge->must_split() || sub.edges[1].edge->must_split() ||
|
||||
sub.edges[2].edge->must_split();
|
||||
if (!do_split) {
|
||||
/* Add the unsplit subpatch. */
|
||||
alloc_subpatch(std::move(sub));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Slight bias so that for equal length edges, we get consistent results across
|
||||
* platforms rather than choice being decided by precision. */
|
||||
const float bias = 1.00012345f;
|
||||
|
||||
/* Pick longest edge that must be split. Note that in degenerate cases edges may have
|
||||
* zero length but still requires splitting at depth 0. */
|
||||
float max_length = 0.0f;
|
||||
int split_index_0 = -1;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (sub.edges[i].edge->must_split() &&
|
||||
(split_index_0 == -1 || sub.edges[i].edge->length > max_length))
|
||||
{
|
||||
split_index_0 = i;
|
||||
max_length = sub.edges[i].edge->length * bias;
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy into new subpatches. */
|
||||
SubPatch sub_a(sub);
|
||||
SubPatch sub_b(sub);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
sub_a.edges[i].own_edge = false;
|
||||
sub_a.edges[i].own_vertex = false;
|
||||
sub_b.edges[i].own_edge = false;
|
||||
sub_b.edges[i].own_vertex = false;
|
||||
}
|
||||
|
||||
const int split_index_1 = (split_index_0 + 1) % 3;
|
||||
const int split_index_2 = (split_index_0 + 2) % 3;
|
||||
|
||||
sub_a.edges[2] = sub.edges[split_index_2];
|
||||
sub_b.edges[1] = sub.edges[split_index_1];
|
||||
|
||||
/*
|
||||
* uv_opposite
|
||||
* 2 2
|
||||
* / | | \
|
||||
* / | | \
|
||||
* / A | | B \
|
||||
* / | | \
|
||||
* 0 --- 1 0 --- 1
|
||||
* uv_split
|
||||
*/
|
||||
|
||||
/* Allocate new edges and vertices. */
|
||||
const float2 uv_split = split_edge(sub.patch,
|
||||
&sub.edges[split_index_0],
|
||||
&sub_a.edges[0],
|
||||
&sub_b.edges[0],
|
||||
sub.uvs[split_index_0],
|
||||
sub.uvs[split_index_1]);
|
||||
|
||||
/* Set UVs. */
|
||||
sub_a.uvs[0] = sub.uvs[split_index_0];
|
||||
sub_a.uvs[1] = uv_split;
|
||||
sub_a.uvs[2] = sub.uvs[split_index_2];
|
||||
sub_b.uvs[0] = uv_split;
|
||||
sub_b.uvs[1] = sub.uvs[split_index_1];
|
||||
sub_b.uvs[2] = sub.uvs[split_index_2];
|
||||
|
||||
/* Create new edge */
|
||||
const int vsplit = sub.edges[split_index_0].mid_vert_index();
|
||||
const int vopposite = sub.edges[split_index_2].start_vert_index();
|
||||
|
||||
const int split_edge_depth = sub.edges[split_index_0].edge->depth + 1;
|
||||
|
||||
alloc_edge(&sub_a.edges[1], vsplit, vopposite, split_edge_depth, true, false);
|
||||
alloc_edge(&sub_b.edges[2], vopposite, vsplit, split_edge_depth, true, false);
|
||||
|
||||
/* Set T for split edge. */
|
||||
const float2 uv_opposite = sub.uvs[split_index_2];
|
||||
assign_edge_factor(sub_a.edges[1].edge, sub.patch, uv_split, uv_opposite);
|
||||
|
||||
/* Recurse */
|
||||
split_triangle(std::move(sub_a));
|
||||
split_triangle(std::move(sub_b));
|
||||
}
|
||||
|
||||
void DiagSplit::split_quad(const Mesh::SubdFace &face, const int face_index, const Patch *patch)
|
||||
{
|
||||
const int *subd_face_corners = params.mesh->get_subd_face_corners().data();
|
||||
const int v0 = subd_face_corners[face.start_corner + 0];
|
||||
const int v1 = subd_face_corners[face.start_corner + 1];
|
||||
const int v2 = subd_face_corners[face.start_corner + 2];
|
||||
const int v3 = subd_face_corners[face.start_corner + 3];
|
||||
|
||||
const int depth = -1;
|
||||
|
||||
SubPatch subpatch(patch, face_index);
|
||||
alloc_edge(&subpatch.edges[0], v0, v1, depth, true, true);
|
||||
alloc_edge(&subpatch.edges[1], v1, v2, depth, true, true);
|
||||
alloc_edge(&subpatch.edges[2], v2, v3, depth, true, true);
|
||||
alloc_edge(&subpatch.edges[3], v3, v0, depth, true, true);
|
||||
|
||||
/* Forces a split in both axis for quads, needed to match split of ngons into quads. */
|
||||
subpatch.edges[0].edge->T = DSPLIT_NON_UNIFORM;
|
||||
subpatch.edges[3].edge->T = DSPLIT_NON_UNIFORM;
|
||||
subpatch.edges[2].edge->T = DSPLIT_NON_UNIFORM;
|
||||
subpatch.edges[1].edge->T = DSPLIT_NON_UNIFORM;
|
||||
|
||||
split_quad(std::move(subpatch));
|
||||
}
|
||||
|
||||
void DiagSplit::split_ngon(const Mesh::SubdFace &face,
|
||||
const int face_index,
|
||||
const Patch *patches,
|
||||
const size_t patches_byte_stride)
|
||||
{
|
||||
const int *subd_face_corners = params.mesh->get_subd_face_corners().data();
|
||||
const int v2 = alloc_verts(1);
|
||||
|
||||
const int depth = 0;
|
||||
|
||||
/* Allocate edges of n-gon. */
|
||||
array<SubPatch::Edge> edges(face.num_corners);
|
||||
for (int corner = 0; corner < face.num_corners; corner++) {
|
||||
const int v = subd_face_corners[face.start_corner + corner];
|
||||
const int vnext = subd_face_corners[face.start_corner + mod(corner + 1, face.num_corners)];
|
||||
|
||||
alloc_edge(&edges[corner], v, vnext, depth, true, true);
|
||||
if (edges[corner].edge->mid_vert_index == -1) {
|
||||
edges[corner].edge->mid_vert_index = alloc_verts(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate patches. */
|
||||
for (int corner = 0; corner < face.num_corners; corner++) {
|
||||
const Patch *patch = (const Patch *)(((char *)patches) + (corner * patches_byte_stride));
|
||||
|
||||
/* v_prev .
|
||||
* . .
|
||||
* . edge2 .
|
||||
* v3 ←------- v2 . . .
|
||||
* | ↑
|
||||
* edge3 | | edge1
|
||||
* ↓ |
|
||||
* v0 ------→ v1 . . v_next
|
||||
* edge0
|
||||
*/
|
||||
SubPatch::Edge &edge3 = edges[mod(corner + face.num_corners - 1, face.num_corners)];
|
||||
SubPatch::Edge &edge0 = edges[corner];
|
||||
|
||||
/* Setup edges. */
|
||||
const int v0 = edge0.start_vert_index();
|
||||
const int v1 = edge0.mid_vert_index();
|
||||
const int v3 = edge3.mid_vert_index();
|
||||
|
||||
SubPatch subpatch(patch, face_index, corner);
|
||||
alloc_edge(&subpatch.edges[0], v0, v1, depth, false, false);
|
||||
alloc_edge(&subpatch.edges[1], v1, v2, depth, true, false);
|
||||
alloc_edge(&subpatch.edges[2], v2, v3, depth, true, corner == 0);
|
||||
alloc_edge(&subpatch.edges[3], v3, v0, depth, false, false);
|
||||
|
||||
subpatch.edges[0].own_edge = edge0.own_edge;
|
||||
subpatch.edges[0].own_vertex = edge0.own_vertex;
|
||||
subpatch.edges[3].own_edge = edge3.own_edge;
|
||||
subpatch.edges[3].own_vertex = edge3.own_edge;
|
||||
|
||||
/* Perform split. */
|
||||
split_quad(std::move(subpatch));
|
||||
}
|
||||
}
|
||||
|
||||
void DiagSplit::split_patches(const Patch *patches, const size_t patches_byte_stride)
|
||||
{
|
||||
/* TODO: reuse edge factor vertex position computations. */
|
||||
/* TODO: support not splitting n-gons if not needed. */
|
||||
/* TODO: multi-threading. */
|
||||
|
||||
/* Keep base mesh vertices, create new triangles. */
|
||||
num_verts = params.mesh->get_num_subd_base_verts();
|
||||
num_triangles = 0;
|
||||
|
||||
owned_verts.resize(num_verts, false);
|
||||
|
||||
/* Split all faces in the mesh. */
|
||||
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) +
|
||||
(face.ptex_offset * patches_byte_stride));
|
||||
if (face.is_quad()) {
|
||||
split_quad(face, f, patch);
|
||||
}
|
||||
else {
|
||||
split_ngon(face, f, patch, patches_byte_stride);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
107
blender-5.2.0/intern/cycles/subd/split.h
Normal file
107
blender-5.2.0/intern/cycles/subd/split.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
/* DiagSplit: Parallel, Crack-free, Adaptive Tessellation for Micro-polygon Rendering
|
||||
* Splits up patches and determines edge tessellation factors for dicing. Patch
|
||||
* evaluation at arbitrary points is required for this to work. See the paper
|
||||
* for more details. */
|
||||
|
||||
#include "scene/mesh.h"
|
||||
|
||||
#include "subd/dice.h"
|
||||
#include "subd/subpatch.h"
|
||||
|
||||
#include "util/set.h"
|
||||
#include "util/types.h"
|
||||
#include "util/vector.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class Mesh;
|
||||
class Patch;
|
||||
class SubdAttributeInterpolation;
|
||||
|
||||
class DiagSplit {
|
||||
private:
|
||||
SubdParams params;
|
||||
vector<SubPatch> subpatches;
|
||||
vector<bool> owned_verts;
|
||||
unordered_set<SubEdge, SubEdge::Hash, SubEdge::Equal> edges;
|
||||
int num_verts = 0;
|
||||
int num_triangles = 0;
|
||||
|
||||
/* Allocate vertices, edges and subpatches. */
|
||||
int alloc_verts(const int num);
|
||||
SubEdge *alloc_edge(const int v0, const int v1, const int depth, bool &was_missing);
|
||||
void alloc_edge(SubPatch::Edge *sub_edge,
|
||||
const int v0,
|
||||
const int v1,
|
||||
const int depth,
|
||||
const bool want_to_own_edge,
|
||||
const bool want_to_own_vertex);
|
||||
void alloc_subpatch(SubPatch &&sub);
|
||||
|
||||
/* Compute edge factors. */
|
||||
float3 to_world(const Patch *patch, const float2 uv);
|
||||
std::pair<int, float> T(const Patch *patch,
|
||||
const float2 uv_start,
|
||||
const float2 uv_end,
|
||||
const int depth,
|
||||
const bool recursive_resolve = false);
|
||||
int limit_edge_factor(const Patch *patch,
|
||||
const float2 uv_start,
|
||||
const float2 uv_end,
|
||||
const int T);
|
||||
void assign_edge_factor(SubEdge *edge,
|
||||
const Patch *patch,
|
||||
const float2 uv_start,
|
||||
const float2 uv_end,
|
||||
const bool recursive_resolve = false);
|
||||
void resolve_edge_factors(const SubPatch &sub);
|
||||
|
||||
/* Split edge, subpatch, quad and n-gon. */
|
||||
float2 split_edge(const Patch *patch,
|
||||
SubPatch::Edge *subedge,
|
||||
SubPatch::Edge *subedge_a,
|
||||
SubPatch::Edge *subedge_b,
|
||||
float2 uv_start,
|
||||
float2 uv_end);
|
||||
void split_quad(SubPatch &&sub);
|
||||
void split_triangle(SubPatch &&sub);
|
||||
void split_quad_into_triangles(SubPatch &&sub);
|
||||
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);
|
||||
|
||||
public:
|
||||
explicit DiagSplit(const SubdParams ¶ms);
|
||||
|
||||
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
|
||||
310
blender-5.2.0/intern/cycles/subd/subpatch.h
Normal file
310
blender-5.2.0/intern/cycles/subd/subpatch.h
Normal file
@@ -0,0 +1,310 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/hash.h"
|
||||
#include "util/math_float2.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
class Patch;
|
||||
|
||||
enum {
|
||||
DSPLIT_NON_UNIFORM = -1,
|
||||
DSPLIT_MAX_DEPTH = 16,
|
||||
DSPLIT_MAX_SEGMENTS = 8,
|
||||
};
|
||||
|
||||
/* SubEdge */
|
||||
|
||||
struct SubEdge {
|
||||
SubEdge(const int start_vert_index, const int end_vert_index, const int depth)
|
||||
: start_vert_index(start_vert_index), end_vert_index(end_vert_index), depth(depth)
|
||||
{
|
||||
}
|
||||
|
||||
/* Vertex indices. */
|
||||
int start_vert_index;
|
||||
int end_vert_index;
|
||||
|
||||
/* If edge was split, vertex index in the middle. */
|
||||
int mid_vert_index = -1;
|
||||
|
||||
/* Number of segments the edge will be diced into, see DiagSplit paper. */
|
||||
int T = 0;
|
||||
|
||||
/* Estimated length of edge, for determining preferred split direction. */
|
||||
float length = 0.0f;
|
||||
|
||||
/* Index of the second vert from this edges corner along the edge towards the next corner. */
|
||||
int second_vert_index = -1;
|
||||
|
||||
/* How many times an edge was subdivided to get this edge. */
|
||||
int depth = 0;
|
||||
|
||||
SubEdge() = default;
|
||||
|
||||
int get_vert_along_edge(const int n) const
|
||||
{
|
||||
assert(n >= 0 && n <= T);
|
||||
|
||||
if (n == 0) {
|
||||
return start_vert_index;
|
||||
}
|
||||
if (n == T) {
|
||||
return end_vert_index;
|
||||
}
|
||||
|
||||
return second_vert_index + n - 1;
|
||||
}
|
||||
|
||||
bool must_split() const
|
||||
{
|
||||
return T == DSPLIT_NON_UNIFORM;
|
||||
}
|
||||
|
||||
struct Hash {
|
||||
size_t operator()(const SubEdge &edge) const
|
||||
{
|
||||
int a = edge.start_vert_index;
|
||||
int b = edge.end_vert_index;
|
||||
if (b > a) {
|
||||
std::swap(a, b);
|
||||
}
|
||||
return hash_uint2(a, b);
|
||||
}
|
||||
};
|
||||
|
||||
struct Equal {
|
||||
size_t operator()(const SubEdge &a, const SubEdge &b) const
|
||||
{
|
||||
return (a.start_vert_index == b.start_vert_index && a.end_vert_index == b.end_vert_index) ||
|
||||
(a.start_vert_index == b.end_vert_index && a.end_vert_index == b.start_vert_index);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/* SubPatch */
|
||||
|
||||
class SubPatch {
|
||||
public:
|
||||
/* Patch this is a subpatch of. */
|
||||
const Patch *patch = nullptr;
|
||||
/* Face and corner. */
|
||||
int face_index = 0;
|
||||
int corner = 0;
|
||||
/* Is a triangular patch instead of a quad patch? */
|
||||
enum { TRIANGLE, QUAD } shape = QUAD;
|
||||
/* 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 {
|
||||
SubEdge *edge;
|
||||
|
||||
/* Is the direction of this edge reverse compared to SubEdge? */
|
||||
bool reversed;
|
||||
|
||||
/* Is this subpatch responsible for owning attributes for the start vertex? */
|
||||
bool own_vertex;
|
||||
/* Is this subpatch responsible for owning attributes for edge vertices? */
|
||||
bool own_edge;
|
||||
|
||||
/* Get vertex indices in the direction of this patch edge, will take into
|
||||
* account the reversed flag to flip the indices. */
|
||||
int start_vert_index() const
|
||||
{
|
||||
return (reversed) ? edge->end_vert_index : edge->start_vert_index;
|
||||
}
|
||||
int mid_vert_index() const
|
||||
{
|
||||
return edge->mid_vert_index;
|
||||
}
|
||||
int end_vert_index() const
|
||||
{
|
||||
return (reversed) ? edge->start_vert_index : edge->end_vert_index;
|
||||
}
|
||||
|
||||
int get_vert_along_edge(const int n_relative) const
|
||||
{
|
||||
assert(n_relative >= 0 && n_relative <= edge->T);
|
||||
|
||||
const int n = (reversed) ? edge->T - n_relative : n_relative;
|
||||
|
||||
return edge->get_vert_along_edge(n);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* edge2
|
||||
* uv3 ←------------ uv2
|
||||
* | ↑
|
||||
* edge3 | | edge1
|
||||
* ↓ |
|
||||
* uv0 ------------→ uv1
|
||||
* edge0
|
||||
*
|
||||
* uv2
|
||||
* | \
|
||||
* | \
|
||||
* edge2 | \ edge1
|
||||
* | \
|
||||
* ↓ \
|
||||
* uv0 --→ uv1
|
||||
* edge0
|
||||
*/
|
||||
|
||||
/* UV within patch, counter-clockwise starting from uv (0, 0) towards (1, 0) etc. */
|
||||
float2 uvs[4] = {zero_float2(), make_float2(1.0f, 0.0f), one_float2(), make_float2(0.0f, 1.0f)};
|
||||
|
||||
/* Edges of this subpatch. */
|
||||
Edge edges[4] = {};
|
||||
|
||||
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
|
||||
{
|
||||
if (shape == TRIANGLE) {
|
||||
const int M = max(max(edges[0].edge->T, edges[1].edge->T), edges[2].edge->T);
|
||||
if (M <= 2) {
|
||||
/* No inner grid. */
|
||||
return 0;
|
||||
}
|
||||
/* 1 + 2 + .. + M-1 */
|
||||
return M * (M - 1) / 2;
|
||||
}
|
||||
|
||||
const int Mu = max(edges[0].edge->T, edges[2].edge->T);
|
||||
const int Mv = max(edges[3].edge->T, edges[1].edge->T);
|
||||
return (Mu - 1) * (Mv - 1);
|
||||
}
|
||||
|
||||
int calc_num_triangles() const
|
||||
{
|
||||
if (shape == TRIANGLE) {
|
||||
const int M = max(max(edges[0].edge->T, edges[1].edge->T), edges[2].edge->T);
|
||||
if (M == 1) {
|
||||
return 1;
|
||||
}
|
||||
if (M == 2) {
|
||||
return edges[0].edge->T + edges[1].edge->T + edges[2].edge->T - 2;
|
||||
}
|
||||
|
||||
const int inner_M = M - 2;
|
||||
const int inner_triangles = inner_M * inner_M;
|
||||
const int edge_triangles = edges[0].edge->T + edges[1].edge->T + edges[2].edge->T +
|
||||
inner_M * 3;
|
||||
return inner_triangles + edge_triangles;
|
||||
}
|
||||
|
||||
const int Mu = max(edges[0].edge->T, edges[2].edge->T);
|
||||
const int Mv = max(edges[3].edge->T, edges[1].edge->T);
|
||||
|
||||
if (Mu == 1) {
|
||||
return edges[3].edge->T + edges[1].edge->T;
|
||||
}
|
||||
if (Mv == 1) {
|
||||
return edges[0].edge->T + edges[2].edge->T;
|
||||
}
|
||||
|
||||
const int inner_triangles = (Mu - 2) * (Mv - 2) * 2;
|
||||
const int edge_triangles = edges[0].edge->T + edges[2].edge->T + edges[3].edge->T +
|
||||
edges[1].edge->T + ((Mu - 2) * 2) + ((Mv - 2) * 2);
|
||||
|
||||
return inner_triangles + edge_triangles;
|
||||
}
|
||||
|
||||
int get_vert_along_edge(const int edge, const int n) const
|
||||
{
|
||||
return edges[edge].get_vert_along_edge(n);
|
||||
}
|
||||
|
||||
int get_vert_along_edge_reverse(const int edge, const int n) const
|
||||
{
|
||||
return get_vert_along_edge(edge, edges[edge].edge->T - n);
|
||||
}
|
||||
|
||||
int get_inner_grid_vert_triangle(int i, int j) const
|
||||
{
|
||||
/* Rows `(1 + 2 + .. + j)`, and column `i`. */
|
||||
const int offset = j * (j + 1) / 2 + i;
|
||||
assert(offset < calc_num_inner_verts());
|
||||
return inner_grid_vert_offset + offset;
|
||||
}
|
||||
|
||||
int get_vert_along_grid_edge(const int edge, const int n) const
|
||||
{
|
||||
if (shape == TRIANGLE) {
|
||||
const int M = max(max(edges[0].edge->T, edges[1].edge->T), edges[2].edge->T);
|
||||
const int inner_M = M - 2;
|
||||
assert(M >= 2);
|
||||
|
||||
switch (edge) {
|
||||
case 0: {
|
||||
return get_inner_grid_vert_triangle(n, n);
|
||||
}
|
||||
case 1: {
|
||||
return get_inner_grid_vert_triangle(inner_M - n, inner_M);
|
||||
}
|
||||
case 2: {
|
||||
return get_inner_grid_vert_triangle(0, inner_M - n);
|
||||
}
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
const int Mu = max(edges[0].edge->T, edges[2].edge->T);
|
||||
const int Mv = max(edges[3].edge->T, edges[1].edge->T);
|
||||
|
||||
assert(Mu >= 2 && Mv >= 2);
|
||||
|
||||
switch (edge) {
|
||||
case 0: {
|
||||
return inner_grid_vert_offset + n;
|
||||
}
|
||||
case 1: {
|
||||
return inner_grid_vert_offset + (Mu - 2) + n * (Mu - 1);
|
||||
}
|
||||
case 2: {
|
||||
const int reverse_n = (Mu - 2) - n;
|
||||
return inner_grid_vert_offset + (Mu - 1) * (Mv - 2) + reverse_n;
|
||||
}
|
||||
case 3: {
|
||||
const int reverse_n = (Mv - 2) - n;
|
||||
return inner_grid_vert_offset + reverse_n * (Mu - 1);
|
||||
}
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
float2 map_uv(float2 uv) const
|
||||
{
|
||||
/* Map UV from subpatch to patch parametric coordinates. */
|
||||
if (shape == TRIANGLE) {
|
||||
return clamp((1.0f - uv.x - uv.y) * uvs[0] + uv.x * uvs[1] + uv.y * uvs[2],
|
||||
zero_float2(),
|
||||
one_float2());
|
||||
}
|
||||
|
||||
const float2 d0 = interp(uvs[0], uvs[3], uv.y);
|
||||
const float2 d1 = interp(uvs[1], uvs[2], uv.y);
|
||||
return clamp(interp(d0, d1, uv.x), zero_float2(), one_float2());
|
||||
}
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
Reference in New Issue
Block a user