This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/bmesh/intern/bmesh_mesh_tessellate.c
Campbell Barton 2d60c496a2 Mesh: minor optimization to concave quad check for tessellation
Use the face normal (when available) for a faster concave quad test.
2021-06-20 14:39:13 +10:00

581 lines
18 KiB
C

/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup bmesh
*
* This file contains code for polygon tessellation
* (creating triangles from polygons).
*
* \see mesh_tessellate.c for the #Mesh equivalent of this file.
*/
#include "DNA_meshdata_types.h"
#include "MEM_guardedalloc.h"
#include "BLI_alloca.h"
#include "BLI_heap.h"
#include "BLI_linklist.h"
#include "BLI_math.h"
#include "BLI_memarena.h"
#include "BLI_polyfill_2d.h"
#include "BLI_polyfill_2d_beautify.h"
#include "BLI_task.h"
#include "bmesh.h"
#include "bmesh_tools.h"
/**
* On systems with 32+ cores,
* only a very small number of faces has any advantage single threading (in the 100's).
* Note that between 500-2000 quads, the difference isn't so much
* (tessellation isn't a bottleneck in this case anyway).
* Avoid the slight overhead of using threads in this case.
*/
#define BM_FACE_TESSELLATE_THREADED_LIMIT 1024
/* -------------------------------------------------------------------- */
/** \name Default Mesh Tessellation
* \{ */
/**
* \param face_normal: This will be optimized out as a constant.
*/
BLI_INLINE void bmesh_calc_tessellation_for_face_impl(BMLoop *(*looptris)[3],
BMFace *efa,
MemArena **pf_arena_p,
const bool face_normal)
{
#ifdef DEBUG
/* The face normal is used for projecting faces into 2D space for tessellation.
* Invalid normals may result in invalid tessellation.
* Either `face_normal` should be true or normals should be updated first. */
BLI_assert(face_normal || BM_face_is_normal_valid(efa));
#endif
switch (efa->len) {
case 3: {
/* `0 1 2` -> `0 1 2` */
BMLoop *l;
BMLoop **l_ptr = looptris[0];
l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa);
l_ptr[1] = l = l->next;
l_ptr[2] = l->next;
if (face_normal) {
normal_tri_v3(efa->no, l_ptr[0]->v->co, l_ptr[1]->v->co, l_ptr[2]->v->co);
}
break;
}
case 4: {
/* `0 1 2 3` -> (`0 1 2`, `0 2 3`) */
BMLoop *l;
BMLoop **l_ptr_a = looptris[0];
BMLoop **l_ptr_b = looptris[1];
(l_ptr_a[0] = l_ptr_b[0] = l = BM_FACE_FIRST_LOOP(efa));
(l_ptr_a[1] = l = l->next);
(l_ptr_a[2] = l_ptr_b[1] = l = l->next);
(l_ptr_b[2] = l->next);
if (face_normal) {
normal_quad_v3(
efa->no, l_ptr_a[0]->v->co, l_ptr_a[1]->v->co, l_ptr_a[2]->v->co, l_ptr_b[2]->v->co);
}
if (UNLIKELY(is_quad_flip_v3_first_third_fast_with_normal(l_ptr_a[0]->v->co,
l_ptr_a[1]->v->co,
l_ptr_a[2]->v->co,
l_ptr_b[2]->v->co,
efa->no))) {
/* Flip out of degenerate 0-2 state. */
l_ptr_a[2] = l_ptr_b[2];
l_ptr_b[0] = l_ptr_a[1];
}
break;
}
default: {
if (face_normal) {
BM_face_calc_normal(efa, efa->no);
}
BMLoop *l_iter, *l_first;
BMLoop **l_arr;
float axis_mat[3][3];
float(*projverts)[2];
uint(*tris)[3];
const int tris_len = efa->len - 2;
MemArena *pf_arena = *pf_arena_p;
if (UNLIKELY(pf_arena == NULL)) {
pf_arena = *pf_arena_p = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
}
tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * tris_len);
l_arr = BLI_memarena_alloc(pf_arena, sizeof(*l_arr) * efa->len);
projverts = BLI_memarena_alloc(pf_arena, sizeof(*projverts) * efa->len);
axis_dominant_v3_to_m3_negate(axis_mat, efa->no);
int i = 0;
l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
do {
l_arr[i] = l_iter;
mul_v2_m3v3(projverts[i], axis_mat, l_iter->v->co);
i++;
} while ((l_iter = l_iter->next) != l_first);
BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, pf_arena);
for (i = 0; i < tris_len; i++) {
BMLoop **l_ptr = looptris[i];
uint *tri = tris[i];
l_ptr[0] = l_arr[tri[0]];
l_ptr[1] = l_arr[tri[1]];
l_ptr[2] = l_arr[tri[2]];
}
BLI_memarena_clear(pf_arena);
break;
}
}
}
static void bmesh_calc_tessellation_for_face(BMLoop *(*looptris)[3],
BMFace *efa,
MemArena **pf_arena_p)
{
bmesh_calc_tessellation_for_face_impl(looptris, efa, pf_arena_p, false);
}
static void bmesh_calc_tessellation_for_face_with_normal(BMLoop *(*looptris)[3],
BMFace *efa,
MemArena **pf_arena_p)
{
bmesh_calc_tessellation_for_face_impl(looptris, efa, pf_arena_p, true);
}
/**
* \brief BM_mesh_calc_tessellation get the looptris and its number from a certain bmesh
* \param looptris:
*
* \note \a looptris Must be pre-allocated to at least the size of given by: poly_to_tri_count
*/
static void bm_mesh_calc_tessellation__single_threaded(BMesh *bm,
BMLoop *(*looptris)[3],
const char face_normals)
{
#ifndef NDEBUG
const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
#endif
BMIter iter;
BMFace *efa;
int i = 0;
MemArena *pf_arena = NULL;
if (face_normals) {
BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
BLI_assert(efa->len >= 3);
BM_face_calc_normal(efa, efa->no);
bmesh_calc_tessellation_for_face_with_normal(looptris + i, efa, &pf_arena);
i += efa->len - 2;
}
}
else {
BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
BLI_assert(efa->len >= 3);
bmesh_calc_tessellation_for_face(looptris + i, efa, &pf_arena);
i += efa->len - 2;
}
}
if (pf_arena) {
BLI_memarena_free(pf_arena);
pf_arena = NULL;
}
BLI_assert(i <= looptris_tot);
}
struct TessellationUserTLS {
MemArena *pf_arena;
};
static void bmesh_calc_tessellation_for_face_fn(void *__restrict userdata,
MempoolIterData *mp_f,
const TaskParallelTLS *__restrict tls)
{
struct TessellationUserTLS *tls_data = tls->userdata_chunk;
BMLoop *(*looptris)[3] = userdata;
BMFace *f = (BMFace *)mp_f;
BMLoop *l = BM_FACE_FIRST_LOOP(f);
const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2);
bmesh_calc_tessellation_for_face(looptris + offset, f, &tls_data->pf_arena);
}
static void bmesh_calc_tessellation_for_face_with_normals_fn(void *__restrict userdata,
MempoolIterData *mp_f,
const TaskParallelTLS *__restrict tls)
{
struct TessellationUserTLS *tls_data = tls->userdata_chunk;
BMLoop *(*looptris)[3] = userdata;
BMFace *f = (BMFace *)mp_f;
BMLoop *l = BM_FACE_FIRST_LOOP(f);
const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2);
bmesh_calc_tessellation_for_face_with_normal(looptris + offset, f, &tls_data->pf_arena);
}
static void bmesh_calc_tessellation_for_face_free_fn(const void *__restrict UNUSED(userdata),
void *__restrict tls_v)
{
struct TessellationUserTLS *tls_data = tls_v;
if (tls_data->pf_arena) {
BLI_memarena_free(tls_data->pf_arena);
}
}
static void bm_mesh_calc_tessellation__multi_threaded(BMesh *bm,
BMLoop *(*looptris)[3],
const char face_normals)
{
BM_mesh_elem_index_ensure(bm, BM_LOOP | BM_FACE);
TaskParallelSettings settings;
struct TessellationUserTLS tls_dummy = {NULL};
BLI_parallel_mempool_settings_defaults(&settings);
settings.userdata_chunk = &tls_dummy;
settings.userdata_chunk_size = sizeof(tls_dummy);
settings.func_free = bmesh_calc_tessellation_for_face_free_fn;
BM_iter_parallel(bm,
BM_FACES_OF_MESH,
face_normals ? bmesh_calc_tessellation_for_face_with_normals_fn :
bmesh_calc_tessellation_for_face_fn,
looptris,
&settings);
}
void BM_mesh_calc_tessellation_ex(BMesh *bm,
BMLoop *(*looptris)[3],
const struct BMeshCalcTessellation_Params *params)
{
if (bm->totface < BM_FACE_TESSELLATE_THREADED_LIMIT) {
bm_mesh_calc_tessellation__single_threaded(bm, looptris, params->face_normals);
}
else {
bm_mesh_calc_tessellation__multi_threaded(bm, looptris, params->face_normals);
}
}
void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3])
{
BM_mesh_calc_tessellation_ex(bm,
looptris,
&(const struct BMeshCalcTessellation_Params){
.face_normals = false,
});
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Default Tessellation (Partial Updates)
* \{ */
struct PartialTessellationUserData {
BMFace **faces;
BMLoop *(*looptris)[3];
};
struct PartialTessellationUserTLS {
MemArena *pf_arena;
};
static void bmesh_calc_tessellation_for_face_partial_fn(void *__restrict userdata,
const int index,
const TaskParallelTLS *__restrict tls)
{
struct PartialTessellationUserTLS *tls_data = tls->userdata_chunk;
struct PartialTessellationUserData *data = userdata;
BMFace *f = data->faces[index];
BMLoop *l = BM_FACE_FIRST_LOOP(f);
const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2);
bmesh_calc_tessellation_for_face(data->looptris + offset, f, &tls_data->pf_arena);
}
static void bmesh_calc_tessellation_for_face_partial_with_normals_fn(
void *__restrict userdata, const int index, const TaskParallelTLS *__restrict tls)
{
struct PartialTessellationUserTLS *tls_data = tls->userdata_chunk;
struct PartialTessellationUserData *data = userdata;
BMFace *f = data->faces[index];
BMLoop *l = BM_FACE_FIRST_LOOP(f);
const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2);
bmesh_calc_tessellation_for_face_with_normal(data->looptris + offset, f, &tls_data->pf_arena);
}
static void bmesh_calc_tessellation_for_face_partial_free_fn(
const void *__restrict UNUSED(userdata), void *__restrict tls_v)
{
struct PartialTessellationUserTLS *tls_data = tls_v;
if (tls_data->pf_arena) {
BLI_memarena_free(tls_data->pf_arena);
}
}
static void bm_mesh_calc_tessellation_with_partial__multi_threaded(
BMLoop *(*looptris)[3],
const BMPartialUpdate *bmpinfo,
const struct BMeshCalcTessellation_Params *params)
{
const int faces_len = bmpinfo->faces_len;
BMFace **faces = bmpinfo->faces;
struct PartialTessellationUserData data = {
.faces = faces,
.looptris = looptris,
};
struct PartialTessellationUserTLS tls_dummy = {NULL};
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.use_threading = true;
settings.userdata_chunk = &tls_dummy;
settings.userdata_chunk_size = sizeof(tls_dummy);
settings.func_free = bmesh_calc_tessellation_for_face_partial_free_fn;
BLI_task_parallel_range(0,
faces_len,
&data,
params->face_normals ?
bmesh_calc_tessellation_for_face_partial_with_normals_fn :
bmesh_calc_tessellation_for_face_partial_fn,
&settings);
}
static void bm_mesh_calc_tessellation_with_partial__single_threaded(
BMLoop *(*looptris)[3],
const BMPartialUpdate *bmpinfo,
const struct BMeshCalcTessellation_Params *params)
{
const int faces_len = bmpinfo->faces_len;
BMFace **faces = bmpinfo->faces;
MemArena *pf_arena = NULL;
if (params->face_normals) {
for (int index = 0; index < faces_len; index++) {
BMFace *f = faces[index];
BMLoop *l = BM_FACE_FIRST_LOOP(f);
const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2);
bmesh_calc_tessellation_for_face_with_normal(looptris + offset, f, &pf_arena);
}
}
else {
for (int index = 0; index < faces_len; index++) {
BMFace *f = faces[index];
BMLoop *l = BM_FACE_FIRST_LOOP(f);
const int offset = BM_elem_index_get(l) - (BM_elem_index_get(f) * 2);
bmesh_calc_tessellation_for_face(looptris + offset, f, &pf_arena);
}
}
if (pf_arena) {
BLI_memarena_free(pf_arena);
}
}
void BM_mesh_calc_tessellation_with_partial_ex(BMesh *bm,
BMLoop *(*looptris)[3],
const BMPartialUpdate *bmpinfo,
const struct BMeshCalcTessellation_Params *params)
{
BLI_assert(bmpinfo->params.do_tessellate);
BM_mesh_elem_index_ensure(bm, BM_LOOP | BM_FACE);
if (bmpinfo->faces_len < BM_FACE_TESSELLATE_THREADED_LIMIT) {
bm_mesh_calc_tessellation_with_partial__single_threaded(looptris, bmpinfo, params);
}
else {
bm_mesh_calc_tessellation_with_partial__multi_threaded(looptris, bmpinfo, params);
}
}
void BM_mesh_calc_tessellation_with_partial(BMesh *bm,
BMLoop *(*looptris)[3],
const BMPartialUpdate *bmpinfo)
{
BM_mesh_calc_tessellation_with_partial_ex(bm, looptris, bmpinfo, false);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Beauty Mesh Tessellation
*
* Avoid degenerate triangles.
* \{ */
static int bmesh_calc_tessellation_for_face_beauty(BMLoop *(*looptris)[3],
BMFace *efa,
MemArena **pf_arena_p,
Heap **pf_heap_p)
{
switch (efa->len) {
case 3: {
BMLoop *l;
BMLoop **l_ptr = looptris[0];
l_ptr[0] = l = BM_FACE_FIRST_LOOP(efa);
l_ptr[1] = l = l->next;
l_ptr[2] = l->next;
return 1;
}
case 4: {
BMLoop *l_v1 = BM_FACE_FIRST_LOOP(efa);
BMLoop *l_v2 = l_v1->next;
BMLoop *l_v3 = l_v2->next;
BMLoop *l_v4 = l_v1->prev;
/* #BM_verts_calc_rotate_beauty performs excessive checks we don't need!
* It's meant for rotating edges, it also calculates a new normal.
*
* Use #BLI_polyfill_beautify_quad_rotate_calc since we have the normal.
*/
#if 0
const bool split_13 = (BM_verts_calc_rotate_beauty(
l_v1->v, l_v2->v, l_v3->v, l_v4->v, 0, 0) < 0.0f);
#else
float axis_mat[3][3], v_quad[4][2];
axis_dominant_v3_to_m3(axis_mat, efa->no);
mul_v2_m3v3(v_quad[0], axis_mat, l_v1->v->co);
mul_v2_m3v3(v_quad[1], axis_mat, l_v2->v->co);
mul_v2_m3v3(v_quad[2], axis_mat, l_v3->v->co);
mul_v2_m3v3(v_quad[3], axis_mat, l_v4->v->co);
const bool split_13 = BLI_polyfill_beautify_quad_rotate_calc(
v_quad[0], v_quad[1], v_quad[2], v_quad[3]) < 0.0f;
#endif
BMLoop **l_ptr_a = looptris[0];
BMLoop **l_ptr_b = looptris[1];
if (split_13) {
l_ptr_a[0] = l_v1;
l_ptr_a[1] = l_v2;
l_ptr_a[2] = l_v3;
l_ptr_b[0] = l_v1;
l_ptr_b[1] = l_v3;
l_ptr_b[2] = l_v4;
}
else {
l_ptr_a[0] = l_v1;
l_ptr_a[1] = l_v2;
l_ptr_a[2] = l_v4;
l_ptr_b[0] = l_v2;
l_ptr_b[1] = l_v3;
l_ptr_b[2] = l_v4;
}
return 2;
}
default: {
MemArena *pf_arena = *pf_arena_p;
Heap *pf_heap = *pf_heap_p;
if (UNLIKELY(pf_arena == NULL)) {
pf_arena = *pf_arena_p = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
pf_heap = *pf_heap_p = BLI_heap_new_ex(BLI_POLYFILL_ALLOC_NGON_RESERVE);
}
BMLoop *l_iter, *l_first;
BMLoop **l_arr;
float axis_mat[3][3];
float(*projverts)[2];
uint(*tris)[3];
const int tris_len = efa->len - 2;
tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * tris_len);
l_arr = BLI_memarena_alloc(pf_arena, sizeof(*l_arr) * efa->len);
projverts = BLI_memarena_alloc(pf_arena, sizeof(*projverts) * efa->len);
axis_dominant_v3_to_m3_negate(axis_mat, efa->no);
int i = 0;
l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
do {
l_arr[i] = l_iter;
mul_v2_m3v3(projverts[i], axis_mat, l_iter->v->co);
i++;
} while ((l_iter = l_iter->next) != l_first);
BLI_polyfill_calc_arena(projverts, efa->len, 1, tris, pf_arena);
BLI_polyfill_beautify(projverts, efa->len, tris, pf_arena, pf_heap);
for (i = 0; i < tris_len; i++) {
BMLoop **l_ptr = looptris[i];
uint *tri = tris[i];
l_ptr[0] = l_arr[tri[0]];
l_ptr[1] = l_arr[tri[1]];
l_ptr[2] = l_arr[tri[2]];
}
BLI_memarena_clear(pf_arena);
return tris_len;
}
}
}
/**
* A version of #BM_mesh_calc_tessellation that avoids degenerate triangles.
*/
void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3])
{
#ifndef NDEBUG
const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop);
#endif
BMIter iter;
BMFace *efa;
int i = 0;
MemArena *pf_arena = NULL;
/* use_beauty */
Heap *pf_heap = NULL;
BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
BLI_assert(efa->len >= 3);
i += bmesh_calc_tessellation_for_face_beauty(looptris + i, efa, &pf_arena, &pf_heap);
}
if (pf_arena) {
BLI_memarena_free(pf_arena);
BLI_heap_free(pf_heap, NULL);
}
BLI_assert(i <= looptris_tot);
}
/** \} */