mirror of
https://github.com/blender/blender
synced 2026-09-29 04:37:17 +03:00
Refactor: Cycles: Tweaks to adaptive subdivision dicing
* Move dicing out of DiagSplit, caller now uses EdgeDice * Merge, rename and reorder various EdgeDice functions * Compute triangle indices for subpatches in advance Pull Request: https://projects.blender.org/blender/blender/pulls/136411
This commit is contained in:
parent
17fbfbc2d5
commit
4e16c4bd7d
8 changed files with 357 additions and 351 deletions
|
|
@ -792,8 +792,7 @@ void GeometryManager::device_update(Device *device,
|
|||
subd_params.objecttoworld = mesh->get_subd_objecttoworld();
|
||||
subd_params.camera = dicing_camera;
|
||||
|
||||
DiagSplit dsplit(subd_params);
|
||||
mesh->tessellate(&dsplit);
|
||||
mesh->tessellate(subd_params);
|
||||
|
||||
i++;
|
||||
|
||||
|
|
|
|||
|
|
@ -225,7 +225,7 @@ class Mesh : public Geometry {
|
|||
bool has_motion_blur() const override;
|
||||
PrimitiveType primitive_type() const override;
|
||||
|
||||
void tessellate(DiagSplit *split);
|
||||
void tessellate(SubdParams ¶ms);
|
||||
|
||||
SubdFace get_subd_face(const size_t index) const;
|
||||
size_t get_num_subd_faces() const
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
void Mesh::tessellate(DiagSplit *split)
|
||||
void Mesh::tessellate(SubdParams ¶ms)
|
||||
{
|
||||
/* reset the number of subdivision vertices, in case the Mesh was not cleared
|
||||
* between calls or data updates */
|
||||
|
|
@ -79,7 +79,12 @@ void Mesh::tessellate(DiagSplit *split)
|
|||
}
|
||||
|
||||
/* Split patches. */
|
||||
split->split_patches(osd_patches.data(), sizeof(OsdPatch));
|
||||
DiagSplit split(params);
|
||||
split.split_patches(osd_patches.data(), sizeof(OsdPatch));
|
||||
|
||||
/* Dice patches. */
|
||||
EdgeDice dice(params, split.get_num_verts(), split.get_num_triangles());
|
||||
dice.dice(split);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
|
@ -138,7 +143,12 @@ void Mesh::tessellate(DiagSplit *split)
|
|||
}
|
||||
|
||||
/* Split patches. */
|
||||
split->split_patches(linear_patches.data(), sizeof(LinearQuadPatch));
|
||||
DiagSplit split(params);
|
||||
split.split_patches(linear_patches.data(), sizeof(LinearQuadPatch));
|
||||
|
||||
/* Dice patches. */
|
||||
EdgeDice dice(params, split.get_num_verts(), split.get_num_triangles());
|
||||
dice.dice(split);
|
||||
}
|
||||
|
||||
if (get_num_subd_faces()) {
|
||||
|
|
|
|||
|
|
@ -7,244 +7,38 @@
|
|||
|
||||
#include "subd/dice.h"
|
||||
#include "subd/patch.h"
|
||||
#include "subd/split.h"
|
||||
|
||||
#include "util/tbb.h"
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
/* EdgeDice Base */
|
||||
|
||||
EdgeDice::EdgeDice(const SubdParams ¶ms_) : params(params_)
|
||||
{
|
||||
params.mesh->attributes.add(ATTR_STD_VERTEX_NORMAL);
|
||||
|
||||
if (params.ptex) {
|
||||
params.mesh->attributes.add(ATTR_STD_PTEX_FACE_ID);
|
||||
params.mesh->attributes.add(ATTR_STD_PTEX_UV);
|
||||
}
|
||||
}
|
||||
|
||||
void EdgeDice::reserve(const int num_verts, const int num_triangles)
|
||||
EdgeDice::EdgeDice(const SubdParams ¶ms_, const int num_verts, const int num_triangles)
|
||||
: params(params_)
|
||||
{
|
||||
Mesh *mesh = params.mesh;
|
||||
|
||||
mesh->num_subd_added_verts = num_verts - mesh->get_verts().size();
|
||||
|
||||
mesh->resize_mesh(num_verts, 0);
|
||||
mesh->reserve_mesh(num_verts, num_triangles);
|
||||
mesh->resize_mesh(num_verts, num_triangles);
|
||||
|
||||
Attribute *attr_vN = mesh->attributes.add(ATTR_STD_VERTEX_NORMAL);
|
||||
|
||||
mesh_triangles = mesh->triangles.data();
|
||||
mesh_shader = mesh->shader.data();
|
||||
mesh_smooth = mesh->smooth.data();
|
||||
mesh_P = mesh->verts.data();
|
||||
mesh_N = attr_vN->data_float3();
|
||||
|
||||
Attribute *attr_ptex_face_id = mesh->attributes.find(ATTR_STD_PTEX_FACE_ID);
|
||||
if (attr_ptex_face_id) {
|
||||
if (params.ptex) {
|
||||
Attribute *attr_ptex_face_id = params.mesh->attributes.add(ATTR_STD_PTEX_FACE_ID);
|
||||
Attribute *attr_ptex_uv = params.mesh->attributes.add(ATTR_STD_PTEX_UV);
|
||||
|
||||
mesh_ptex_face_id = attr_ptex_face_id->data_float();
|
||||
}
|
||||
Attribute *attr_ptex_uv = mesh->attributes.find(ATTR_STD_PTEX_UV);
|
||||
if (attr_ptex_uv) {
|
||||
mesh_ptex_uv = attr_ptex_uv->data_float2();
|
||||
}
|
||||
}
|
||||
|
||||
void EdgeDice::set_vert(const Patch *patch, const int index, const float2 uv)
|
||||
{
|
||||
float3 P;
|
||||
float3 N;
|
||||
|
||||
patch->eval(&P, nullptr, nullptr, &N, uv.x, uv.y);
|
||||
|
||||
assert(index < params.mesh->verts.size());
|
||||
|
||||
mesh_P[index] = P;
|
||||
mesh_N[index] = N;
|
||||
}
|
||||
|
||||
void EdgeDice::add_triangle(const Patch *patch,
|
||||
const int v0,
|
||||
const int v1,
|
||||
const int v2,
|
||||
const float2 uv0,
|
||||
const float2 uv1,
|
||||
const float2 uv2)
|
||||
{
|
||||
Mesh *mesh = params.mesh;
|
||||
|
||||
mesh->add_triangle(v0, v1, v2, patch->shader, true);
|
||||
|
||||
const int triangle_offset = params.mesh->num_triangles() - 1;
|
||||
|
||||
params.mesh->subd_triangle_patch_index[triangle_offset] = patch->patch_index;
|
||||
if (mesh_ptex_face_id) {
|
||||
mesh_ptex_face_id[triangle_offset] = patch->patch_index;
|
||||
}
|
||||
|
||||
params.mesh->subd_corner_patch_uv[(triangle_offset * 3) + 0] = uv0;
|
||||
params.mesh->subd_corner_patch_uv[(triangle_offset * 3) + 1] = uv1;
|
||||
params.mesh->subd_corner_patch_uv[(triangle_offset * 3) + 2] = uv2;
|
||||
|
||||
if (mesh_ptex_uv) {
|
||||
mesh_ptex_uv[(triangle_offset * 3) + 0] = uv0;
|
||||
mesh_ptex_uv[(triangle_offset * 3) + 0] = uv1;
|
||||
mesh_ptex_uv[(triangle_offset * 3) + 0] = uv2;
|
||||
}
|
||||
}
|
||||
|
||||
void EdgeDice::stitch_triangles_to_inner_grid(SubPatch &sub,
|
||||
const int edge,
|
||||
const int Mu,
|
||||
const int Mv)
|
||||
{
|
||||
const int outer_T = sub.edges[edge].edge->T;
|
||||
const int inner_T = ((edge % 2) == 0) ? Mu - 2 : Mv - 2;
|
||||
|
||||
const float du = 1.0f / (float)Mu;
|
||||
const float dv = 1.0f / (float)Mv;
|
||||
float2 inner_uv, outer_uv, inner_uv_step, outer_uv_step;
|
||||
switch (edge) {
|
||||
case 0:
|
||||
inner_uv = make_float2(du, dv);
|
||||
outer_uv = make_float2(0.0f, 0.0f);
|
||||
inner_uv_step = make_float2(du, 0.0f);
|
||||
outer_uv_step = make_float2(1.0f / (float)outer_T, 0.0f);
|
||||
break;
|
||||
case 1:
|
||||
inner_uv = make_float2(1.0f - du, dv);
|
||||
outer_uv = make_float2(1.0f, 0.0f);
|
||||
inner_uv_step = make_float2(0.0f, dv);
|
||||
outer_uv_step = make_float2(0.0f, 1.0f / (float)outer_T);
|
||||
break;
|
||||
case 2:
|
||||
inner_uv = make_float2(1.0f - du, 1.0f - dv);
|
||||
outer_uv = make_float2(1.0f, 1.0f);
|
||||
inner_uv_step = make_float2(-du, 0.0f);
|
||||
outer_uv_step = make_float2(-1.0f / (float)outer_T, 0.0f);
|
||||
break;
|
||||
case 3:
|
||||
default:
|
||||
inner_uv = make_float2(du, 1.0f - dv);
|
||||
outer_uv = make_float2(0.0f, 1.0f);
|
||||
inner_uv_step = make_float2(0.0f, -dv);
|
||||
outer_uv_step = make_float2(0.0f, -1.0f / (float)outer_T);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Stitch together two arrays of verts with triangles. at each step, we compare using the next
|
||||
* verts on both sides, to find the split direction with the smallest diagonal, and use that
|
||||
* in order to keep the triangle shape reasonable. */
|
||||
for (size_t i = 0, j = 0; i < inner_T || j < outer_T;) {
|
||||
const int v0 = sub.get_vert_along_grid_edge(edge, i);
|
||||
const int v1 = sub.get_vert_along_edge(edge, j);
|
||||
int v2;
|
||||
|
||||
const float2 uv0 = sub.map_uv(inner_uv);
|
||||
const float2 uv1 = sub.map_uv(outer_uv);
|
||||
float2 uv2;
|
||||
|
||||
if (j == outer_T) {
|
||||
v2 = sub.get_vert_along_grid_edge(edge, ++i);
|
||||
inner_uv += inner_uv_step;
|
||||
uv2 = sub.map_uv(inner_uv);
|
||||
}
|
||||
else if (i == inner_T) {
|
||||
v2 = sub.get_vert_along_edge(edge, ++j);
|
||||
outer_uv += outer_uv_step;
|
||||
uv2 = sub.map_uv(outer_uv);
|
||||
}
|
||||
else {
|
||||
/* Length of diagonals. */
|
||||
const float len1 = len_squared(mesh_P[sub.get_vert_along_grid_edge(edge, i)] -
|
||||
mesh_P[sub.get_vert_along_edge(edge, j + 1)]);
|
||||
const float len2 = len_squared(mesh_P[sub.get_vert_along_edge(edge, j)] -
|
||||
mesh_P[sub.get_vert_along_grid_edge(edge, i + 1)]);
|
||||
|
||||
/* Use smallest diagonal. */
|
||||
if (len1 < len2) {
|
||||
v2 = sub.get_vert_along_edge(edge, ++j);
|
||||
outer_uv += outer_uv_step;
|
||||
uv2 = sub.map_uv(outer_uv);
|
||||
}
|
||||
else {
|
||||
v2 = sub.get_vert_along_grid_edge(edge, ++i);
|
||||
inner_uv += inner_uv_step;
|
||||
uv2 = sub.map_uv(inner_uv);
|
||||
}
|
||||
}
|
||||
|
||||
add_triangle(sub.patch, v0, v1, v2, uv0, uv1, uv2);
|
||||
}
|
||||
}
|
||||
|
||||
void EdgeDice::stitch_triangles_across(SubPatch &sub, const int left_edge, const int right_edge)
|
||||
{
|
||||
/* Stitch triangles from side to side, edge in the other direction has T = 1. */
|
||||
const int left_T = sub.edges[left_edge].edge->T;
|
||||
const int right_T = sub.edges[right_edge].edge->T;
|
||||
|
||||
float2 left_uv, right_uv, left_uv_step, right_uv_step;
|
||||
if (right_edge == 0) {
|
||||
left_uv = make_float2(0.0f, 1.0f);
|
||||
right_uv = make_float2(0.0f, 0.0f);
|
||||
left_uv_step = make_float2(1.0f / (float)left_T, 0.0f);
|
||||
right_uv_step = make_float2(1.0f / (float)right_T, 0.0f);
|
||||
}
|
||||
else {
|
||||
left_uv = make_float2(0.0f, 0.0f);
|
||||
right_uv = make_float2(1.0f, 0.0f);
|
||||
left_uv_step = make_float2(0.0f, 1.0f / (float)left_T);
|
||||
right_uv_step = make_float2(0.0f, 1.0f / (float)right_T);
|
||||
}
|
||||
|
||||
/* Stitch together two arrays of verts with triangles. at each step, we compare using the next
|
||||
* verts on both sides, to find the split direction with the smallest diagonal, and use that
|
||||
* in order to keep the triangle shape reasonable. */
|
||||
for (size_t i = 0, j = 0; i < left_T || j < right_T;) {
|
||||
const int v0 = sub.get_vert_along_edge_reverse(left_edge, i);
|
||||
const int v1 = sub.get_vert_along_edge(right_edge, j);
|
||||
int v2;
|
||||
|
||||
const float2 uv0 = sub.map_uv(left_uv);
|
||||
const float2 uv1 = sub.map_uv(right_uv);
|
||||
float2 uv2;
|
||||
|
||||
if (j == right_T) {
|
||||
v2 = sub.get_vert_along_edge_reverse(left_edge, ++i);
|
||||
left_uv += left_uv_step;
|
||||
uv2 = sub.map_uv(left_uv);
|
||||
}
|
||||
else if (i == left_T) {
|
||||
v2 = sub.get_vert_along_edge(right_edge, ++j);
|
||||
right_uv += right_uv_step;
|
||||
uv2 = sub.map_uv(right_uv);
|
||||
}
|
||||
else {
|
||||
/* Length of diagonals. */
|
||||
const float len1 = len_squared(mesh_P[sub.get_vert_along_edge_reverse(left_edge, i)] -
|
||||
mesh_P[sub.get_vert_along_edge(right_edge, j + 1)]);
|
||||
const float len2 = len_squared(mesh_P[sub.get_vert_along_edge(right_edge, j)] -
|
||||
mesh_P[sub.get_vert_along_edge_reverse(left_edge, i + 1)]);
|
||||
|
||||
/* Use smallest diagonal. */
|
||||
if (len1 < len2) {
|
||||
v2 = sub.get_vert_along_edge(right_edge, ++j);
|
||||
right_uv += right_uv_step;
|
||||
uv2 = sub.map_uv(right_uv);
|
||||
}
|
||||
else {
|
||||
v2 = sub.get_vert_along_edge_reverse(left_edge, ++i);
|
||||
left_uv += left_uv_step;
|
||||
uv2 = sub.map_uv(left_uv);
|
||||
}
|
||||
}
|
||||
|
||||
add_triangle(sub.patch, v0, v1, v2, uv0, uv1, uv2);
|
||||
}
|
||||
}
|
||||
|
||||
/* QuadDice */
|
||||
|
||||
QuadDice::QuadDice(const SubdParams ¶ms_) : EdgeDice(params_) {}
|
||||
|
||||
float3 QuadDice::eval_projected(SubPatch &sub, const float2 uv)
|
||||
float3 EdgeDice::eval_projected(const SubPatch &sub, const float2 uv)
|
||||
{
|
||||
float3 P;
|
||||
|
||||
|
|
@ -256,48 +50,12 @@ float3 QuadDice::eval_projected(SubPatch &sub, const float2 uv)
|
|||
return P;
|
||||
}
|
||||
|
||||
void QuadDice::set_vert(SubPatch &sub, const int index, const float2 uv)
|
||||
{
|
||||
EdgeDice::set_vert(sub.patch, index, sub.map_uv(uv));
|
||||
}
|
||||
|
||||
void QuadDice::set_side(SubPatch &sub, const int edge)
|
||||
{
|
||||
const int t = sub.edges[edge].edge->T;
|
||||
const int i_start = (sub.edges[edge].own_vertex) ? 0 : 1;
|
||||
const int i_end = (sub.edges[edge].own_edge) ? t : 1;
|
||||
|
||||
/* set verts on the edge of the patch */
|
||||
for (int i = i_start; i < i_end; i++) {
|
||||
const float f = i / (float)t;
|
||||
|
||||
float2 uv;
|
||||
switch (edge) {
|
||||
case 0:
|
||||
uv = make_float2(f, 0.0f);
|
||||
break;
|
||||
case 1:
|
||||
uv = make_float2(1.0f, f);
|
||||
break;
|
||||
case 2:
|
||||
uv = make_float2(1.0f - f, 1.0f);
|
||||
break;
|
||||
case 3:
|
||||
default:
|
||||
uv = make_float2(0.0f, 1.0f - f);
|
||||
break;
|
||||
}
|
||||
|
||||
set_vert(sub, sub.get_vert_along_edge(edge, i), uv);
|
||||
}
|
||||
}
|
||||
|
||||
float QuadDice::quad_area(const float3 &a, const float3 &b, const float3 &c, const float3 &d)
|
||||
float EdgeDice::quad_area(const float3 &a, const float3 &b, const float3 &c, const float3 &d)
|
||||
{
|
||||
return triangle_area(a, b, d) + triangle_area(a, d, c);
|
||||
}
|
||||
|
||||
float QuadDice::scale_factor(SubPatch &sub, const int Mu, const int Mv)
|
||||
float EdgeDice::scale_factor(const SubPatch &sub, const int Mu, const int Mv)
|
||||
{
|
||||
/* estimate area as 4x largest of 4 quads */
|
||||
float3 P[3][3];
|
||||
|
|
@ -329,81 +87,311 @@ float QuadDice::scale_factor(SubPatch &sub, const int Mu, const int Mv)
|
|||
return S;
|
||||
}
|
||||
|
||||
void QuadDice::add_grid(SubPatch &sub, const int Mu, const int Mv, const int offset)
|
||||
void EdgeDice::set_vertex(const SubPatch &sub, const int index, const float2 uv)
|
||||
{
|
||||
/* no inner grid? */
|
||||
if (Mu == 1 || Mv == 1) {
|
||||
return;
|
||||
assert(index < params.mesh->verts.size());
|
||||
|
||||
float3 P;
|
||||
float3 N;
|
||||
|
||||
sub.patch->eval(&P, nullptr, nullptr, &N, uv.x, uv.y);
|
||||
|
||||
mesh_P[index] = P;
|
||||
mesh_N[index] = N;
|
||||
}
|
||||
|
||||
void EdgeDice::add_triangle(const SubPatch &sub,
|
||||
const int triangle_index,
|
||||
const int v0,
|
||||
const int v1,
|
||||
const int v2,
|
||||
const float2 uv0,
|
||||
const float2 uv1,
|
||||
const float2 uv2)
|
||||
{
|
||||
assert(triangle_index * 3 < params.mesh->triangles.size());
|
||||
|
||||
const Patch *patch = sub.patch;
|
||||
|
||||
mesh_triangles[triangle_index * 3 + 0] = v0;
|
||||
mesh_triangles[triangle_index * 3 + 1] = v1;
|
||||
mesh_triangles[triangle_index * 3 + 2] = v2;
|
||||
mesh_shader[triangle_index] = patch->shader;
|
||||
mesh_smooth[triangle_index] = true;
|
||||
|
||||
params.mesh->subd_triangle_patch_index[triangle_index] = patch->patch_index;
|
||||
if (mesh_ptex_face_id) {
|
||||
mesh_ptex_face_id[triangle_index] = patch->patch_index;
|
||||
}
|
||||
|
||||
/* create inner grid */
|
||||
params.mesh->subd_corner_patch_uv[triangle_index * 3 + 0] = uv0;
|
||||
params.mesh->subd_corner_patch_uv[triangle_index * 3 + 1] = uv1;
|
||||
params.mesh->subd_corner_patch_uv[triangle_index * 3 + 2] = uv2;
|
||||
|
||||
if (mesh_ptex_uv) {
|
||||
mesh_ptex_uv[triangle_index * 3 + 0] = uv0;
|
||||
mesh_ptex_uv[triangle_index * 3 + 0] = uv1;
|
||||
mesh_ptex_uv[triangle_index * 3 + 0] = uv2;
|
||||
}
|
||||
}
|
||||
|
||||
void EdgeDice::add_grid_triangles_and_stitch(const SubPatch &sub, const int Mu, const int Mv)
|
||||
{
|
||||
const float du = 1.0f / (float)Mu;
|
||||
const float dv = 1.0f / (float)Mv;
|
||||
const int grid_vertex_offset = sub.inner_grid_vert_offset;
|
||||
int triangle_index = sub.triangles_offset;
|
||||
|
||||
/* Create inner grid. */
|
||||
for (int j = 1; j < Mv; j++) {
|
||||
for (int i = 1; i < Mu; i++) {
|
||||
const float u = i * du;
|
||||
const float v = j * dv;
|
||||
const int center_i = offset + (i - 1) + (j - 1) * (Mu - 1);
|
||||
const int center_i = grid_vertex_offset + (i - 1) + (j - 1) * (Mu - 1);
|
||||
|
||||
set_vert(sub, center_i, make_float2(u, v));
|
||||
set_vertex(sub, center_i, sub.map_uv(make_float2(u, v)));
|
||||
|
||||
if (i < Mu - 1 && j < Mv - 1) {
|
||||
const int i1 = offset + (i - 1) + (j - 1) * (Mu - 1);
|
||||
const int i2 = offset + i + (j - 1) * (Mu - 1);
|
||||
const int i3 = offset + i + j * (Mu - 1);
|
||||
const int i4 = offset + (i - 1) + j * (Mu - 1);
|
||||
const int i1 = grid_vertex_offset + (i - 1) + (j - 1) * (Mu - 1);
|
||||
const int i2 = grid_vertex_offset + i + (j - 1) * (Mu - 1);
|
||||
const int i3 = grid_vertex_offset + i + j * (Mu - 1);
|
||||
const int i4 = grid_vertex_offset + (i - 1) + j * (Mu - 1);
|
||||
|
||||
const float2 uv1 = sub.map_uv(make_float2(u, v));
|
||||
const float2 uv2 = sub.map_uv(make_float2(u + du, v));
|
||||
const float2 uv3 = sub.map_uv(make_float2(u + du, v + dv));
|
||||
const float2 uv4 = sub.map_uv(make_float2(u, v + dv));
|
||||
|
||||
add_triangle(sub.patch, i1, i2, i3, uv1, uv2, uv3);
|
||||
add_triangle(sub.patch, i1, i3, i4, uv1, uv3, uv4);
|
||||
add_triangle(sub, triangle_index++, i1, i2, i3, uv1, uv2, uv3);
|
||||
add_triangle(sub, triangle_index++, i1, i3, i4, uv1, uv3, uv4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Stitch inner grid to edges. */
|
||||
for (int edge = 0; edge < 4; edge++) {
|
||||
const int outer_T = sub.edges[edge].edge->T;
|
||||
const int inner_T = ((edge % 2) == 0) ? Mu - 2 : Mv - 2;
|
||||
|
||||
float2 inner_uv, outer_uv, inner_uv_step, outer_uv_step;
|
||||
switch (edge) {
|
||||
case 0:
|
||||
inner_uv = make_float2(du, dv);
|
||||
outer_uv = make_float2(0.0f, 0.0f);
|
||||
inner_uv_step = make_float2(du, 0.0f);
|
||||
outer_uv_step = make_float2(1.0f / (float)outer_T, 0.0f);
|
||||
break;
|
||||
case 1:
|
||||
inner_uv = make_float2(1.0f - du, dv);
|
||||
outer_uv = make_float2(1.0f, 0.0f);
|
||||
inner_uv_step = make_float2(0.0f, dv);
|
||||
outer_uv_step = make_float2(0.0f, 1.0f / (float)outer_T);
|
||||
break;
|
||||
case 2:
|
||||
inner_uv = make_float2(1.0f - du, 1.0f - dv);
|
||||
outer_uv = make_float2(1.0f, 1.0f);
|
||||
inner_uv_step = make_float2(-du, 0.0f);
|
||||
outer_uv_step = make_float2(-1.0f / (float)outer_T, 0.0f);
|
||||
break;
|
||||
case 3:
|
||||
default:
|
||||
inner_uv = make_float2(du, 1.0f - dv);
|
||||
outer_uv = make_float2(0.0f, 1.0f);
|
||||
inner_uv_step = make_float2(0.0f, -dv);
|
||||
outer_uv_step = make_float2(0.0f, -1.0f / (float)outer_T);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Stitch together two arrays of verts with triangles. At each step, we compare using
|
||||
* the next verts on both sides, to find the split direction with the smallest
|
||||
* diagonal, and use that in order to keep the triangle shape reasonable. */
|
||||
for (size_t i = 0, j = 0; i < inner_T || j < outer_T;) {
|
||||
const int v0 = sub.get_vert_along_grid_edge(edge, i);
|
||||
const int v1 = sub.get_vert_along_edge(edge, j);
|
||||
int v2;
|
||||
|
||||
const float2 uv0 = sub.map_uv(inner_uv);
|
||||
const float2 uv1 = sub.map_uv(outer_uv);
|
||||
float2 uv2;
|
||||
|
||||
if (j == outer_T) {
|
||||
v2 = sub.get_vert_along_grid_edge(edge, ++i);
|
||||
inner_uv += inner_uv_step;
|
||||
uv2 = sub.map_uv(inner_uv);
|
||||
}
|
||||
else if (i == inner_T) {
|
||||
v2 = sub.get_vert_along_edge(edge, ++j);
|
||||
outer_uv += outer_uv_step;
|
||||
uv2 = sub.map_uv(outer_uv);
|
||||
}
|
||||
else {
|
||||
/* Length of diagonals. */
|
||||
const int v2_a = sub.get_vert_along_edge(edge, j + 1);
|
||||
const int v2_b = sub.get_vert_along_grid_edge(edge, i + 1);
|
||||
|
||||
const float len_a = len_squared(mesh_P[v0] - mesh_P[v2_a]);
|
||||
const float len_b = len_squared(mesh_P[v1] - mesh_P[v2_b]);
|
||||
|
||||
/* Use smallest diagonal. */
|
||||
if (len_a < len_b) {
|
||||
v2 = v2_a;
|
||||
outer_uv += outer_uv_step;
|
||||
uv2 = sub.map_uv(outer_uv);
|
||||
j++;
|
||||
}
|
||||
else {
|
||||
v2 = v2_b;
|
||||
inner_uv += inner_uv_step;
|
||||
uv2 = sub.map_uv(inner_uv);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
add_triangle(sub, triangle_index++, v0, v1, v2, uv0, uv1, uv2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QuadDice::dice(SubPatch &sub)
|
||||
void EdgeDice::add_triangle_strip(const SubPatch &sub, const int left_edge, const int right_edge)
|
||||
{
|
||||
/* Stitch triangles from side to side, edge in the other direction has T = 1. */
|
||||
const int left_T = sub.edges[left_edge].edge->T;
|
||||
const int right_T = sub.edges[right_edge].edge->T;
|
||||
|
||||
float2 left_uv, right_uv, left_uv_step, right_uv_step;
|
||||
if (right_edge == 0) {
|
||||
left_uv = make_float2(0.0f, 1.0f);
|
||||
right_uv = make_float2(0.0f, 0.0f);
|
||||
left_uv_step = make_float2(1.0f / (float)left_T, 0.0f);
|
||||
right_uv_step = make_float2(1.0f / (float)right_T, 0.0f);
|
||||
}
|
||||
else {
|
||||
left_uv = make_float2(0.0f, 0.0f);
|
||||
right_uv = make_float2(1.0f, 0.0f);
|
||||
left_uv_step = make_float2(0.0f, 1.0f / (float)left_T);
|
||||
right_uv_step = make_float2(0.0f, 1.0f / (float)right_T);
|
||||
}
|
||||
|
||||
/* Stitch together two arrays of verts with triangles. at each step, we compare using the next
|
||||
* verts on both sides, to find the split direction with the smallest diagonal, and use that
|
||||
* in order to keep the triangle shape reasonable. */
|
||||
int triangle_index = sub.triangles_offset;
|
||||
|
||||
for (size_t i = 0, j = 0; i < left_T || j < right_T;) {
|
||||
const int v0 = sub.get_vert_along_edge_reverse(left_edge, i);
|
||||
const int v1 = sub.get_vert_along_edge(right_edge, j);
|
||||
int v2;
|
||||
|
||||
const float2 uv0 = sub.map_uv(left_uv);
|
||||
const float2 uv1 = sub.map_uv(right_uv);
|
||||
float2 uv2;
|
||||
|
||||
if (j == right_T) {
|
||||
v2 = sub.get_vert_along_edge_reverse(left_edge, ++i);
|
||||
left_uv += left_uv_step;
|
||||
uv2 = sub.map_uv(left_uv);
|
||||
}
|
||||
else if (i == left_T) {
|
||||
v2 = sub.get_vert_along_edge(right_edge, ++j);
|
||||
right_uv += right_uv_step;
|
||||
uv2 = sub.map_uv(right_uv);
|
||||
}
|
||||
else {
|
||||
/* Length of diagonals. */
|
||||
const int v2_a = sub.get_vert_along_edge(right_edge, j + 1);
|
||||
const int v2_b = sub.get_vert_along_edge_reverse(left_edge, i + 1);
|
||||
|
||||
const float len_a = len_squared(mesh_P[v0] - mesh_P[v2_a]);
|
||||
const float len_b = len_squared(mesh_P[v1] - mesh_P[v2_b]);
|
||||
|
||||
/* Use smallest diagonal. */
|
||||
if (len_a < len_b) {
|
||||
v2 = v2_a;
|
||||
right_uv += right_uv_step;
|
||||
uv2 = sub.map_uv(right_uv);
|
||||
j++;
|
||||
}
|
||||
else {
|
||||
v2 = v2_b;
|
||||
left_uv += left_uv_step;
|
||||
uv2 = sub.map_uv(left_uv);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
add_triangle(sub, triangle_index++, v0, v1, v2, uv0, uv1, uv2);
|
||||
}
|
||||
}
|
||||
|
||||
void EdgeDice::set_sides(const SubPatch &sub)
|
||||
{
|
||||
for (int edge = 0; edge < 4; edge++) {
|
||||
const int t = sub.edges[edge].edge->T;
|
||||
const int i_start = (sub.edges[edge].own_vertex) ? 0 : 1;
|
||||
const int i_end = (sub.edges[edge].own_edge) ? t : 1;
|
||||
|
||||
/* set verts on the edge of the patch */
|
||||
for (int i = i_start; i < i_end; i++) {
|
||||
const float f = i / (float)t;
|
||||
|
||||
float2 uv;
|
||||
switch (edge) {
|
||||
case 0:
|
||||
uv = make_float2(f, 0.0f);
|
||||
break;
|
||||
case 1:
|
||||
uv = make_float2(1.0f, f);
|
||||
break;
|
||||
case 2:
|
||||
uv = make_float2(1.0f - f, 1.0f);
|
||||
break;
|
||||
case 3:
|
||||
default:
|
||||
uv = make_float2(0.0f, 1.0f - f);
|
||||
break;
|
||||
}
|
||||
|
||||
const int vert_index = sub.get_vert_along_edge(edge, i);
|
||||
set_vertex(sub, vert_index, sub.map_uv(uv));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EdgeDice::dice(const SubPatch &sub)
|
||||
{
|
||||
/* Compute inner grid size with scale factor. */
|
||||
const int Mu = max(sub.edge_u0.edge->T, sub.edge_u1.edge->T);
|
||||
const int Mv = max(sub.edge_v0.edge->T, sub.edge_v1.edge->T);
|
||||
|
||||
/* Vertex coordinates for sides. */
|
||||
set_side(sub, 0);
|
||||
set_side(sub, 1);
|
||||
set_side(sub, 2);
|
||||
set_side(sub, 3);
|
||||
set_sides(sub);
|
||||
|
||||
if (Mv == 1) {
|
||||
/* No inner grid, stitch triangles from side to side. */
|
||||
stitch_triangles_across(sub, 2, 0);
|
||||
add_triangle_strip(sub, 2, 0);
|
||||
}
|
||||
else if (Mu == 1) {
|
||||
/* No inner grid, stitch triangles from side to side. */
|
||||
stitch_triangles_across(sub, 3, 1);
|
||||
add_triangle_strip(sub, 3, 1);
|
||||
}
|
||||
else {
|
||||
#if 0 /* Doesn't work very well, especially at grazing angles. */
|
||||
#if 0
|
||||
/* Doesn't work very well, especially at grazing angles. */
|
||||
const float S = scale_factor(sub, ef, Mu, Mv);
|
||||
#else
|
||||
const float S = 1.0f;
|
||||
#endif
|
||||
|
||||
const int grid_Mu = max((int)ceilf(S * Mu), 1); // XXX handle 0 & 1?
|
||||
const int grid_Mv = max((int)ceilf(S * Mv), 1); // XXX handle 0 & 1?
|
||||
add_grid_triangles_and_stitch(sub, grid_Mu, grid_Mv);
|
||||
#else
|
||||
add_grid_triangles_and_stitch(sub, Mu, Mv);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Inner grid. */
|
||||
add_grid(sub, grid_Mu, grid_Mv, sub.inner_grid_vert_offset);
|
||||
|
||||
/* Stitch triangles to inner grid. */
|
||||
stitch_triangles_to_inner_grid(sub, 0, Mu, Mv);
|
||||
stitch_triangles_to_inner_grid(sub, 1, Mu, Mv);
|
||||
stitch_triangles_to_inner_grid(sub, 2, Mu, Mv);
|
||||
stitch_triangles_to_inner_grid(sub, 3, Mu, Mv);
|
||||
void EdgeDice::dice(const DiagSplit &split)
|
||||
{
|
||||
const size_t num_subpatches = split.get_num_subpatches();
|
||||
for (size_t i = 0; i < num_subpatches; i++) {
|
||||
dice(split.get_subpatch(i));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ CCL_NAMESPACE_BEGIN
|
|||
class Camera;
|
||||
class Mesh;
|
||||
class Patch;
|
||||
class DiagSplit;
|
||||
|
||||
struct SubdParams {
|
||||
Mesh *mesh = nullptr;
|
||||
|
|
@ -34,23 +35,27 @@ struct SubdParams {
|
|||
SubdParams(Mesh *mesh_, bool ptex_ = false) : mesh(mesh_), ptex(ptex_) {}
|
||||
};
|
||||
|
||||
/* EdgeDice Base */
|
||||
|
||||
class EdgeDice {
|
||||
public:
|
||||
SubdParams params;
|
||||
int *mesh_triangles = nullptr;
|
||||
int *mesh_shader = nullptr;
|
||||
bool *mesh_smooth = nullptr;
|
||||
float3 *mesh_P = nullptr;
|
||||
float3 *mesh_N = nullptr;
|
||||
float *mesh_ptex_face_id = nullptr;
|
||||
float2 *mesh_ptex_uv = nullptr;
|
||||
|
||||
explicit EdgeDice(const SubdParams ¶ms);
|
||||
explicit EdgeDice(const SubdParams ¶ms, const int num_verts, const int num_triangles);
|
||||
|
||||
void reserve(const int num_verts, const int num_triangles);
|
||||
void dice(const DiagSplit &split);
|
||||
|
||||
protected:
|
||||
void set_vert(const Patch *patch, const int index, const float2 uv);
|
||||
void add_triangle(const Patch *patch,
|
||||
void dice(const SubPatch &sub);
|
||||
|
||||
void set_vertex(const SubPatch &sub, const int index, const float2 uv);
|
||||
void add_triangle(const SubPatch &sub,
|
||||
const int triangle_index,
|
||||
const int v0,
|
||||
const int v1,
|
||||
const int v2,
|
||||
|
|
@ -58,29 +63,15 @@ class EdgeDice {
|
|||
const float2 uv1,
|
||||
const float2 uv2);
|
||||
|
||||
void stitch_triangles_to_inner_grid(SubPatch &sub, const int edge, const int Mu, const int Mv);
|
||||
void stitch_triangles_across(SubPatch &sub, const int left_edge, const int right_edge);
|
||||
};
|
||||
void add_grid_triangles_and_stitch(const SubPatch &sub, const int Mu, const int Mv);
|
||||
void add_triangle_strip(const SubPatch &sub, const int left_edge, const int right_edge);
|
||||
|
||||
/* Quad EdgeDice */
|
||||
float3 eval_projected(const SubPatch &sub, const float2 uv);
|
||||
|
||||
class QuadDice : public EdgeDice {
|
||||
public:
|
||||
explicit QuadDice(const SubdParams ¶ms);
|
||||
|
||||
void dice(SubPatch &sub);
|
||||
|
||||
protected:
|
||||
float3 eval_projected(SubPatch &sub, const float2 uv);
|
||||
|
||||
void set_vert(SubPatch &sub, const int index, const float2 uv);
|
||||
|
||||
void add_grid(SubPatch &sub, const int Mu, const int Mv, const int offset);
|
||||
|
||||
void set_side(SubPatch &sub, const int edge);
|
||||
void set_sides(const SubPatch &sub);
|
||||
|
||||
float quad_area(const float3 &a, const float3 &b, const float3 &c, const float3 &d);
|
||||
float scale_factor(SubPatch &sub, const int Mu, const int Mv);
|
||||
float scale_factor(const SubPatch &sub, const int Mu, const int Mv);
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ void DiagSplit::alloc_subpatch(SubPatch &&sub)
|
|||
assert(sub.edge_v0.edge->T >= 1);
|
||||
|
||||
sub.inner_grid_vert_offset = alloc_verts(sub.calc_num_inner_verts());
|
||||
sub.triangles_offset = num_triangles;
|
||||
num_triangles += sub.calc_num_triangles();
|
||||
|
||||
subpatches.push_back(std::move(sub));
|
||||
|
|
@ -449,7 +450,7 @@ void DiagSplit::split(SubPatch &&sub, const int depth)
|
|||
split(std::move(sub_b), depth + 1);
|
||||
}
|
||||
|
||||
void DiagSplit::split_quad(const Mesh::SubdFace &face, const Patch *patch)
|
||||
void DiagSplit::split_quad(const Mesh::SubdFace &face, const int face_index, const Patch *patch)
|
||||
{
|
||||
/*
|
||||
* edge_u1
|
||||
|
|
@ -467,7 +468,7 @@ void DiagSplit::split_quad(const Mesh::SubdFace &face, const Patch *patch)
|
|||
const int v11 = subd_face_corners[face.start_corner + 2];
|
||||
const int v01 = subd_face_corners[face.start_corner + 3];
|
||||
|
||||
SubPatch subpatch(patch);
|
||||
SubPatch subpatch(patch, face_index);
|
||||
alloc_edge(&subpatch.edge_u0, v00, v10, true, true);
|
||||
alloc_edge(&subpatch.edge_v1, v10, v11, true, true);
|
||||
alloc_edge(&subpatch.edge_u1, v11, v01, true, true);
|
||||
|
|
@ -483,6 +484,7 @@ void DiagSplit::split_quad(const Mesh::SubdFace &face, const Patch *patch)
|
|||
}
|
||||
|
||||
void DiagSplit::split_ngon(const Mesh::SubdFace &face,
|
||||
const int face_index,
|
||||
const Patch *patches,
|
||||
const size_t patches_byte_stride)
|
||||
{
|
||||
|
|
@ -523,7 +525,7 @@ void DiagSplit::split_ngon(const Mesh::SubdFace &face,
|
|||
const int v10 = edge_u0.mid_vert_index();
|
||||
const int v01 = edge_v0.mid_vert_index();
|
||||
|
||||
SubPatch subpatch(patch);
|
||||
SubPatch subpatch(patch, face_index, corner);
|
||||
alloc_edge(&subpatch.edge_u0, v00, v10, false, false);
|
||||
alloc_edge(&subpatch.edge_v1, v10, v11, true, false);
|
||||
alloc_edge(&subpatch.edge_u1, v11, v01, true, corner == 0);
|
||||
|
|
@ -541,7 +543,11 @@ void DiagSplit::split_ngon(const Mesh::SubdFace &face,
|
|||
|
||||
void DiagSplit::split_patches(const Patch *patches, const size_t patches_byte_stride)
|
||||
{
|
||||
/* Keep base mesh vertices, create new triangles. */
|
||||
// TODO: reuse edge factor vertex position computations
|
||||
// TODO: support not splitting n-gons if not needed
|
||||
// TODO: multithreading
|
||||
|
||||
/* Keep base mesh vertices, create new triangels. */
|
||||
num_verts = params.mesh->get_num_subd_base_verts();
|
||||
num_triangles = 0;
|
||||
|
||||
|
|
@ -555,31 +561,13 @@ void DiagSplit::split_patches(const Patch *patches, const size_t patches_byte_st
|
|||
|
||||
if (face.is_quad()) {
|
||||
patch_index++;
|
||||
split_quad(face, patch);
|
||||
split_quad(face, f, patch);
|
||||
}
|
||||
else {
|
||||
patch_index += face.num_corners;
|
||||
split_ngon(face, patch, patches_byte_stride);
|
||||
split_ngon(face, f, patch, patches_byte_stride);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: reuse edge factor vertex position computations
|
||||
// TODO: avoid multiple write for linear vert attributes
|
||||
// TODO: avoid multiple write for smooth vert attributes
|
||||
// TODO: support not splitting n-gons if not needed
|
||||
// TODO: multithreading
|
||||
|
||||
/* Dice all patches. */
|
||||
QuadDice dice(params);
|
||||
dice.reserve(num_verts, num_triangles);
|
||||
|
||||
for (SubPatch &sub : subpatches) {
|
||||
dice.dice(sub);
|
||||
}
|
||||
|
||||
/* Cleanup */
|
||||
subpatches.clear();
|
||||
edges.clear();
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ CCL_NAMESPACE_BEGIN
|
|||
|
||||
class Mesh;
|
||||
class Patch;
|
||||
class SubdAttributeInterpolation;
|
||||
|
||||
class DiagSplit {
|
||||
private:
|
||||
|
|
@ -62,8 +63,9 @@ class DiagSplit {
|
|||
float2 Pend,
|
||||
const int depth);
|
||||
void split(SubPatch &&sub, const int depth = 0);
|
||||
void split_quad(const Mesh::SubdFace &face, const Patch *patch);
|
||||
void split_quad(const Mesh::SubdFace &face, const int face_index, const Patch *patch);
|
||||
void split_ngon(const Mesh::SubdFace &face,
|
||||
const int face_index,
|
||||
const Patch *patches,
|
||||
const size_t patches_byte_stride);
|
||||
|
||||
|
|
@ -71,6 +73,26 @@ class DiagSplit {
|
|||
explicit DiagSplit(const SubdParams ¶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
|
||||
|
|
|
|||
|
|
@ -75,8 +75,13 @@ class SubPatch {
|
|||
public:
|
||||
/* Patch this is a subpatch of. */
|
||||
const Patch *patch = nullptr;
|
||||
/* Face and corner. */
|
||||
int face_index = 0;
|
||||
int corner = 0;
|
||||
/* Vertex indices for inner grid start at this index. */
|
||||
int inner_grid_vert_offset = 0;
|
||||
/* Triangle indices. */
|
||||
int triangles_offset = 0;
|
||||
|
||||
/* Edge of patch. */
|
||||
struct Edge {
|
||||
|
|
@ -139,7 +144,10 @@ class SubPatch {
|
|||
};
|
||||
};
|
||||
|
||||
explicit SubPatch(const Patch *patch = nullptr) : patch(patch) {}
|
||||
explicit SubPatch(const Patch *patch, const int face_index, const int corner = 0)
|
||||
: patch(patch), face_index(face_index), corner(corner)
|
||||
{
|
||||
}
|
||||
|
||||
int calc_num_inner_verts() const
|
||||
{
|
||||
|
|
@ -154,10 +162,10 @@ class SubPatch {
|
|||
const int Mv = max(edge_v0.edge->T, edge_v1.edge->T);
|
||||
|
||||
if (Mu == 1) {
|
||||
return Mv * 2;
|
||||
return edge_v0.edge->T + edge_v1.edge->T;
|
||||
}
|
||||
if (Mv == 1) {
|
||||
return Mu * 2;
|
||||
return edge_u0.edge->T + edge_u1.edge->T;
|
||||
}
|
||||
|
||||
const int inner_triangles = (Mu - 2) * (Mv - 2) * 2;
|
||||
|
|
@ -207,7 +215,7 @@ class SubPatch {
|
|||
return -1;
|
||||
}
|
||||
|
||||
float2 map_uv(float2 uv)
|
||||
float2 map_uv(float2 uv) const
|
||||
{
|
||||
/* Map UV from subpatch to patch parametric coordinates. */
|
||||
const float2 d0 = interp(uv00, uv01, uv.y);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue