Files
test/source/blender/blenkernel/intern/subdiv_foreach.cc
Campbell Barton e955c94ed3 License Headers: Set copyright to "Blender Authors", add AUTHORS
Listing the "Blender Foundation" as copyright holder implied the Blender
Foundation holds copyright to files which may include work from many
developers.

While keeping copyright on headers makes sense for isolated libraries,
Blender's own code may be refactored or moved between files in a way
that makes the per file copyright holders less meaningful.

Copyright references to the "Blender Foundation" have been replaced with
"Blender Authors", with the exception of `./extern/` since these this
contains libraries which are more isolated, any changed to license
headers there can be handled on a case-by-case basis.

Some directories in `./intern/` have also been excluded:

- `./intern/cycles/` it's own `AUTHORS` file is planned.
- `./intern/opensubdiv/`.

An "AUTHORS" file has been added, using the chromium projects authors
file as a template.

Design task: #110784

Ref !110783.
2023-08-16 00:20:26 +10:00

1873 lines
79 KiB
C++

/* SPDX-FileCopyrightText: 2018 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#include "BKE_subdiv_foreach.hh"
#include "atomic_ops.h"
#include "DNA_key_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BLI_bitmap.h"
#include "BLI_task.h"
#include "BKE_customdata.h"
#include "BKE_key.h"
#include "BKE_mesh.hh"
#include "BKE_subdiv.hh"
#include "BKE_subdiv_mesh.hh"
#include "MEM_guardedalloc.h"
using blender::IndexRange;
using blender::int2;
/* -------------------------------------------------------------------- */
/** \name General helpers
* \{ */
/* Number of ptex faces for a given face. */
BLI_INLINE int num_ptex_faces_per_face_get(const IndexRange face)
{
return (face.size() == 4) ? 1 : face.size();
}
BLI_INLINE int num_edges_per_ptex_face_get(const int resolution)
{
return 2 * (resolution - 1) * resolution;
}
BLI_INLINE int num_inner_edges_per_ptex_face_get(const int resolution)
{
if (resolution < 2) {
return 0;
}
return (resolution - 2) * resolution + (resolution - 1) * (resolution - 1);
}
/* Number of subdivision polygons per ptex face. */
BLI_INLINE int num_faces_per_ptex_get(const int resolution)
{
return (resolution - 1) * (resolution - 1);
}
/* Subdivision resolution per given face's ptex faces. */
BLI_INLINE int ptex_face_resolution_get(const IndexRange face, int resolution)
{
return (face.size() == 4) ? (resolution) : ((resolution >> 1) + 1);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Context which is passed to all threaded tasks
* \{ */
struct SubdivForeachTaskContext {
const Mesh *coarse_mesh;
blender::Span<int2> coarse_edges;
blender::OffsetIndices<int> coarse_faces;
blender::Span<int> coarse_corner_verts;
blender::Span<int> coarse_corner_edges;
const SubdivToMeshSettings *settings;
/* Callbacks. */
const SubdivForeachContext *foreach_context;
/* Counters of geometry in subdivided mesh, initialized as a part of
* offsets calculation.
*/
int num_subdiv_vertices;
int num_subdiv_edges;
int num_subdiv_loops;
int num_subdiv_faces;
/* Offsets of various geometry in the subdivision mesh arrays. */
int vertices_corner_offset;
int vertices_edge_offset;
int vertices_inner_offset;
int edge_boundary_offset;
int edge_inner_offset;
/* Indexed by coarse face index, indicates offset in subdivided mesh
* vertices, edges and polygons arrays, where first element of the face
* begins.
*/
int *subdiv_vertex_offset;
int *subdiv_edge_offset;
int *subdiv_face_offset;
/* Indexed by base face index, element indicates total number of ptex faces
* created for preceding base faces.
*/
int *face_ptex_offset;
/* Bitmap indicating whether vertex was used already or not.
* - During patch evaluation indicates whether coarse vertex was already
* evaluated and its position on limit is already known.
*/
BLI_bitmap *coarse_vertices_used_map;
/* Bitmap indicating whether edge was used already or not. This includes:
* - During context initialization it indicates whether subdivided vertices
* for corresponding edge were already calculated or not.
* - During patch evaluation it indicates whether vertices along this edge
* were already evaluated.
*/
BLI_bitmap *coarse_edges_used_map;
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Threading helpers
* \{ */
static void *subdiv_foreach_tls_alloc(SubdivForeachTaskContext *ctx)
{
const SubdivForeachContext *foreach_context = ctx->foreach_context;
void *tls = nullptr;
if (foreach_context->user_data_tls_size != 0) {
tls = MEM_mallocN(foreach_context->user_data_tls_size, "tls");
memcpy(tls, foreach_context->user_data_tls, foreach_context->user_data_tls_size);
}
return tls;
}
static void subdiv_foreach_tls_free(SubdivForeachTaskContext *ctx, void *tls)
{
if (tls == nullptr) {
return;
}
if (ctx->foreach_context != nullptr) {
ctx->foreach_context->user_data_tls_free(tls);
}
MEM_freeN(tls);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Initialization
* \{ */
/* NOTE: Expects edge map to be zeroed. */
static void subdiv_foreach_ctx_count(SubdivForeachTaskContext *ctx)
{
/* Reset counters. */
ctx->num_subdiv_vertices = 0;
ctx->num_subdiv_edges = 0;
ctx->num_subdiv_loops = 0;
ctx->num_subdiv_faces = 0;
/* Static geometry counters. */
const int resolution = ctx->settings->resolution;
const int no_quad_patch_resolution = ((resolution >> 1) + 1);
const int num_subdiv_vertices_per_coarse_edge = resolution - 2;
const int num_inner_vertices_per_quad = (resolution - 2) * (resolution - 2);
const int num_inner_vertices_per_noquad_patch = (no_quad_patch_resolution - 2) *
(no_quad_patch_resolution - 2);
const Mesh *coarse_mesh = ctx->coarse_mesh;
ctx->num_subdiv_vertices = coarse_mesh->totvert;
ctx->num_subdiv_edges = coarse_mesh->totedge * (num_subdiv_vertices_per_coarse_edge + 1);
/* Calculate extra vertices and edges created by non-loose geometry. */
for (int face_index = 0; face_index < coarse_mesh->faces_num; face_index++) {
const IndexRange coarse_face = ctx->coarse_faces[face_index];
const int num_ptex_faces_per_face = num_ptex_faces_per_face_get(coarse_face);
/* Inner vertices of face. */
if (num_ptex_faces_per_face == 1) {
ctx->num_subdiv_vertices += num_inner_vertices_per_quad;
ctx->num_subdiv_edges += num_edges_per_ptex_face_get(resolution - 2) +
4 * num_subdiv_vertices_per_coarse_edge;
ctx->num_subdiv_faces += num_faces_per_ptex_get(resolution);
}
else {
ctx->num_subdiv_vertices += 1 + num_ptex_faces_per_face * (no_quad_patch_resolution - 2) +
num_ptex_faces_per_face * num_inner_vertices_per_noquad_patch;
ctx->num_subdiv_edges += num_ptex_faces_per_face *
(num_inner_edges_per_ptex_face_get(no_quad_patch_resolution - 1) +
(no_quad_patch_resolution - 2) +
num_subdiv_vertices_per_coarse_edge);
if (no_quad_patch_resolution >= 3) {
ctx->num_subdiv_edges += coarse_face.size();
}
ctx->num_subdiv_faces += num_ptex_faces_per_face *
num_faces_per_ptex_get(no_quad_patch_resolution);
}
}
/* Add vertices used by outer edges on subdivided faces and loose edges. */
ctx->num_subdiv_vertices += num_subdiv_vertices_per_coarse_edge * coarse_mesh->totedge;
ctx->num_subdiv_loops = ctx->num_subdiv_faces * 4;
}
static void subdiv_foreach_ctx_init_offsets(SubdivForeachTaskContext *ctx)
{
const Mesh *coarse_mesh = ctx->coarse_mesh;
const int resolution = ctx->settings->resolution;
const int resolution_2 = resolution - 2;
const int resolution_2_squared = resolution_2 * resolution_2;
const int no_quad_patch_resolution = ((resolution >> 1) + 1);
const int num_irregular_vertices_per_patch = (no_quad_patch_resolution - 2) *
(no_quad_patch_resolution - 1);
const int num_subdiv_vertices_per_coarse_edge = resolution - 2;
const int num_subdiv_edges_per_coarse_edge = resolution - 1;
/* Constant offsets in arrays. */
ctx->vertices_corner_offset = 0;
ctx->vertices_edge_offset = coarse_mesh->totvert;
ctx->vertices_inner_offset = ctx->vertices_edge_offset +
coarse_mesh->totedge * num_subdiv_vertices_per_coarse_edge;
ctx->edge_boundary_offset = 0;
ctx->edge_inner_offset = ctx->edge_boundary_offset +
coarse_mesh->totedge * num_subdiv_edges_per_coarse_edge;
/* "Indexed" offsets. */
int vertex_offset = 0;
int edge_offset = 0;
int face_offset = 0;
for (int face_index = 0; face_index < coarse_mesh->faces_num; face_index++) {
const IndexRange coarse_face = ctx->coarse_faces[face_index];
const int num_ptex_faces_per_face = num_ptex_faces_per_face_get(coarse_face);
ctx->subdiv_vertex_offset[face_index] = vertex_offset;
ctx->subdiv_edge_offset[face_index] = edge_offset;
ctx->subdiv_face_offset[face_index] = face_offset;
if (num_ptex_faces_per_face == 1) {
vertex_offset += resolution_2_squared;
edge_offset += num_edges_per_ptex_face_get(resolution - 2) +
4 * num_subdiv_vertices_per_coarse_edge;
face_offset += num_faces_per_ptex_get(resolution);
}
else {
vertex_offset += 1 + num_ptex_faces_per_face * num_irregular_vertices_per_patch;
edge_offset += num_ptex_faces_per_face *
(num_inner_edges_per_ptex_face_get(no_quad_patch_resolution - 1) +
(no_quad_patch_resolution - 2) + num_subdiv_vertices_per_coarse_edge);
if (no_quad_patch_resolution >= 3) {
edge_offset += coarse_face.size();
}
face_offset += num_ptex_faces_per_face * num_faces_per_ptex_get(no_quad_patch_resolution);
}
}
}
static void subdiv_foreach_ctx_init(Subdiv *subdiv, SubdivForeachTaskContext *ctx)
{
const Mesh *coarse_mesh = ctx->coarse_mesh;
/* Allocate maps and offsets. */
ctx->coarse_vertices_used_map = BLI_BITMAP_NEW(coarse_mesh->totvert, "vertices used map");
ctx->coarse_edges_used_map = BLI_BITMAP_NEW(coarse_mesh->totedge, "edges used map");
ctx->subdiv_vertex_offset = static_cast<int *>(MEM_malloc_arrayN(
coarse_mesh->faces_num, sizeof(*ctx->subdiv_vertex_offset), "vertex_offset"));
ctx->subdiv_edge_offset = static_cast<int *>(MEM_malloc_arrayN(
coarse_mesh->faces_num, sizeof(*ctx->subdiv_edge_offset), "subdiv_edge_offset"));
ctx->subdiv_face_offset = static_cast<int *>(MEM_malloc_arrayN(
coarse_mesh->faces_num, sizeof(*ctx->subdiv_face_offset), "subdiv_edge_offset"));
/* Initialize all offsets. */
subdiv_foreach_ctx_init_offsets(ctx);
/* Calculate number of geometry in the result subdivision mesh. */
subdiv_foreach_ctx_count(ctx);
ctx->face_ptex_offset = BKE_subdiv_face_ptex_offset_get(subdiv);
}
static void subdiv_foreach_ctx_free(SubdivForeachTaskContext *ctx)
{
MEM_freeN(ctx->coarse_vertices_used_map);
MEM_freeN(ctx->coarse_edges_used_map);
MEM_freeN(ctx->subdiv_vertex_offset);
MEM_freeN(ctx->subdiv_edge_offset);
MEM_freeN(ctx->subdiv_face_offset);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Vertex traversal process
* \{ */
/* Traversal of corner vertices. They are coming from coarse vertices. */
static void subdiv_foreach_corner_vertices_regular_do(
SubdivForeachTaskContext *ctx,
void *tls,
const int coarse_face_index,
SubdivForeachVertexFromCornerCb vertex_corner,
bool check_usage)
{
const float weights[4][2] = {{0.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 1.0f}};
const IndexRange coarse_face = ctx->coarse_faces[coarse_face_index];
const int ptex_face_index = ctx->face_ptex_offset[coarse_face_index];
for (int corner = 0; corner < coarse_face.size(); corner++) {
const int coarse_vert = ctx->coarse_corner_verts[coarse_face[corner]];
if (check_usage && BLI_BITMAP_TEST_AND_SET_ATOMIC(ctx->coarse_vertices_used_map, coarse_vert))
{
continue;
}
const int coarse_vertex_index = coarse_vert;
const int subdiv_vertex_index = ctx->vertices_corner_offset + coarse_vertex_index;
const float u = weights[corner][0];
const float v = weights[corner][1];
vertex_corner(ctx->foreach_context,
tls,
ptex_face_index,
u,
v,
coarse_vertex_index,
coarse_face_index,
0,
subdiv_vertex_index);
}
}
static void subdiv_foreach_corner_vertices_regular(SubdivForeachTaskContext *ctx,
void *tls,
const int coarse_face_index)
{
subdiv_foreach_corner_vertices_regular_do(
ctx, tls, coarse_face_index, ctx->foreach_context->vertex_corner, true);
}
static void subdiv_foreach_corner_vertices_special_do(
SubdivForeachTaskContext *ctx,
void *tls,
const int coarse_face_index,
SubdivForeachVertexFromCornerCb vertex_corner,
bool check_usage)
{
const IndexRange coarse_face = ctx->coarse_faces[coarse_face_index];
int ptex_face_index = ctx->face_ptex_offset[coarse_face_index];
for (int corner = 0; corner < coarse_face.size(); corner++, ptex_face_index++) {
const int coarse_vert = ctx->coarse_corner_verts[coarse_face[corner]];
if (check_usage && BLI_BITMAP_TEST_AND_SET_ATOMIC(ctx->coarse_vertices_used_map, coarse_vert))
{
continue;
}
const int coarse_vertex_index = coarse_vert;
const int subdiv_vertex_index = ctx->vertices_corner_offset + coarse_vertex_index;
vertex_corner(ctx->foreach_context,
tls,
ptex_face_index,
0.0f,
0.0f,
coarse_vertex_index,
coarse_face_index,
corner,
subdiv_vertex_index);
}
}
static void subdiv_foreach_corner_vertices_special(SubdivForeachTaskContext *ctx,
void *tls,
const int coarse_face_index)
{
subdiv_foreach_corner_vertices_special_do(
ctx, tls, coarse_face_index, ctx->foreach_context->vertex_corner, true);
}
static void subdiv_foreach_corner_vertices(SubdivForeachTaskContext *ctx,
void *tls,
const int coarse_face_index)
{
if (ctx->coarse_faces[coarse_face_index].size() == 4) {
subdiv_foreach_corner_vertices_regular(ctx, tls, coarse_face_index);
}
else {
subdiv_foreach_corner_vertices_special(ctx, tls, coarse_face_index);
}
}
static void subdiv_foreach_every_corner_vertices_regular(SubdivForeachTaskContext *ctx,
void *tls,
const int coarse_face_index)
{
subdiv_foreach_corner_vertices_regular_do(
ctx, tls, coarse_face_index, ctx->foreach_context->vertex_every_corner, false);
}
static void subdiv_foreach_every_corner_vertices_special(SubdivForeachTaskContext *ctx,
void *tls,
const int coarse_face_index)
{
subdiv_foreach_corner_vertices_special_do(
ctx, tls, coarse_face_index, ctx->foreach_context->vertex_every_corner, false);
}
static void subdiv_foreach_every_corner_vertices(SubdivForeachTaskContext *ctx, void *tls)
{
if (ctx->foreach_context->vertex_every_corner == nullptr) {
return;
}
const Mesh *coarse_mesh = ctx->coarse_mesh;
for (int face_index = 0; face_index < coarse_mesh->faces_num; face_index++) {
if (ctx->coarse_faces[face_index].size() == 4) {
subdiv_foreach_every_corner_vertices_regular(ctx, tls, face_index);
}
else {
subdiv_foreach_every_corner_vertices_special(ctx, tls, face_index);
}
}
}
/* Traverse of edge vertices. They are coming from coarse edges. */
static void subdiv_foreach_edge_vertices_regular_do(SubdivForeachTaskContext *ctx,
void *tls,
const int coarse_face_index,
SubdivForeachVertexFromEdgeCb vertex_edge,
bool check_usage)
{
const IndexRange coarse_face = ctx->coarse_faces[coarse_face_index];
const int resolution = ctx->settings->resolution;
const int resolution_1 = resolution - 1;
const float inv_resolution_1 = 1.0f / float(resolution_1);
const int num_subdiv_vertices_per_coarse_edge = resolution - 2;
const int ptex_face_index = ctx->face_ptex_offset[coarse_face_index];
for (int corner = 0; corner < coarse_face.size(); corner++) {
const int coarse_vert = ctx->coarse_corner_verts[coarse_face[corner]];
const int coarse_edge_index = ctx->coarse_corner_edges[coarse_face[corner]];
if (check_usage &&
BLI_BITMAP_TEST_AND_SET_ATOMIC(ctx->coarse_edges_used_map, coarse_edge_index)) {
continue;
}
const int2 &coarse_edge = ctx->coarse_edges[coarse_edge_index];
const bool flip = (coarse_edge[1] == coarse_vert);
int subdiv_vertex_index = ctx->vertices_edge_offset +
coarse_edge_index * num_subdiv_vertices_per_coarse_edge;
for (int vertex_index = 0; vertex_index < num_subdiv_vertices_per_coarse_edge;
vertex_index++, subdiv_vertex_index++)
{
float fac = (vertex_index + 1) * inv_resolution_1;
if (flip) {
fac = 1.0f - fac;
}
if (corner >= 2) {
fac = 1.0f - fac;
}
float u, v;
if ((corner & 1) == 0) {
u = fac;
v = (corner == 2) ? 1.0f : 0.0f;
}
else {
u = (corner == 1) ? 1.0f : 0.0f;
v = fac;
}
vertex_edge(ctx->foreach_context,
tls,
ptex_face_index,
u,
v,
coarse_edge_index,
coarse_face_index,
0,
subdiv_vertex_index);
}
}
}
static void subdiv_foreach_edge_vertices_regular(SubdivForeachTaskContext *ctx,
void *tls,
const int coarse_face_index)
{
subdiv_foreach_edge_vertices_regular_do(
ctx, tls, coarse_face_index, ctx->foreach_context->vertex_edge, true);
}
static void subdiv_foreach_edge_vertices_special_do(SubdivForeachTaskContext *ctx,
void *tls,
const int coarse_face_index,
SubdivForeachVertexFromEdgeCb vertex_edge,
bool check_usage)
{
const IndexRange coarse_face = ctx->coarse_faces[coarse_face_index];
const int resolution = ctx->settings->resolution;
const int num_subdiv_vertices_per_coarse_edge = resolution - 2;
const int num_vertices_per_ptex_edge = ((resolution >> 1) + 1);
const float inv_ptex_resolution_1 = 1.0f / float(num_vertices_per_ptex_edge - 1);
const int ptex_face_start_index = ctx->face_ptex_offset[coarse_face_index];
int ptex_face_index = ptex_face_start_index;
for (int corner = 0; corner < coarse_face.size(); corner++, ptex_face_index++) {
const int coarse_vert = ctx->coarse_corner_verts[coarse_face[corner]];
const int coarse_edge_index = ctx->coarse_corner_edges[coarse_face[corner]];
if (check_usage &&
BLI_BITMAP_TEST_AND_SET_ATOMIC(ctx->coarse_edges_used_map, coarse_edge_index)) {
continue;
}
const int2 &coarse_edge = ctx->coarse_edges[coarse_edge_index];
const bool flip = (coarse_edge[1] == coarse_vert);
int subdiv_vertex_index = ctx->vertices_edge_offset +
coarse_edge_index * num_subdiv_vertices_per_coarse_edge;
int vertex_delta = 1;
if (flip) {
subdiv_vertex_index += num_subdiv_vertices_per_coarse_edge - 1;
vertex_delta = -1;
}
for (int vertex_index = 1; vertex_index < num_vertices_per_ptex_edge;
vertex_index++, subdiv_vertex_index += vertex_delta)
{
const float u = vertex_index * inv_ptex_resolution_1;
vertex_edge(ctx->foreach_context,
tls,
ptex_face_index,
u,
0.0f,
coarse_edge_index,
coarse_face_index,
corner,
subdiv_vertex_index);
}
const int next_corner = (corner + 1) % coarse_face.size();
const int next_ptex_face_index = ptex_face_start_index + next_corner;
for (int vertex_index = 1; vertex_index < num_vertices_per_ptex_edge - 1;
vertex_index++, subdiv_vertex_index += vertex_delta)
{
const float v = 1.0f - vertex_index * inv_ptex_resolution_1;
vertex_edge(ctx->foreach_context,
tls,
next_ptex_face_index,
0.0f,
v,
coarse_edge_index,
coarse_face_index,
next_corner,
subdiv_vertex_index);
}
}
}
static void subdiv_foreach_edge_vertices_special(SubdivForeachTaskContext *ctx,
void *tls,
const int coarse_face_index)
{
subdiv_foreach_edge_vertices_special_do(
ctx, tls, coarse_face_index, ctx->foreach_context->vertex_edge, true);
}
static void subdiv_foreach_edge_vertices(SubdivForeachTaskContext *ctx,
void *tls,
const int coarse_face_index)
{
if (ctx->coarse_faces[coarse_face_index].size() == 4) {
subdiv_foreach_edge_vertices_regular(ctx, tls, coarse_face_index);
}
else {
subdiv_foreach_edge_vertices_special(ctx, tls, coarse_face_index);
}
}
static void subdiv_foreach_every_edge_vertices_regular(SubdivForeachTaskContext *ctx,
void *tls,
const int coarse_face_index)
{
subdiv_foreach_edge_vertices_regular_do(
ctx, tls, coarse_face_index, ctx->foreach_context->vertex_every_edge, false);
}
static void subdiv_foreach_every_edge_vertices_special(SubdivForeachTaskContext *ctx,
void *tls,
const int coarse_face_index)
{
subdiv_foreach_edge_vertices_special_do(
ctx, tls, coarse_face_index, ctx->foreach_context->vertex_every_edge, false);
}
static void subdiv_foreach_every_edge_vertices(SubdivForeachTaskContext *ctx, void *tls)
{
if (ctx->foreach_context->vertex_every_edge == nullptr) {
return;
}
const Mesh *coarse_mesh = ctx->coarse_mesh;
for (int face_index = 0; face_index < coarse_mesh->faces_num; face_index++) {
if (ctx->coarse_faces[face_index].size() == 4) {
subdiv_foreach_every_edge_vertices_regular(ctx, tls, face_index);
}
else {
subdiv_foreach_every_edge_vertices_special(ctx, tls, face_index);
}
}
}
/* Traversal of inner vertices, they are coming from ptex patches. */
static void subdiv_foreach_inner_vertices_regular(SubdivForeachTaskContext *ctx,
void *tls,
const int coarse_face_index)
{
const int resolution = ctx->settings->resolution;
const float inv_resolution_1 = 1.0f / float(resolution - 1);
const int ptex_face_index = ctx->face_ptex_offset[coarse_face_index];
const int start_vertex_index = ctx->subdiv_vertex_offset[coarse_face_index];
int subdiv_vertex_index = ctx->vertices_inner_offset + start_vertex_index;
for (int y = 1; y < resolution - 1; y++) {
const float v = y * inv_resolution_1;
for (int x = 1; x < resolution - 1; x++, subdiv_vertex_index++) {
const float u = x * inv_resolution_1;
ctx->foreach_context->vertex_inner(ctx->foreach_context,
tls,
ptex_face_index,
u,
v,
coarse_face_index,
0,
subdiv_vertex_index);
}
}
}
static void subdiv_foreach_inner_vertices_special(SubdivForeachTaskContext *ctx,
void *tls,
const int coarse_face_index)
{
const int resolution = ctx->settings->resolution;
const IndexRange coarse_face = ctx->coarse_faces[coarse_face_index];
const int ptex_face_resolution = ptex_face_resolution_get(coarse_face, resolution);
const float inv_ptex_face_resolution_1 = 1.0f / float(ptex_face_resolution - 1);
int ptex_face_index = ctx->face_ptex_offset[coarse_face_index];
const int start_vertex_index = ctx->subdiv_vertex_offset[coarse_face_index];
int subdiv_vertex_index = ctx->vertices_inner_offset + start_vertex_index;
ctx->foreach_context->vertex_inner(ctx->foreach_context,
tls,
ptex_face_index,
1.0f,
1.0f,
coarse_face_index,
0,
subdiv_vertex_index);
subdiv_vertex_index++;
for (int corner = 0; corner < coarse_face.size(); corner++, ptex_face_index++) {
for (int y = 1; y < ptex_face_resolution - 1; y++) {
const float v = y * inv_ptex_face_resolution_1;
for (int x = 1; x < ptex_face_resolution; x++, subdiv_vertex_index++) {
const float u = x * inv_ptex_face_resolution_1;
ctx->foreach_context->vertex_inner(ctx->foreach_context,
tls,
ptex_face_index,
u,
v,
coarse_face_index,
corner,
subdiv_vertex_index);
}
}
}
}
static void subdiv_foreach_inner_vertices(SubdivForeachTaskContext *ctx,
void *tls,
const int coarse_face_index)
{
if (ctx->coarse_faces[coarse_face_index].size() == 4) {
subdiv_foreach_inner_vertices_regular(ctx, tls, coarse_face_index);
}
else {
subdiv_foreach_inner_vertices_special(ctx, tls, coarse_face_index);
}
}
/* Traverse all vertices which are emitted from given coarse face. */
static void subdiv_foreach_vertices(SubdivForeachTaskContext *ctx, void *tls, const int face_index)
{
if (ctx->foreach_context->vertex_inner != nullptr) {
subdiv_foreach_inner_vertices(ctx, tls, face_index);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Edge traversal process
* \{ */
/* TODO(sergey): Coarse edge are always NONE, consider getting rid of it. */
static int subdiv_foreach_edges_row(SubdivForeachTaskContext *ctx,
void *tls,
const int coarse_edge_index,
const int start_subdiv_edge_index,
const int start_vertex_index,
const int num_edges_per_row)
{
int subdiv_edge_index = start_subdiv_edge_index;
int vertex_index = start_vertex_index;
for (int edge_index = 0; edge_index < num_edges_per_row - 1; edge_index++, subdiv_edge_index++) {
const int v1 = vertex_index;
const int v2 = vertex_index + 1;
ctx->foreach_context->edge(
ctx->foreach_context, tls, coarse_edge_index, subdiv_edge_index, false, v1, v2);
vertex_index += 1;
}
return subdiv_edge_index;
}
/* TODO(sergey): Coarse edges are always NONE, consider getting rid of them. */
static int subdiv_foreach_edges_column(SubdivForeachTaskContext *ctx,
void *tls,
const int coarse_start_edge_index,
const int coarse_end_edge_index,
const int start_subdiv_edge_index,
const int start_vertex_index,
const int num_edges_per_row)
{
int subdiv_edge_index = start_subdiv_edge_index;
int vertex_index = start_vertex_index;
for (int edge_index = 0; edge_index < num_edges_per_row; edge_index++, subdiv_edge_index++) {
int coarse_edge_index = ORIGINDEX_NONE;
if (edge_index == 0) {
coarse_edge_index = coarse_start_edge_index;
}
else if (edge_index == num_edges_per_row - 1) {
coarse_edge_index = coarse_end_edge_index;
}
const int v1 = vertex_index;
const int v2 = vertex_index + num_edges_per_row;
ctx->foreach_context->edge(
ctx->foreach_context, tls, coarse_edge_index, subdiv_edge_index, false, v1, v2);
vertex_index += 1;
}
return subdiv_edge_index;
}
/* Defines edges between inner vertices of patch, and also edges to the
* boundary.
*/
/* Consider a subdivision of base face at level 1:
*
* y
* ^
* | (6) ---- (7) ---- (8)
* | | | |
* | (3) ---- (4) ---- (5)
* | | | |
* | (0) ---- (1) ---- (2)
* o---------------------------> x
*
* This is illustrate which parts of geometry is created by code below.
*/
static void subdiv_foreach_edges_all_patches_regular(SubdivForeachTaskContext *ctx,
void *tls,
const int coarse_face_index)
{
const IndexRange coarse_face = ctx->coarse_faces[coarse_face_index];
const int resolution = ctx->settings->resolution;
const int start_vertex_index = ctx->vertices_inner_offset +
ctx->subdiv_vertex_offset[coarse_face_index];
const int num_subdiv_vertices_per_coarse_edge = resolution - 2;
int subdiv_edge_index = ctx->edge_inner_offset + ctx->subdiv_edge_offset[coarse_face_index];
/* Traverse bottom row of edges (0-1, 1-2). */
subdiv_edge_index = subdiv_foreach_edges_row(
ctx, tls, ORIGINDEX_NONE, subdiv_edge_index, start_vertex_index, resolution - 2);
/* Traverse remaining edges. */
for (int row = 0; row < resolution - 3; row++) {
const int start_row_vertex_index = start_vertex_index + row * (resolution - 2);
/* Traverse vertical columns.
*
* At first iteration it will be edges (0-3. 1-4, 2-5), then it
* will be (3-6, 4-7, 5-8) and so on.
*/
subdiv_edge_index = subdiv_foreach_edges_column(ctx,
tls,
ORIGINDEX_NONE,
ORIGINDEX_NONE,
subdiv_edge_index,
start_row_vertex_index,
resolution - 2);
/* Create horizontal edge row.
*
* At first iteration it will be edges (3-4, 4-5), then it will be
* (6-7, 7-8) and so on.
*/
subdiv_edge_index = subdiv_foreach_edges_row(ctx,
tls,
ORIGINDEX_NONE,
subdiv_edge_index,
start_row_vertex_index + resolution - 2,
resolution - 2);
}
/* Connect inner part of patch to boundary. */
for (int corner = 0; corner < coarse_face.size(); corner++) {
const int coarse_vert_index = ctx->coarse_corner_verts[coarse_face[corner]];
const int coarse_edge_index = ctx->coarse_corner_edges[coarse_face[corner]];
const int2 &coarse_edge = ctx->coarse_edges[coarse_edge_index];
const int start_edge_vertex = ctx->vertices_edge_offset +
coarse_edge_index * num_subdiv_vertices_per_coarse_edge;
const bool flip = (coarse_edge[1] == coarse_vert_index);
int side_start_index = start_vertex_index;
int side_stride = 0;
/* Calculate starting vertex of corresponding inner part of ptex. */
if (corner == 0) {
side_stride = 1;
}
else if (corner == 1) {
side_start_index += resolution - 3;
side_stride = resolution - 2;
}
else if (corner == 2) {
side_start_index += num_subdiv_vertices_per_coarse_edge *
num_subdiv_vertices_per_coarse_edge -
1;
side_stride = -1;
}
else if (corner == 3) {
side_start_index += num_subdiv_vertices_per_coarse_edge *
(num_subdiv_vertices_per_coarse_edge - 1);
side_stride = -(resolution - 2);
}
for (int i = 0; i < resolution - 2; i++, subdiv_edge_index++) {
const int v1 = (flip) ? (start_edge_vertex + (resolution - i - 3)) : (start_edge_vertex + i);
const int v2 = side_start_index + side_stride * i;
ctx->foreach_context->edge(
ctx->foreach_context, tls, ORIGINDEX_NONE, subdiv_edge_index, false, v1, v2);
}
}
}
static void subdiv_foreach_edges_all_patches_special(SubdivForeachTaskContext *ctx,
void *tls,
const int coarse_face_index)
{
const int resolution = ctx->settings->resolution;
const IndexRange coarse_face = ctx->coarse_faces[coarse_face_index];
const int ptex_face_resolution = ptex_face_resolution_get(coarse_face, resolution);
const int ptex_face_inner_resolution = ptex_face_resolution - 2;
const int num_inner_vertices_per_ptex = (ptex_face_resolution - 1) * (ptex_face_resolution - 2);
const int num_subdiv_vertices_per_coarse_edge = resolution - 2;
const int center_vertex_index = ctx->vertices_inner_offset +
ctx->subdiv_vertex_offset[coarse_face_index];
const int start_vertex_index = center_vertex_index + 1;
int subdiv_edge_index = ctx->edge_inner_offset + ctx->subdiv_edge_offset[coarse_face_index];
/* Traverse inner ptex edges. */
for (int corner = 0; corner < coarse_face.size(); corner++) {
const int start_ptex_face_vertex_index = start_vertex_index +
corner * num_inner_vertices_per_ptex;
/* Similar steps to regular patch case. */
subdiv_edge_index = subdiv_foreach_edges_row(ctx,
tls,
ORIGINDEX_NONE,
subdiv_edge_index,
start_ptex_face_vertex_index,
ptex_face_inner_resolution + 1);
for (int row = 0; row < ptex_face_inner_resolution - 1; row++) {
const int start_row_vertex_index = start_ptex_face_vertex_index +
row * (ptex_face_inner_resolution + 1);
subdiv_edge_index = subdiv_foreach_edges_column(ctx,
tls,
ORIGINDEX_NONE,
ORIGINDEX_NONE,
subdiv_edge_index,
start_row_vertex_index,
ptex_face_inner_resolution + 1);
subdiv_edge_index = subdiv_foreach_edges_row(ctx,
tls,
ORIGINDEX_NONE,
subdiv_edge_index,
start_row_vertex_index +
ptex_face_inner_resolution + 1,
ptex_face_inner_resolution + 1);
}
}
/* Create connections between ptex faces. */
for (int corner = 0; corner < coarse_face.size(); corner++) {
const int next_corner = (corner + 1) % coarse_face.size();
int current_patch_vertex_index = start_vertex_index + corner * num_inner_vertices_per_ptex +
ptex_face_inner_resolution;
int next_path_vertex_index = start_vertex_index + next_corner * num_inner_vertices_per_ptex +
num_inner_vertices_per_ptex - ptex_face_resolution + 1;
for (int row = 0; row < ptex_face_inner_resolution; row++, subdiv_edge_index++) {
const int v1 = current_patch_vertex_index;
const int v2 = next_path_vertex_index;
ctx->foreach_context->edge(
ctx->foreach_context, tls, ORIGINDEX_NONE, subdiv_edge_index, false, v1, v2);
current_patch_vertex_index += ptex_face_inner_resolution + 1;
next_path_vertex_index += 1;
}
}
/* Create edges from center. */
if (ptex_face_resolution >= 3) {
for (int corner = 0; corner < coarse_face.size(); corner++, subdiv_edge_index++) {
const int current_patch_end_vertex_index = start_vertex_index +
corner * num_inner_vertices_per_ptex +
num_inner_vertices_per_ptex - 1;
const int v1 = center_vertex_index;
const int v2 = current_patch_end_vertex_index;
ctx->foreach_context->edge(
ctx->foreach_context, tls, ORIGINDEX_NONE, subdiv_edge_index, false, v1, v2);
}
}
/* Connect inner path of patch to boundary. */
int prev_corner = coarse_face.size() - 1;
for (int corner = 0; corner < coarse_face.size(); corner++) {
const int coarse_vert = ctx->coarse_corner_verts[coarse_face[corner]];
const int coarse_edge_i = ctx->coarse_corner_edges[coarse_face[corner]];
const int coarse_prev_edge = ctx->coarse_corner_edges[coarse_face[prev_corner]];
{
const int2 &coarse_edge = ctx->coarse_edges[coarse_edge_i];
const int start_edge_vertex = ctx->vertices_edge_offset +
coarse_edge_i * num_subdiv_vertices_per_coarse_edge;
const bool flip = (coarse_edge[1] == coarse_vert);
int side_start_index;
if (ptex_face_resolution >= 3) {
side_start_index = start_vertex_index + num_inner_vertices_per_ptex * corner;
}
else {
side_start_index = center_vertex_index;
}
for (int i = 0; i < ptex_face_resolution - 1; i++, subdiv_edge_index++) {
const int v1 = (flip) ? (start_edge_vertex + (resolution - i - 3)) :
(start_edge_vertex + i);
const int v2 = side_start_index + i;
ctx->foreach_context->edge(
ctx->foreach_context, tls, ORIGINDEX_NONE, subdiv_edge_index, false, v1, v2);
}
}
if (ptex_face_resolution >= 3) {
const int2 &coarse_edge = ctx->coarse_edges[coarse_prev_edge];
const int start_edge_vertex = ctx->vertices_edge_offset +
coarse_prev_edge * num_subdiv_vertices_per_coarse_edge;
const bool flip = (coarse_edge[1] == coarse_vert);
int side_start_index = start_vertex_index + num_inner_vertices_per_ptex * corner;
for (int i = 0; i < ptex_face_resolution - 2; i++, subdiv_edge_index++) {
const int v1 = (flip) ? (start_edge_vertex + (resolution - i - 3)) :
(start_edge_vertex + i);
const int v2 = side_start_index + (ptex_face_inner_resolution + 1) * i;
ctx->foreach_context->edge(
ctx->foreach_context, tls, ORIGINDEX_NONE, subdiv_edge_index, false, v1, v2);
}
}
prev_corner = corner;
}
}
static void subdiv_foreach_edges_all_patches(SubdivForeachTaskContext *ctx,
void *tls,
const int coarse_face_index)
{
if (ctx->coarse_faces[coarse_face_index].size() == 4) {
subdiv_foreach_edges_all_patches_regular(ctx, tls, coarse_face_index);
}
else {
subdiv_foreach_edges_all_patches_special(ctx, tls, coarse_face_index);
}
}
static void subdiv_foreach_edges(SubdivForeachTaskContext *ctx, void *tls, int face_index)
{
subdiv_foreach_edges_all_patches(ctx, tls, face_index);
}
static void subdiv_foreach_boundary_edges(SubdivForeachTaskContext *ctx,
void *tls,
int coarse_edge_index)
{
const int2 &coarse_edge = ctx->coarse_edges[coarse_edge_index];
const int resolution = ctx->settings->resolution;
const int num_subdiv_vertices_per_coarse_edge = resolution - 2;
const int num_subdiv_edges_per_coarse_edge = resolution - 1;
const bool is_loose = !BLI_BITMAP_TEST_BOOL(ctx->coarse_edges_used_map, coarse_edge_index);
int subdiv_edge_index = ctx->edge_boundary_offset +
coarse_edge_index * num_subdiv_edges_per_coarse_edge;
int last_vertex_index = ctx->vertices_corner_offset + coarse_edge[0];
for (int i = 0; i < num_subdiv_edges_per_coarse_edge - 1; i++, subdiv_edge_index++) {
const int v1 = last_vertex_index;
const int v2 = ctx->vertices_edge_offset +
coarse_edge_index * num_subdiv_vertices_per_coarse_edge + i;
ctx->foreach_context->edge(
ctx->foreach_context, tls, coarse_edge_index, subdiv_edge_index, is_loose, v1, v2);
last_vertex_index = v2;
}
const int v1 = last_vertex_index;
const int v2 = ctx->vertices_corner_offset + coarse_edge[1];
ctx->foreach_context->edge(
ctx->foreach_context, tls, coarse_edge_index, subdiv_edge_index, is_loose, v1, v2);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Loops traversal
* \{ */
static void rotate_indices(const int rot, int *a, int *b, int *c, int *d)
{
const int values[4] = {*a, *b, *c, *d};
*a = values[(0 - rot + 4) % 4];
*b = values[(1 - rot + 4) % 4];
*c = values[(2 - rot + 4) % 4];
*d = values[(3 - rot + 4) % 4];
}
static void subdiv_foreach_loops_of_face(SubdivForeachTaskContext *ctx,
void *tls,
int subdiv_loop_start_index,
const int ptex_face_index,
const int coarse_face_index,
const int coarse_corner_index,
const int rotation,
/*const*/ int v0,
/*const*/ int e0,
/*const*/ int v1,
/*const*/ int e1,
/*const*/ int v2,
/*const*/ int e2,
/*const*/ int v3,
/*const*/ int e3,
const float u,
const float v,
const float du,
const float dv)
{
rotate_indices(rotation, &v0, &v1, &v2, &v3);
rotate_indices(rotation, &e0, &e1, &e2, &e3);
ctx->foreach_context->loop(ctx->foreach_context,
tls,
ptex_face_index,
u,
v,
ORIGINDEX_NONE,
coarse_face_index,
coarse_corner_index,
subdiv_loop_start_index + 0,
v0,
e0);
ctx->foreach_context->loop(ctx->foreach_context,
tls,
ptex_face_index,
u + du,
v,
ORIGINDEX_NONE,
coarse_face_index,
coarse_corner_index,
subdiv_loop_start_index + 1,
v1,
e1);
ctx->foreach_context->loop(ctx->foreach_context,
tls,
ptex_face_index,
u + du,
v + dv,
ORIGINDEX_NONE,
coarse_face_index,
coarse_corner_index,
subdiv_loop_start_index + 2,
v2,
e2);
ctx->foreach_context->loop(ctx->foreach_context,
tls,
ptex_face_index,
u,
v + dv,
ORIGINDEX_NONE,
coarse_face_index,
coarse_corner_index,
subdiv_loop_start_index + 3,
v3,
e3);
}
static int subdiv_foreach_loops_corner_index(const float u,
const float v,
const float du,
const float dv)
{
if (u + du <= 0.5f && v + dv <= 0.5f) {
return 0;
}
if (u >= 0.5f && v + dv <= 0.5f) {
return 1;
}
if (u >= 0.5f && v >= 0.5f) {
return 2;
}
return 3;
}
static void subdiv_foreach_loops_regular(SubdivForeachTaskContext *ctx,
void *tls,
const int coarse_face_index)
{
const int resolution = ctx->settings->resolution;
/* Base/coarse mesh information. */
const IndexRange coarse_face = ctx->coarse_faces[coarse_face_index];
const int ptex_resolution = ptex_face_resolution_get(coarse_face, resolution);
const int ptex_inner_resolution = ptex_resolution - 2;
const int num_subdiv_edges_per_coarse_edge = resolution - 1;
const int num_subdiv_vertices_per_coarse_edge = resolution - 2;
const float inv_ptex_resolution_1 = 1.0f / float(ptex_resolution - 1);
const int ptex_face_index = ctx->face_ptex_offset[coarse_face_index];
const int start_vertex_index = ctx->vertices_inner_offset +
ctx->subdiv_vertex_offset[coarse_face_index];
const int start_edge_index = ctx->edge_inner_offset + ctx->subdiv_edge_offset[coarse_face_index];
const int start_face_index = ctx->subdiv_face_offset[coarse_face_index];
const int start_loop_index = 4 * start_face_index;
const float du = inv_ptex_resolution_1;
const float dv = inv_ptex_resolution_1;
/* Hi-poly subdivided mesh. */
int subdiv_loop_index = start_loop_index;
/* Loops for inner part of ptex. */
for (int y = 1; y < ptex_resolution - 2; y++) {
const float v = y * inv_ptex_resolution_1;
const int inner_y = y - 1;
for (int x = 1; x < ptex_resolution - 2; x++, subdiv_loop_index += 4) {
const int inner_x = x - 1;
const float u = x * inv_ptex_resolution_1;
/* Vertex indices ordered counter-clockwise. */
const int v0 = start_vertex_index + (inner_y * ptex_inner_resolution + inner_x);
const int v1 = v0 + 1;
const int v2 = v0 + ptex_inner_resolution + 1;
const int v3 = v0 + ptex_inner_resolution;
/* Edge indices ordered counter-clockwise. */
const int e0 = start_edge_index + (inner_y * (2 * ptex_inner_resolution - 1) + inner_x);
const int e1 = e0 + ptex_inner_resolution;
const int e2 = e0 + (2 * ptex_inner_resolution - 1);
const int e3 = e0 + ptex_inner_resolution - 1;
subdiv_foreach_loops_of_face(ctx,
tls,
subdiv_loop_index,
ptex_face_index,
coarse_face_index,
subdiv_foreach_loops_corner_index(u, v, du, dv),
0,
v0,
e0,
v1,
e1,
v2,
e2,
v3,
e3,
u,
v,
du,
dv);
}
}
/* Loops for faces connecting inner ptex part with boundary. */
int prev_corner_index = coarse_face.size() - 1;
for (int corner = 0; corner < coarse_face.size(); corner++) {
const int coarse_vert = ctx->coarse_corner_verts[coarse_face[corner]];
const int coarse_edge_i = ctx->coarse_corner_edges[coarse_face[corner]];
const int coase_prev_vert = ctx->coarse_corner_verts[coarse_face[prev_corner_index]];
const int coarse_prev_edge = ctx->coarse_corner_edges[coarse_face[prev_corner_index]];
const int2 &coarse_edge = ctx->coarse_edges[coarse_edge_i];
const int2 &prev_coarse_edge = ctx->coarse_edges[coarse_prev_edge];
const int start_edge_vertex = ctx->vertices_edge_offset +
coarse_edge_i * num_subdiv_vertices_per_coarse_edge;
const bool flip = (coarse_edge[1] == coarse_vert);
int side_start_index = start_vertex_index;
int side_stride = 0;
int v0 = ctx->vertices_corner_offset + coarse_vert;
int v3, e3;
int e2_offset, e2_stride;
float u, v, delta_u, delta_v;
if (coase_prev_vert == prev_coarse_edge[0]) {
v3 = ctx->vertices_edge_offset + coarse_prev_edge * num_subdiv_vertices_per_coarse_edge +
num_subdiv_vertices_per_coarse_edge - 1;
e3 = ctx->edge_boundary_offset + coarse_prev_edge * num_subdiv_edges_per_coarse_edge +
num_subdiv_edges_per_coarse_edge - 1;
}
else {
v3 = ctx->vertices_edge_offset + coarse_prev_edge * num_subdiv_vertices_per_coarse_edge;
e3 = ctx->edge_boundary_offset + coarse_prev_edge * num_subdiv_edges_per_coarse_edge;
}
/* Calculate starting vertex of corresponding inner part of ptex. */
if (corner == 0) {
side_stride = 1;
e2_offset = 0;
e2_stride = 1;
u = 0.0f;
v = 0.0f;
delta_u = du;
delta_v = 0.0f;
}
else if (corner == 1) {
side_start_index += resolution - 3;
side_stride = resolution - 2;
e2_offset = 2 * num_subdiv_edges_per_coarse_edge - 4;
e2_stride = 2 * num_subdiv_edges_per_coarse_edge - 3;
u = 1.0f - du;
v = 0;
delta_u = 0.0f;
delta_v = dv;
}
else if (corner == 2) {
side_start_index += num_subdiv_vertices_per_coarse_edge *
num_subdiv_vertices_per_coarse_edge -
1;
side_stride = -1;
e2_offset = num_edges_per_ptex_face_get(resolution - 2) - 1;
e2_stride = -1;
u = 1.0f - du;
v = 1.0f - dv;
delta_u = -du;
delta_v = 0.0f;
}
else if (corner == 3) {
side_start_index += num_subdiv_vertices_per_coarse_edge *
(num_subdiv_vertices_per_coarse_edge - 1);
side_stride = -(resolution - 2);
e2_offset = num_edges_per_ptex_face_get(resolution - 2) -
(2 * num_subdiv_edges_per_coarse_edge - 3);
e2_stride = -(2 * num_subdiv_edges_per_coarse_edge - 3);
u = 0.0f;
v = 1.0f - dv;
delta_u = 0.0f;
delta_v = -dv;
}
for (int i = 0; i < resolution - 2; i++, subdiv_loop_index += 4) {
int v1;
if (flip) {
v1 = start_edge_vertex + (resolution - i - 3);
}
else {
v1 = start_edge_vertex + i;
}
const int v2 = side_start_index + side_stride * i;
int e0;
if (flip) {
e0 = ctx->edge_boundary_offset + coarse_edge_i * num_subdiv_edges_per_coarse_edge +
num_subdiv_edges_per_coarse_edge - i - 1;
}
else {
e0 = ctx->edge_boundary_offset + coarse_edge_i * num_subdiv_edges_per_coarse_edge + i;
}
int e1 = start_edge_index + num_edges_per_ptex_face_get(resolution - 2) +
corner * num_subdiv_vertices_per_coarse_edge + i;
int e2;
if (i == 0) {
e2 = start_edge_index + num_edges_per_ptex_face_get(resolution - 2) +
((corner - 1 + coarse_face.size()) % coarse_face.size()) *
num_subdiv_vertices_per_coarse_edge +
num_subdiv_vertices_per_coarse_edge - 1;
}
else {
e2 = start_edge_index + e2_offset + e2_stride * (i - 1);
}
const float loop_u = u + delta_u * i;
const float loop_v = v + delta_v * i;
subdiv_foreach_loops_of_face(ctx,
tls,
subdiv_loop_index,
ptex_face_index,
coarse_face_index,
subdiv_foreach_loops_corner_index(loop_u, loop_v, du, dv),
corner,
v0,
e0,
v1,
e1,
v2,
e2,
v3,
e3,
loop_u,
loop_v,
du,
dv);
v0 = v1;
v3 = v2;
e3 = e1;
}
prev_corner_index = corner;
}
}
static void subdiv_foreach_loops_special(SubdivForeachTaskContext *ctx,
void *tls,
const int coarse_face_index)
{
const int resolution = ctx->settings->resolution;
/* Base/coarse mesh information. */
const IndexRange coarse_face = ctx->coarse_faces[coarse_face_index];
const int ptex_face_resolution = ptex_face_resolution_get(coarse_face, resolution);
const int ptex_face_inner_resolution = ptex_face_resolution - 2;
const float inv_ptex_resolution_1 = 1.0f / float(ptex_face_resolution - 1);
const int num_inner_vertices_per_ptex = (ptex_face_resolution - 1) * (ptex_face_resolution - 2);
const int num_inner_edges_per_ptex_face = num_inner_edges_per_ptex_face_get(
ptex_face_inner_resolution + 1);
const int num_subdiv_vertices_per_coarse_edge = resolution - 2;
const int num_subdiv_edges_per_coarse_edge = resolution - 1;
const int ptex_face_index = ctx->face_ptex_offset[coarse_face_index];
const int center_vertex_index = ctx->vertices_inner_offset +
ctx->subdiv_vertex_offset[coarse_face_index];
const int start_vertex_index = center_vertex_index + 1;
const int start_inner_vertex_index = center_vertex_index + 1;
const int start_edge_index = ctx->edge_inner_offset + ctx->subdiv_edge_offset[coarse_face_index];
const int start_face_index = ctx->subdiv_face_offset[coarse_face_index];
const int start_loop_index = 4 * start_face_index;
const float du = inv_ptex_resolution_1;
const float dv = inv_ptex_resolution_1;
/* Hi-poly subdivided mesh. */
int subdiv_loop_index = start_loop_index;
for (int corner = 0; corner < coarse_face.size(); corner++) {
const int corner_vertex_index = start_vertex_index + corner * num_inner_vertices_per_ptex;
const int corner_edge_index = start_edge_index + corner * num_inner_edges_per_ptex_face;
for (int y = 1; y < ptex_face_inner_resolution; y++) {
const float v = y * inv_ptex_resolution_1;
const int inner_y = y - 1;
for (int x = 1; x < ptex_face_inner_resolution + 1; x++, subdiv_loop_index += 4) {
const int inner_x = x - 1;
const float u = x * inv_ptex_resolution_1;
/* Vertex indices ordered counter-clockwise. */
const int v0 = corner_vertex_index +
(inner_y * (ptex_face_inner_resolution + 1) + inner_x);
const int v1 = v0 + 1;
const int v2 = v0 + ptex_face_inner_resolution + 2;
const int v3 = v0 + ptex_face_inner_resolution + 1;
/* Edge indices ordered counter-clockwise. */
const int e0 = corner_edge_index +
(inner_y * (2 * ptex_face_inner_resolution + 1) + inner_x);
const int e1 = e0 + ptex_face_inner_resolution + 1;
const int e2 = e0 + (2 * ptex_face_inner_resolution + 1);
const int e3 = e0 + ptex_face_inner_resolution;
subdiv_foreach_loops_of_face(ctx,
tls,
subdiv_loop_index,
ptex_face_index + corner,
coarse_face_index,
corner,
0,
v0,
e0,
v1,
e1,
v2,
e2,
v3,
e3,
u,
v,
du,
dv);
}
}
}
/* Create connections between ptex faces. */
for (int corner = 0; corner < coarse_face.size(); corner++) {
const int next_corner = (corner + 1) % coarse_face.size();
const int corner_edge_index = start_edge_index + corner * num_inner_edges_per_ptex_face;
const int next_corner_edge_index = start_edge_index +
next_corner * num_inner_edges_per_ptex_face;
int current_patch_vertex_index = start_inner_vertex_index +
corner * num_inner_vertices_per_ptex +
ptex_face_inner_resolution;
int next_path_vertex_index = start_inner_vertex_index +
next_corner * num_inner_vertices_per_ptex +
num_inner_vertices_per_ptex - ptex_face_resolution + 1;
int v0 = current_patch_vertex_index;
int v1 = next_path_vertex_index;
current_patch_vertex_index += ptex_face_inner_resolution + 1;
next_path_vertex_index += 1;
int e0 = start_edge_index + coarse_face.size() * num_inner_edges_per_ptex_face +
corner * (ptex_face_resolution - 2);
int e1 = next_corner_edge_index + num_inner_edges_per_ptex_face - ptex_face_resolution + 2;
int e3 = corner_edge_index + 2 * ptex_face_resolution - 4;
for (int row = 1; row < ptex_face_inner_resolution; row++, subdiv_loop_index += 4) {
const int v2 = next_path_vertex_index;
const int v3 = current_patch_vertex_index;
const int e2 = e0 + 1;
const float u = row * du;
const float v = 1.0f - dv;
subdiv_foreach_loops_of_face(ctx,
tls,
subdiv_loop_index,
ptex_face_index + next_corner,
coarse_face_index,
next_corner,
3,
v0,
e0,
v1,
e1,
v2,
e2,
v3,
e3,
u,
v,
du,
dv);
current_patch_vertex_index += ptex_face_inner_resolution + 1;
next_path_vertex_index += 1;
v0 = v3;
v1 = v2;
e0 = e2;
e1 += 1;
e3 += 2 * ptex_face_resolution - 3;
}
}
/* Create loops from center. */
if (ptex_face_resolution >= 3) {
const int start_center_edge_index = start_edge_index + (num_inner_edges_per_ptex_face +
ptex_face_inner_resolution) *
coarse_face.size();
const int start_boundary_edge = start_edge_index +
coarse_face.size() * num_inner_edges_per_ptex_face +
ptex_face_inner_resolution - 1;
for (int corner = 0, prev_corner = coarse_face.size() - 1; corner < coarse_face.size();
prev_corner = corner, corner++, subdiv_loop_index += 4)
{
const int corner_edge_index = start_edge_index + corner * num_inner_edges_per_ptex_face;
const int current_patch_end_vertex_index = start_vertex_index +
corner * num_inner_vertices_per_ptex +
num_inner_vertices_per_ptex - 1;
const int prev_current_patch_end_vertex_index = start_vertex_index +
prev_corner * num_inner_vertices_per_ptex +
num_inner_vertices_per_ptex - 1;
const int v0 = center_vertex_index;
const int v1 = prev_current_patch_end_vertex_index;
const int v2 = current_patch_end_vertex_index - 1;
const int v3 = current_patch_end_vertex_index;
const int e0 = start_center_edge_index + prev_corner;
const int e1 = start_boundary_edge + prev_corner * (ptex_face_inner_resolution);
const int e2 = corner_edge_index + num_inner_edges_per_ptex_face - 1;
const int e3 = start_center_edge_index + corner;
const float u = 1.0f - du;
const float v = 1.0f - dv;
subdiv_foreach_loops_of_face(ctx,
tls,
subdiv_loop_index,
ptex_face_index + corner,
coarse_face_index,
corner,
2,
v0,
e0,
v1,
e1,
v2,
e2,
v3,
e3,
u,
v,
du,
dv);
}
}
/* Loops for faces connecting inner ptex part with boundary. */
for (int prev_corner = coarse_face.size() - 1, corner = 0; corner < coarse_face.size();
prev_corner = corner, corner++)
{
const int coarse_vert = ctx->coarse_corner_verts[coarse_face[corner]];
const int coarse_edge_i = ctx->coarse_corner_edges[coarse_face[corner]];
const int coase_prev_vert = ctx->coarse_corner_verts[coarse_face[prev_corner]];
const int coarse_prev_edge = ctx->coarse_corner_edges[coarse_face[prev_corner]];
const int2 &coarse_edge = ctx->coarse_edges[coarse_edge_i];
const int2 &prev_coarse_edge = ctx->coarse_edges[coarse_prev_edge];
const bool flip = (coarse_edge[1] == coarse_vert);
const int start_edge_vertex = ctx->vertices_edge_offset +
coarse_edge_i * num_subdiv_vertices_per_coarse_edge;
const int corner_vertex_index = start_vertex_index + corner * num_inner_vertices_per_ptex;
const int corner_edge_index = start_edge_index + corner * num_inner_edges_per_ptex_face;
/* Create loops for polygons along U axis. */
int v0 = ctx->vertices_corner_offset + coarse_vert;
int v3, e3;
if (coase_prev_vert == prev_coarse_edge[0]) {
v3 = ctx->vertices_edge_offset + coarse_prev_edge * num_subdiv_vertices_per_coarse_edge +
num_subdiv_vertices_per_coarse_edge - 1;
e3 = ctx->edge_boundary_offset + coarse_prev_edge * num_subdiv_edges_per_coarse_edge +
num_subdiv_edges_per_coarse_edge - 1;
}
else {
v3 = ctx->vertices_edge_offset + coarse_prev_edge * num_subdiv_vertices_per_coarse_edge;
e3 = ctx->edge_boundary_offset + coarse_prev_edge * num_subdiv_edges_per_coarse_edge;
}
for (int i = 0; i <= ptex_face_inner_resolution; i++, subdiv_loop_index += 4) {
int v1;
if (flip) {
v1 = start_edge_vertex + (resolution - i - 3);
}
else {
v1 = start_edge_vertex + i;
}
int v2;
if (ptex_face_inner_resolution >= 1) {
v2 = corner_vertex_index + i;
}
else {
v2 = center_vertex_index;
}
int e0;
if (flip) {
e0 = ctx->edge_boundary_offset + coarse_edge_i * num_subdiv_edges_per_coarse_edge +
num_subdiv_edges_per_coarse_edge - i - 1;
}
else {
e0 = ctx->edge_boundary_offset + coarse_edge_i * num_subdiv_edges_per_coarse_edge + i;
}
int e1 = start_edge_index + corner * (2 * ptex_face_inner_resolution + 1);
if (ptex_face_resolution >= 3) {
e1 += coarse_face.size() *
(num_inner_edges_per_ptex_face + ptex_face_inner_resolution + 1) +
i;
}
int e2 = 0;
if (i == 0 && ptex_face_resolution >= 3) {
e2 = start_edge_index +
coarse_face.size() *
(num_inner_edges_per_ptex_face + ptex_face_inner_resolution + 1) +
corner * (2 * ptex_face_inner_resolution + 1) + ptex_face_inner_resolution + 1;
}
else if (i == 0 && ptex_face_resolution < 3) {
e2 = start_edge_index + prev_corner * (2 * ptex_face_inner_resolution + 1);
}
else {
e2 = corner_edge_index + i - 1;
}
const float u = du * i;
const float v = 0.0f;
subdiv_foreach_loops_of_face(ctx,
tls,
subdiv_loop_index,
ptex_face_index + corner,
coarse_face_index,
corner,
0,
v0,
e0,
v1,
e1,
v2,
e2,
v3,
e3,
u,
v,
du,
dv);
v0 = v1;
v3 = v2;
e3 = e1;
}
/* Create loops for polygons along V axis. */
const bool flip_prev = (prev_coarse_edge[1] == coarse_vert);
v0 = corner_vertex_index;
if (coase_prev_vert == prev_coarse_edge[0]) {
v3 = ctx->vertices_edge_offset + coarse_prev_edge * num_subdiv_vertices_per_coarse_edge +
num_subdiv_vertices_per_coarse_edge - 1;
}
else {
v3 = ctx->vertices_edge_offset + coarse_prev_edge * num_subdiv_vertices_per_coarse_edge;
}
e3 = start_edge_index +
coarse_face.size() * (num_inner_edges_per_ptex_face + ptex_face_inner_resolution + 1) +
corner * (2 * ptex_face_inner_resolution + 1) + ptex_face_inner_resolution + 1;
for (int i = 0; i <= ptex_face_inner_resolution - 1; i++, subdiv_loop_index += 4) {
int v1;
int e0, e1;
if (i == ptex_face_inner_resolution - 1) {
v1 = start_vertex_index + prev_corner * num_inner_vertices_per_ptex +
ptex_face_inner_resolution;
e1 = start_edge_index +
coarse_face.size() *
(num_inner_edges_per_ptex_face + ptex_face_inner_resolution + 1) +
prev_corner * (2 * ptex_face_inner_resolution + 1) + ptex_face_inner_resolution;
e0 = start_edge_index + coarse_face.size() * num_inner_edges_per_ptex_face +
prev_corner * ptex_face_inner_resolution;
}
else {
v1 = v0 + ptex_face_inner_resolution + 1;
e0 = corner_edge_index + ptex_face_inner_resolution +
i * (2 * ptex_face_inner_resolution + 1);
e1 = e3 + 1;
}
int v2 = flip_prev ? v3 - 1 : v3 + 1;
int e2;
if (flip_prev) {
e2 = ctx->edge_boundary_offset + coarse_prev_edge * num_subdiv_edges_per_coarse_edge +
num_subdiv_edges_per_coarse_edge - 2 - i;
}
else {
e2 = ctx->edge_boundary_offset + coarse_prev_edge * num_subdiv_edges_per_coarse_edge + 1 +
i;
}
const float u = 0.0f;
const float v = du * (i + 1);
subdiv_foreach_loops_of_face(ctx,
tls,
subdiv_loop_index,
ptex_face_index + corner,
coarse_face_index,
corner,
1,
v0,
e0,
v1,
e1,
v2,
e2,
v3,
e3,
u,
v,
du,
dv);
v0 = v1;
v3 = v2;
e3 = e1;
}
}
}
static void subdiv_foreach_loops(SubdivForeachTaskContext *ctx, void *tls, int face_index)
{
if (ctx->coarse_faces[face_index].size() == 4) {
subdiv_foreach_loops_regular(ctx, tls, face_index);
}
else {
subdiv_foreach_loops_special(ctx, tls, face_index);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Polygons traverse process
* \{ */
static void subdiv_foreach_faces(SubdivForeachTaskContext *ctx, void *tls, int face_index)
{
const int resolution = ctx->settings->resolution;
const int start_face_index = ctx->subdiv_face_offset[face_index];
/* Base/coarse mesh information. */
const IndexRange coarse_face = ctx->coarse_faces[face_index];
const int num_ptex_faces_per_face = num_ptex_faces_per_face_get(coarse_face);
const int ptex_resolution = ptex_face_resolution_get(coarse_face, resolution);
const int num_faces_per_ptex = num_faces_per_ptex_get(ptex_resolution);
const int num_loops_per_ptex = 4 * num_faces_per_ptex;
const int start_loop_index = 4 * start_face_index;
/* Hi-poly subdivided mesh. */
int subdiv_faceon_index = start_face_index;
for (int ptex_of_face_index = 0; ptex_of_face_index < num_ptex_faces_per_face;
ptex_of_face_index++)
{
for (int subdiv_face_index = 0; subdiv_face_index < num_faces_per_ptex;
subdiv_face_index++, subdiv_faceon_index++)
{
const int loopstart = start_loop_index + (ptex_of_face_index * num_loops_per_ptex) +
(subdiv_face_index * 4);
ctx->foreach_context->poly(
ctx->foreach_context, tls, face_index, subdiv_faceon_index, loopstart, 4);
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Loose elements traverse process
* \{ */
static void subdiv_foreach_loose_vertices_task(void *__restrict userdata,
const int coarse_vertex_index,
const TaskParallelTLS *__restrict tls)
{
SubdivForeachTaskContext *ctx = static_cast<SubdivForeachTaskContext *>(userdata);
if (BLI_BITMAP_TEST_BOOL(ctx->coarse_vertices_used_map, coarse_vertex_index)) {
/* Vertex is not loose, was handled when handling polygons. */
return;
}
const int subdiv_vertex_index = ctx->vertices_corner_offset + coarse_vertex_index;
ctx->foreach_context->vertex_loose(
ctx->foreach_context, tls->userdata_chunk, coarse_vertex_index, subdiv_vertex_index);
}
static void subdiv_foreach_vertices_of_loose_edges_task(void *__restrict userdata,
const int coarse_edge_index,
const TaskParallelTLS *__restrict tls)
{
SubdivForeachTaskContext *ctx = static_cast<SubdivForeachTaskContext *>(userdata);
if (BLI_BITMAP_TEST_BOOL(ctx->coarse_edges_used_map, coarse_edge_index)) {
/* Vertex is not loose, was handled when handling polygons. */
return;
}
const int resolution = ctx->settings->resolution;
const int resolution_1 = resolution - 1;
const float inv_resolution_1 = 1.0f / float(resolution_1);
const int num_subdiv_vertices_per_coarse_edge = resolution - 2;
const int2 &coarse_edge = ctx->coarse_edges[coarse_edge_index];
/* Subdivision vertices which corresponds to edge's v1 and v2. */
const int subdiv_v1_index = ctx->vertices_corner_offset + coarse_edge[0];
const int subdiv_v2_index = ctx->vertices_corner_offset + coarse_edge[1];
/* First subdivided inner vertex of the edge. */
const int subdiv_start_vertex = ctx->vertices_edge_offset +
coarse_edge_index * num_subdiv_vertices_per_coarse_edge;
/* Perform interpolation. */
for (int i = 0; i < resolution; i++) {
const float u = i * inv_resolution_1;
int subdiv_vertex_index;
if (i == 0) {
subdiv_vertex_index = subdiv_v1_index;
}
else if (i == resolution - 1) {
subdiv_vertex_index = subdiv_v2_index;
}
else {
subdiv_vertex_index = subdiv_start_vertex + (i - 1);
}
ctx->foreach_context->vertex_of_loose_edge(
ctx->foreach_context, tls->userdata_chunk, coarse_edge_index, u, subdiv_vertex_index);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Subdivision process entry points
* \{ */
static void subdiv_foreach_single_geometry_vertices(SubdivForeachTaskContext *ctx, void *tls)
{
if (ctx->foreach_context->vertex_corner == nullptr) {
return;
}
const Mesh *coarse_mesh = ctx->coarse_mesh;
for (int face_index = 0; face_index < coarse_mesh->faces_num; face_index++) {
subdiv_foreach_corner_vertices(ctx, tls, face_index);
subdiv_foreach_edge_vertices(ctx, tls, face_index);
}
}
static void subdiv_foreach_mark_non_loose_geometry(SubdivForeachTaskContext *ctx)
{
for (const int face_index : ctx->coarse_faces.index_range()) {
for (const int corner : ctx->coarse_faces[face_index]) {
BLI_BITMAP_ENABLE(ctx->coarse_vertices_used_map, ctx->coarse_corner_verts[corner]);
BLI_BITMAP_ENABLE(ctx->coarse_edges_used_map, ctx->coarse_corner_edges[corner]);
}
}
}
static void subdiv_foreach_single_thread_tasks(SubdivForeachTaskContext *ctx)
{
/* NOTE: In theory, we can try to skip allocation of TLS here, but in
* practice if the callbacks used here are not specified then TLS will not
* be requested anyway. */
void *tls = subdiv_foreach_tls_alloc(ctx);
/* Passes to average displacement on the corner vertices
* and boundary edges. */
subdiv_foreach_every_corner_vertices(ctx, tls);
subdiv_foreach_every_edge_vertices(ctx, tls);
/* Run callbacks which are supposed to be run once per shared geometry. */
subdiv_foreach_single_geometry_vertices(ctx, tls);
subdiv_foreach_tls_free(ctx, tls);
const SubdivForeachContext *foreach_context = ctx->foreach_context;
const bool is_loose_geometry_tagged = (foreach_context->vertex_every_edge != nullptr &&
foreach_context->vertex_every_corner != nullptr);
const bool is_loose_geometry_tags_needed = (foreach_context->vertex_loose != nullptr ||
foreach_context->vertex_of_loose_edge != nullptr);
if (is_loose_geometry_tagged && is_loose_geometry_tags_needed) {
subdiv_foreach_mark_non_loose_geometry(ctx);
}
}
static void subdiv_foreach_task(void *__restrict userdata,
const int face_index,
const TaskParallelTLS *__restrict tls)
{
SubdivForeachTaskContext *ctx = static_cast<SubdivForeachTaskContext *>(userdata);
/* Traverse hi-poly vertex coordinates and normals. */
subdiv_foreach_vertices(ctx, tls->userdata_chunk, face_index);
/* Traverse mesh geometry for the given base poly index. */
if (ctx->foreach_context->edge != nullptr) {
subdiv_foreach_edges(ctx, tls->userdata_chunk, face_index);
}
if (ctx->foreach_context->loop != nullptr) {
subdiv_foreach_loops(ctx, tls->userdata_chunk, face_index);
}
if (ctx->foreach_context->poly != nullptr) {
subdiv_foreach_faces(ctx, tls->userdata_chunk, face_index);
}
}
static void subdiv_foreach_boundary_edges_task(void *__restrict userdata,
const int edge_index,
const TaskParallelTLS *__restrict tls)
{
SubdivForeachTaskContext *ctx = static_cast<SubdivForeachTaskContext *>(userdata);
subdiv_foreach_boundary_edges(ctx, tls->userdata_chunk, edge_index);
}
static void subdiv_foreach_free(const void *__restrict userdata, void *__restrict userdata_chunk)
{
const SubdivForeachTaskContext *ctx = static_cast<const SubdivForeachTaskContext *>(userdata);
ctx->foreach_context->user_data_tls_free(userdata_chunk);
}
bool BKE_subdiv_foreach_subdiv_geometry(Subdiv *subdiv,
const SubdivForeachContext *context,
const SubdivToMeshSettings *mesh_settings,
const Mesh *coarse_mesh)
{
SubdivForeachTaskContext ctx = {nullptr};
ctx.coarse_mesh = coarse_mesh;
ctx.coarse_edges = coarse_mesh->edges();
ctx.coarse_faces = coarse_mesh->faces();
ctx.coarse_corner_verts = coarse_mesh->corner_verts();
ctx.coarse_corner_edges = coarse_mesh->corner_edges();
ctx.settings = mesh_settings;
ctx.foreach_context = context;
subdiv_foreach_ctx_init(subdiv, &ctx);
if (context->topology_info != nullptr) {
if (!context->topology_info(context,
ctx.num_subdiv_vertices,
ctx.num_subdiv_edges,
ctx.num_subdiv_loops,
ctx.num_subdiv_faces,
ctx.subdiv_face_offset))
{
subdiv_foreach_ctx_free(&ctx);
return false;
}
}
/* Run all the code which is not supposed to be run from threads. */
subdiv_foreach_single_thread_tasks(&ctx);
/* Threaded traversal of the rest of topology. */
TaskParallelSettings parallel_range_settings;
BLI_parallel_range_settings_defaults(&parallel_range_settings);
parallel_range_settings.userdata_chunk = context->user_data_tls;
parallel_range_settings.userdata_chunk_size = context->user_data_tls_size;
parallel_range_settings.min_iter_per_thread = 1;
if (context->user_data_tls_free != nullptr) {
parallel_range_settings.func_free = subdiv_foreach_free;
}
/* TODO(sergey): Possible optimization is to have a single pool and push all
* the tasks into it.
* NOTE: Watch out for callbacks which needs to run for loose geometry as they
* currently are relying on the fact that face/grid callbacks will tag non-
* loose geometry. */
BLI_task_parallel_range(
0, coarse_mesh->faces_num, &ctx, subdiv_foreach_task, &parallel_range_settings);
if (context->vertex_loose != nullptr) {
BLI_task_parallel_range(0,
coarse_mesh->totvert,
&ctx,
subdiv_foreach_loose_vertices_task,
&parallel_range_settings);
}
if (context->vertex_of_loose_edge != nullptr) {
BLI_task_parallel_range(0,
coarse_mesh->totedge,
&ctx,
subdiv_foreach_vertices_of_loose_edges_task,
&parallel_range_settings);
}
if (context->edge != nullptr) {
BLI_task_parallel_range(0,
coarse_mesh->totedge,
&ctx,
subdiv_foreach_boundary_edges_task,
&parallel_range_settings);
}
subdiv_foreach_ctx_free(&ctx);
return true;
}
/** \} */