Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blenkernel/intern/mball_tessellate.cc')
-rw-r--r--source/blender/blenkernel/intern/mball_tessellate.cc1500
1 files changed, 1500 insertions, 0 deletions
diff --git a/source/blender/blenkernel/intern/mball_tessellate.cc b/source/blender/blenkernel/intern/mball_tessellate.cc
new file mode 100644
index 00000000000..5df924c1bbf
--- /dev/null
+++ b/source/blender/blenkernel/intern/mball_tessellate.cc
@@ -0,0 +1,1500 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2001-2002 NaN Holding BV. All rights reserved. */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include <ctype.h>
+#include <float.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_meta_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_memarena.h"
+#include "BLI_string_utils.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_displist.h"
+#include "BKE_global.h"
+#include "BKE_lib_id.h"
+#include "BKE_mball_tessellate.h" /* own include */
+#include "BKE_mesh.h"
+#include "BKE_object.h"
+#include "BKE_scene.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
+
+#include "BLI_strict_flags.h"
+
+/* experimental (faster) normal calculation */
+// #define USE_ACCUM_NORMAL
+
+#define MBALL_ARRAY_LEN_INIT 4096
+
+/* Data types */
+
+typedef struct corner { /* corner of a cube */
+ int i, j, k; /* (i, j, k) is index within lattice */
+ float co[3], value; /* location and function value */
+ struct corner *next;
+} CORNER;
+
+typedef struct cube { /* partitioning cell (cube) */
+ int i, j, k; /* lattice location of cube */
+ CORNER *corners[8]; /* eight corners */
+} CUBE;
+
+typedef struct cubes { /* linked list of cubes acting as stack */
+ CUBE cube; /* a single cube */
+ struct cubes *next; /* remaining elements */
+} CUBES;
+
+typedef struct centerlist { /* list of cube locations */
+ int i, j, k; /* cube location */
+ struct centerlist *next; /* remaining elements */
+} CENTERLIST;
+
+typedef struct edgelist { /* list of edges */
+ int i1, j1, k1, i2, j2, k2; /* edge corner ids */
+ int vid; /* vertex id */
+ struct edgelist *next; /* remaining elements */
+} EDGELIST;
+
+typedef struct intlist { /* list of integers */
+ int i; /* an integer */
+ struct intlist *next; /* remaining elements */
+} INTLIST;
+
+typedef struct intlists { /* list of list of integers */
+ INTLIST *list; /* a list of integers */
+ struct intlists *next; /* remaining elements */
+} INTLISTS;
+
+typedef struct Box { /* an AABB with pointer to metalelem */
+ float min[3], max[3];
+ const MetaElem *ml;
+} Box;
+
+typedef struct MetaballBVHNode { /* BVH node */
+ Box bb[2]; /* AABB of children */
+ struct MetaballBVHNode *child[2];
+} MetaballBVHNode;
+
+typedef struct process { /* parameters, storage */
+ float thresh, size; /* mball threshold, single cube size */
+ float delta; /* small delta for calculating normals */
+ uint converge_res; /* converge procedure resolution (more = slower) */
+
+ MetaElem **mainb; /* array of all metaelems */
+ uint totelem, mem; /* number of metaelems */
+
+ MetaballBVHNode metaball_bvh; /* The simplest bvh */
+ Box allbb; /* Bounding box of all metaelems */
+
+ MetaballBVHNode **bvh_queue; /* Queue used during bvh traversal */
+ uint bvh_queue_size;
+
+ CUBES *cubes; /* stack of cubes waiting for polygonization */
+ CENTERLIST **centers; /* cube center hash table */
+ CORNER **corners; /* corner value hash table */
+ EDGELIST **edges; /* edge and vertex id hash table */
+
+ int (*indices)[4]; /* output indices */
+ uint totindex; /* size of memory allocated for indices */
+ uint curindex; /* number of currently added indices */
+
+ float (*co)[3], (*no)[3]; /* surface vertices - positions and normals */
+ uint totvertex; /* memory size */
+ uint curvertex; /* currently added vertices */
+
+ /* memory allocation from common pool */
+ MemArena *pgn_elements;
+} PROCESS;
+
+/* Forward declarations */
+static int vertid(PROCESS *process, const CORNER *c1, const CORNER *c2);
+static void add_cube(PROCESS *process, int i, int j, int k);
+static void make_face(PROCESS *process, int i1, int i2, int i3, int i4);
+static void converge(PROCESS *process, const CORNER *c1, const CORNER *c2, float r_p[3]);
+
+/* ******************* SIMPLE BVH ********************* */
+
+static void make_box_union(const BoundBox *a, const Box *b, Box *r_out)
+{
+ r_out->min[0] = min_ff(a->vec[0][0], b->min[0]);
+ r_out->min[1] = min_ff(a->vec[0][1], b->min[1]);
+ r_out->min[2] = min_ff(a->vec[0][2], b->min[2]);
+
+ r_out->max[0] = max_ff(a->vec[6][0], b->max[0]);
+ r_out->max[1] = max_ff(a->vec[6][1], b->max[1]);
+ r_out->max[2] = max_ff(a->vec[6][2], b->max[2]);
+}
+
+static void make_box_from_metaelem(Box *r, const MetaElem *ml)
+{
+ copy_v3_v3(r->max, ml->bb->vec[6]);
+ copy_v3_v3(r->min, ml->bb->vec[0]);
+ r->ml = ml;
+}
+
+/**
+ * Partitions part of #process.mainb array [start, end) along axis s. Returns i,
+ * where centroids of elements in the [start, i) segment lie "on the right side" of div,
+ * and elements in the [i, end) segment lie "on the left"
+ */
+static uint partition_mainb(MetaElem **mainb, uint start, uint end, uint s, float div)
+{
+ uint i = start, j = end - 1;
+ div *= 2.0f;
+
+ while (1) {
+ while (i < j && div > (mainb[i]->bb->vec[6][s] + mainb[i]->bb->vec[0][s])) {
+ i++;
+ }
+ while (j > i && div < (mainb[j]->bb->vec[6][s] + mainb[j]->bb->vec[0][s])) {
+ j--;
+ }
+
+ if (i >= j) {
+ break;
+ }
+
+ SWAP(MetaElem *, mainb[i], mainb[j]);
+ i++;
+ j--;
+ }
+
+ if (i == start) {
+ i++;
+ }
+
+ return i;
+}
+
+/**
+ * Recursively builds a BVH, dividing elements along the middle of the longest axis of allbox.
+ */
+static void build_bvh_spatial(
+ PROCESS *process, MetaballBVHNode *node, uint start, uint end, const Box *allbox)
+{
+ uint part, j, s;
+ float dim[3], div;
+
+ /* Maximum bvh queue size is number of nodes which are made, equals calls to this function. */
+ process->bvh_queue_size++;
+
+ dim[0] = allbox->max[0] - allbox->min[0];
+ dim[1] = allbox->max[1] - allbox->min[1];
+ dim[2] = allbox->max[2] - allbox->min[2];
+
+ s = 0;
+ if (dim[1] > dim[0] && dim[1] > dim[2]) {
+ s = 1;
+ }
+ else if (dim[2] > dim[1] && dim[2] > dim[0]) {
+ s = 2;
+ }
+
+ div = allbox->min[s] + (dim[s] / 2.0f);
+
+ part = partition_mainb(process->mainb, start, end, s, div);
+
+ make_box_from_metaelem(&node->bb[0], process->mainb[start]);
+ node->child[0] = NULL;
+
+ if (part > start + 1) {
+ for (j = start; j < part; j++) {
+ make_box_union(process->mainb[j]->bb, &node->bb[0], &node->bb[0]);
+ }
+
+ node->child[0] = static_cast<MetaballBVHNode *>(
+ BLI_memarena_alloc(process->pgn_elements, sizeof(MetaballBVHNode)));
+ build_bvh_spatial(process, node->child[0], start, part, &node->bb[0]);
+ }
+
+ node->child[1] = NULL;
+ if (part < end) {
+ make_box_from_metaelem(&node->bb[1], process->mainb[part]);
+
+ if (part < end - 1) {
+ for (j = part; j < end; j++) {
+ make_box_union(process->mainb[j]->bb, &node->bb[1], &node->bb[1]);
+ }
+
+ node->child[1] = static_cast<MetaballBVHNode *>(
+ BLI_memarena_alloc(process->pgn_elements, sizeof(MetaballBVHNode)));
+ build_bvh_spatial(process, node->child[1], part, end, &node->bb[1]);
+ }
+ }
+ else {
+ INIT_MINMAX(node->bb[1].min, node->bb[1].max);
+ }
+}
+
+/* ******************** ARITH ************************* */
+
+/**
+ * BASED AT CODE (but mostly rewritten) :
+ * C code from the article
+ * "An Implicit Surface Polygonizer"
+ * by Jules Bloomenthal <jbloom@beauty.gmu.edu>
+ * in "Graphics Gems IV", Academic Press, 1994
+ *
+ * Authored by Jules Bloomenthal, Xerox PARC.
+ * Copyright (c) Xerox Corporation, 1991. All rights reserved.
+ * Permission is granted to reproduce, use and distribute this code for
+ * any and all purposes, provided that this notice appears in all copies.
+ */
+
+#define L 0 /* Left direction: -x, -i. */
+#define R 1 /* Right direction: +x, +i. */
+#define B 2 /* Bottom direction: -y, -j. */
+#define T 3 /* Top direction: +y, +j. */
+#define N 4 /* Near direction: -z, -k. */
+#define F 5 /* Far direction: +z, +k. */
+#define LBN 0 /* Left bottom near corner. */
+#define LBF 1 /* Left bottom far corner. */
+#define LTN 2 /* Left top near corner. */
+#define LTF 3 /* Left top far corner. */
+#define RBN 4 /* Right bottom near corner. */
+#define RBF 5 /* Right bottom far corner. */
+#define RTN 6 /* Right top near corner. */
+#define RTF 7 /* Right top far corner. */
+
+/**
+ * the LBN corner of cube (i, j, k), corresponds with location
+ * (i-0.5)*size, (j-0.5)*size, (k-0.5)*size)
+ */
+
+#define HASHBIT (5)
+/** Hash table size (32768). */
+#define HASHSIZE (size_t)(1 << (3 * HASHBIT))
+
+#define HASH(i, j, k) ((((((i)&31) << 5) | ((j)&31)) << 5) | ((k)&31))
+
+#define MB_BIT(i, bit) (((i) >> (bit)) & 1)
+// #define FLIP(i, bit) ((i) ^ 1 << (bit)) /* flip the given bit of i */
+
+/* ******************** DENSITY COPMPUTATION ********************* */
+
+/**
+ * Computes density from given metaball at given position.
+ * Metaball equation is: `(1 - r^2 / R^2)^3 * s`
+ *
+ * r = distance from center
+ * R = metaball radius
+ * s - metaball stiffness
+ */
+static float densfunc(const MetaElem *ball, float x, float y, float z)
+{
+ float dist2;
+ float dvec[3] = {x, y, z};
+
+ mul_m4_v3((const float(*)[4])ball->imat, dvec);
+
+ switch (ball->type) {
+ case MB_BALL:
+ /* do nothing */
+ break;
+ case MB_CUBE:
+ if (dvec[2] > ball->expz) {
+ dvec[2] -= ball->expz;
+ }
+ else if (dvec[2] < -ball->expz) {
+ dvec[2] += ball->expz;
+ }
+ else {
+ dvec[2] = 0.0;
+ }
+ ATTR_FALLTHROUGH;
+ case MB_PLANE:
+ if (dvec[1] > ball->expy) {
+ dvec[1] -= ball->expy;
+ }
+ else if (dvec[1] < -ball->expy) {
+ dvec[1] += ball->expy;
+ }
+ else {
+ dvec[1] = 0.0;
+ }
+ ATTR_FALLTHROUGH;
+ case MB_TUBE:
+ if (dvec[0] > ball->expx) {
+ dvec[0] -= ball->expx;
+ }
+ else if (dvec[0] < -ball->expx) {
+ dvec[0] += ball->expx;
+ }
+ else {
+ dvec[0] = 0.0;
+ }
+ break;
+ case MB_ELIPSOID:
+ dvec[0] /= ball->expx;
+ dvec[1] /= ball->expy;
+ dvec[2] /= ball->expz;
+ break;
+
+ /* *** deprecated, could be removed?, do-versioned at least *** */
+ case MB_TUBEX:
+ if (dvec[0] > ball->len) {
+ dvec[0] -= ball->len;
+ }
+ else if (dvec[0] < -ball->len) {
+ dvec[0] += ball->len;
+ }
+ else {
+ dvec[0] = 0.0;
+ }
+ break;
+ case MB_TUBEY:
+ if (dvec[1] > ball->len) {
+ dvec[1] -= ball->len;
+ }
+ else if (dvec[1] < -ball->len) {
+ dvec[1] += ball->len;
+ }
+ else {
+ dvec[1] = 0.0;
+ }
+ break;
+ case MB_TUBEZ:
+ if (dvec[2] > ball->len) {
+ dvec[2] -= ball->len;
+ }
+ else if (dvec[2] < -ball->len) {
+ dvec[2] += ball->len;
+ }
+ else {
+ dvec[2] = 0.0;
+ }
+ break;
+ /* *** end deprecated *** */
+ }
+
+ /* ball->rad2 is inverse of squared rad */
+ dist2 = 1.0f - (len_squared_v3(dvec) * ball->rad2);
+
+ /* ball->s is negative if metaball is negative */
+ return (dist2 < 0.0f) ? 0.0f : (ball->s * dist2 * dist2 * dist2);
+}
+
+/**
+ * Computes density at given position form all meta-balls which contain this point in their box.
+ * Traverses BVH using a queue.
+ */
+static float metaball(PROCESS *process, float x, float y, float z)
+{
+ float dens = 0.0f;
+ uint front = 0, back = 0;
+ MetaballBVHNode *node;
+
+ process->bvh_queue[front++] = &process->metaball_bvh;
+
+ while (front != back) {
+ node = process->bvh_queue[back++];
+
+ for (int i = 0; i < 2; i++) {
+ if ((node->bb[i].min[0] <= x) && (node->bb[i].max[0] >= x) && (node->bb[i].min[1] <= y) &&
+ (node->bb[i].max[1] >= y) && (node->bb[i].min[2] <= z) && (node->bb[i].max[2] >= z)) {
+ if (node->child[i]) {
+ process->bvh_queue[front++] = node->child[i];
+ }
+ else {
+ dens += densfunc(node->bb[i].ml, x, y, z);
+ }
+ }
+ }
+ }
+
+ return process->thresh - dens;
+}
+
+/**
+ * Adds face to indices, expands memory if needed.
+ */
+static void make_face(PROCESS *process, int i1, int i2, int i3, int i4)
+{
+#ifdef USE_ACCUM_NORMAL
+ float n[3];
+#endif
+
+ if (UNLIKELY(process->totindex == process->curindex)) {
+ process->totindex = process->totindex ? (process->totindex * 2) : MBALL_ARRAY_LEN_INIT;
+ process->indices = static_cast<int(*)[4]>(
+ MEM_reallocN(process->indices, sizeof(int[4]) * process->totindex));
+ }
+
+ int *cur = process->indices[process->curindex++];
+
+ /* Treat triangles as fake quads. */
+ cur[0] = i1;
+ cur[1] = i2;
+ cur[2] = i3;
+ cur[3] = i4;
+
+#ifdef USE_ACCUM_NORMAL
+ if (i4 == i3) {
+ normal_tri_v3(n, process->co[i1], process->co[i2], process->co[i3]);
+ accumulate_vertex_normals_v3(process->no[i1],
+ process->no[i2],
+ process->no[i3],
+ NULL,
+ n,
+ process->co[i1],
+ process->co[i2],
+ process->co[i3],
+ NULL);
+ }
+ else {
+ normal_quad_v3(n, process->co[i1], process->co[i2], process->co[i3], process->co[i4]);
+ accumulate_vertex_normals_v3(process->no[i1],
+ process->no[i2],
+ process->no[i3],
+ process->no[i4],
+ n,
+ process->co[i1],
+ process->co[i2],
+ process->co[i3],
+ process->co[i4]);
+ }
+#endif
+}
+
+/* Frees allocated memory */
+static void freepolygonize(PROCESS *process)
+{
+ if (process->corners) {
+ MEM_freeN(process->corners);
+ }
+ if (process->edges) {
+ MEM_freeN(process->edges);
+ }
+ if (process->centers) {
+ MEM_freeN(process->centers);
+ }
+ if (process->mainb) {
+ MEM_freeN(process->mainb);
+ }
+ if (process->bvh_queue) {
+ MEM_freeN(process->bvh_queue);
+ }
+ if (process->pgn_elements) {
+ BLI_memarena_free(process->pgn_elements);
+ }
+}
+
+/* **************** POLYGONIZATION ************************ */
+
+/**** Cubical Polygonization (optional) ****/
+
+#define LB 0 /* left bottom edge */
+#define LT 1 /* left top edge */
+#define LN 2 /* left near edge */
+#define LF 3 /* left far edge */
+#define RB 4 /* right bottom edge */
+#define RT 5 /* right top edge */
+#define RN 6 /* right near edge */
+#define RF 7 /* right far edge */
+#define BN 8 /* bottom near edge */
+#define BF 9 /* bottom far edge */
+#define TN 10 /* top near edge */
+#define TF 11 /* top far edge */
+
+static INTLISTS *cubetable[256];
+static char faces[256];
+
+/* edge: LB, LT, LN, LF, RB, RT, RN, RF, BN, BF, TN, TF */
+static int corner1[12] = {
+ LBN,
+ LTN,
+ LBN,
+ LBF,
+ RBN,
+ RTN,
+ RBN,
+ RBF,
+ LBN,
+ LBF,
+ LTN,
+ LTF,
+};
+static int corner2[12] = {
+ LBF,
+ LTF,
+ LTN,
+ LTF,
+ RBF,
+ RTF,
+ RTN,
+ RTF,
+ RBN,
+ RBF,
+ RTN,
+ RTF,
+};
+static int leftface[12] = {
+ B,
+ L,
+ L,
+ F,
+ R,
+ T,
+ N,
+ R,
+ N,
+ B,
+ T,
+ F,
+};
+/* face on left when going corner1 to corner2 */
+static int rightface[12] = {
+ L,
+ T,
+ N,
+ L,
+ B,
+ R,
+ R,
+ F,
+ B,
+ F,
+ N,
+ T,
+};
+/* face on right when going corner1 to corner2 */
+
+/**
+ * triangulate the cube directly, without decomposition
+ */
+static void docube(PROCESS *process, CUBE *cube)
+{
+ INTLISTS *polys;
+ CORNER *c1, *c2;
+ int i, index = 0, count, indexar[8];
+
+ /* Determine which case cube falls into. */
+ for (i = 0; i < 8; i++) {
+ if (cube->corners[i]->value > 0.0f) {
+ index += (1 << i);
+ }
+ }
+
+ /* Using faces[] table, adds neighboring cube if surface intersects face in this direction. */
+ if (MB_BIT(faces[index], 0)) {
+ add_cube(process, cube->i - 1, cube->j, cube->k);
+ }
+ if (MB_BIT(faces[index], 1)) {
+ add_cube(process, cube->i + 1, cube->j, cube->k);
+ }
+ if (MB_BIT(faces[index], 2)) {
+ add_cube(process, cube->i, cube->j - 1, cube->k);
+ }
+ if (MB_BIT(faces[index], 3)) {
+ add_cube(process, cube->i, cube->j + 1, cube->k);
+ }
+ if (MB_BIT(faces[index], 4)) {
+ add_cube(process, cube->i, cube->j, cube->k - 1);
+ }
+ if (MB_BIT(faces[index], 5)) {
+ add_cube(process, cube->i, cube->j, cube->k + 1);
+ }
+
+ /* Using cubetable[], determines polygons for output. */
+ for (polys = cubetable[index]; polys; polys = polys->next) {
+ INTLIST *edges;
+
+ count = 0;
+ /* Sets needed vertex id's lying on the edges. */
+ for (edges = polys->list; edges; edges = edges->next) {
+ c1 = cube->corners[corner1[edges->i]];
+ c2 = cube->corners[corner2[edges->i]];
+
+ indexar[count] = vertid(process, c1, c2);
+ count++;
+ }
+
+ /* Adds faces to output. */
+ if (count > 2) {
+ switch (count) {
+ case 3:
+ make_face(process, indexar[2], indexar[1], indexar[0], indexar[0]); /* triangle */
+ break;
+ case 4:
+ make_face(process, indexar[3], indexar[2], indexar[1], indexar[0]);
+ break;
+ case 5:
+ make_face(process, indexar[3], indexar[2], indexar[1], indexar[0]);
+ make_face(process, indexar[4], indexar[3], indexar[0], indexar[0]); /* triangle */
+ break;
+ case 6:
+ make_face(process, indexar[3], indexar[2], indexar[1], indexar[0]);
+ make_face(process, indexar[5], indexar[4], indexar[3], indexar[0]);
+ break;
+ case 7:
+ make_face(process, indexar[3], indexar[2], indexar[1], indexar[0]);
+ make_face(process, indexar[5], indexar[4], indexar[3], indexar[0]);
+ make_face(process, indexar[6], indexar[5], indexar[0], indexar[0]); /* triangle */
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * return corner with the given lattice location
+ * set (and cache) its function value
+ */
+static CORNER *setcorner(PROCESS *process, int i, int j, int k)
+{
+ /* for speed, do corner value caching here */
+ CORNER *c;
+ int index;
+
+ /* does corner exist? */
+ index = HASH(i, j, k);
+ c = process->corners[index];
+
+ for (; c != NULL; c = c->next) {
+ if (c->i == i && c->j == j && c->k == k) {
+ return c;
+ }
+ }
+
+ c = static_cast<CORNER *>(BLI_memarena_alloc(process->pgn_elements, sizeof(CORNER)));
+
+ c->i = i;
+ c->co[0] = ((float)i - 0.5f) * process->size;
+ c->j = j;
+ c->co[1] = ((float)j - 0.5f) * process->size;
+ c->k = k;
+ c->co[2] = ((float)k - 0.5f) * process->size;
+
+ c->value = metaball(process, c->co[0], c->co[1], c->co[2]);
+
+ c->next = process->corners[index];
+ process->corners[index] = c;
+
+ return c;
+}
+
+/**
+ * return next clockwise edge from given edge around given face
+ */
+static int nextcwedge(int edge, int face)
+{
+ switch (edge) {
+ case LB:
+ return (face == L) ? LF : BN;
+ case LT:
+ return (face == L) ? LN : TF;
+ case LN:
+ return (face == L) ? LB : TN;
+ case LF:
+ return (face == L) ? LT : BF;
+ case RB:
+ return (face == R) ? RN : BF;
+ case RT:
+ return (face == R) ? RF : TN;
+ case RN:
+ return (face == R) ? RT : BN;
+ case RF:
+ return (face == R) ? RB : TF;
+ case BN:
+ return (face == B) ? RB : LN;
+ case BF:
+ return (face == B) ? LB : RF;
+ case TN:
+ return (face == T) ? LT : RN;
+ case TF:
+ return (face == T) ? RT : LF;
+ }
+ return 0;
+}
+
+/**
+ * \return the face adjoining edge that is not the given face
+ */
+static int otherface(int edge, int face)
+{
+ int other = leftface[edge];
+ return face == other ? rightface[edge] : other;
+}
+
+/**
+ * create the 256 entry table for cubical polygonization
+ */
+static void makecubetable(void)
+{
+ static bool is_done = false;
+ int i, e, c, done[12], pos[8];
+
+ if (is_done) {
+ return;
+ }
+ is_done = true;
+
+ for (i = 0; i < 256; i++) {
+ for (e = 0; e < 12; e++) {
+ done[e] = 0;
+ }
+ for (c = 0; c < 8; c++) {
+ pos[c] = MB_BIT(i, c);
+ }
+ for (e = 0; e < 12; e++) {
+ if (!done[e] && (pos[corner1[e]] != pos[corner2[e]])) {
+ INTLIST *ints = NULL;
+ INTLISTS *lists = static_cast<INTLISTS *>(MEM_callocN(sizeof(INTLISTS), "mball_intlist"));
+ int start = e, edge = e;
+
+ /* get face that is to right of edge from pos to neg corner: */
+ int face = pos[corner1[e]] ? rightface[e] : leftface[e];
+
+ while (1) {
+ edge = nextcwedge(edge, face);
+ done[edge] = 1;
+ if (pos[corner1[edge]] != pos[corner2[edge]]) {
+ INTLIST *tmp = ints;
+
+ ints = static_cast<INTLIST *>(MEM_callocN(sizeof(INTLIST), "mball_intlist"));
+ ints->i = edge;
+ ints->next = tmp; /* add edge to head of list */
+
+ if (edge == start) {
+ break;
+ }
+ face = otherface(edge, face);
+ }
+ }
+ lists->list = ints; /* add ints to head of table entry */
+ lists->next = cubetable[i];
+ cubetable[i] = lists;
+ }
+ }
+ }
+
+ for (i = 0; i < 256; i++) {
+ INTLISTS *polys;
+ faces[i] = 0;
+ for (polys = cubetable[i]; polys; polys = polys->next) {
+ INTLIST *edges;
+
+ for (edges = polys->list; edges; edges = edges->next) {
+ if (ELEM(edges->i, LB, LT, LN, LF)) {
+ faces[i] |= 1 << L;
+ }
+ if (ELEM(edges->i, RB, RT, RN, RF)) {
+ faces[i] |= 1 << R;
+ }
+ if (ELEM(edges->i, LB, RB, BN, BF)) {
+ faces[i] |= 1 << B;
+ }
+ if (ELEM(edges->i, LT, RT, TN, TF)) {
+ faces[i] |= 1 << T;
+ }
+ if (ELEM(edges->i, LN, RN, BN, TN)) {
+ faces[i] |= 1 << N;
+ }
+ if (ELEM(edges->i, LF, RF, BF, TF)) {
+ faces[i] |= 1 << F;
+ }
+ }
+ }
+ }
+}
+
+void BKE_mball_cubeTable_free(void)
+{
+ for (int i = 0; i < 256; i++) {
+ INTLISTS *lists = cubetable[i];
+ while (lists) {
+ INTLISTS *nlists = lists->next;
+
+ INTLIST *ints = lists->list;
+ while (ints) {
+ INTLIST *nints = ints->next;
+ MEM_freeN(ints);
+ ints = nints;
+ }
+
+ MEM_freeN(lists);
+ lists = nlists;
+ }
+ cubetable[i] = NULL;
+ }
+}
+
+/**** Storage ****/
+
+/**
+ * Inserts cube at lattice i, j, k into hash table, marking it as "done"
+ */
+static int setcenter(PROCESS *process, CENTERLIST *table[], const int i, const int j, const int k)
+{
+ int index;
+ CENTERLIST *newc, *l, *q;
+
+ index = HASH(i, j, k);
+ q = table[index];
+
+ for (l = q; l != NULL; l = l->next) {
+ if (l->i == i && l->j == j && l->k == k) {
+ return 1;
+ }
+ }
+
+ newc = static_cast<CENTERLIST *>(BLI_memarena_alloc(process->pgn_elements, sizeof(CENTERLIST)));
+ newc->i = i;
+ newc->j = j;
+ newc->k = k;
+ newc->next = q;
+ table[index] = newc;
+
+ return 0;
+}
+
+/**
+ * Sets vid of vertex lying on given edge.
+ */
+static void setedge(PROCESS *process, int i1, int j1, int k1, int i2, int j2, int k2, int vid)
+{
+ int index;
+ EDGELIST *newe;
+
+ if (i1 > i2 || (i1 == i2 && (j1 > j2 || (j1 == j2 && k1 > k2)))) {
+ int t = i1;
+ i1 = i2;
+ i2 = t;
+ t = j1;
+ j1 = j2;
+ j2 = t;
+ t = k1;
+ k1 = k2;
+ k2 = t;
+ }
+ index = HASH(i1, j1, k1) + HASH(i2, j2, k2);
+ newe = static_cast<EDGELIST *>(BLI_memarena_alloc(process->pgn_elements, sizeof(EDGELIST)));
+
+ newe->i1 = i1;
+ newe->j1 = j1;
+ newe->k1 = k1;
+ newe->i2 = i2;
+ newe->j2 = j2;
+ newe->k2 = k2;
+ newe->vid = vid;
+ newe->next = process->edges[index];
+ process->edges[index] = newe;
+}
+
+/**
+ * \return vertex id for edge; return -1 if not set
+ */
+static int getedge(EDGELIST *table[], int i1, int j1, int k1, int i2, int j2, int k2)
+{
+ EDGELIST *q;
+
+ if (i1 > i2 || (i1 == i2 && (j1 > j2 || (j1 == j2 && k1 > k2)))) {
+ int t = i1;
+ i1 = i2;
+ i2 = t;
+ t = j1;
+ j1 = j2;
+ j2 = t;
+ t = k1;
+ k1 = k2;
+ k2 = t;
+ }
+ q = table[HASH(i1, j1, k1) + HASH(i2, j2, k2)];
+ for (; q != NULL; q = q->next) {
+ if (q->i1 == i1 && q->j1 == j1 && q->k1 == k1 && q->i2 == i2 && q->j2 == j2 && q->k2 == k2) {
+ return q->vid;
+ }
+ }
+ return -1;
+}
+
+/**
+ * Adds a vertex, expands memory if needed.
+ */
+static void addtovertices(PROCESS *process, const float v[3], const float no[3])
+{
+ if (UNLIKELY(process->curvertex == process->totvertex)) {
+ process->totvertex = process->totvertex ? process->totvertex * 2 : MBALL_ARRAY_LEN_INIT;
+ process->co = static_cast<float(*)[3]>(
+ MEM_reallocN(process->co, process->totvertex * sizeof(float[3])));
+ process->no = static_cast<float(*)[3]>(
+ MEM_reallocN(process->no, process->totvertex * sizeof(float[3])));
+ }
+
+ copy_v3_v3(process->co[process->curvertex], v);
+ copy_v3_v3(process->no[process->curvertex], no);
+
+ process->curvertex++;
+}
+
+#ifndef USE_ACCUM_NORMAL
+/**
+ * Computes normal from density field at given point.
+ *
+ * \note Doesn't do normalization!
+ */
+static void vnormal(PROCESS *process, const float point[3], float r_no[3])
+{
+ const float delta = process->delta;
+ const float f = metaball(process, point[0], point[1], point[2]);
+
+ r_no[0] = metaball(process, point[0] + delta, point[1], point[2]) - f;
+ r_no[1] = metaball(process, point[0], point[1] + delta, point[2]) - f;
+ r_no[2] = metaball(process, point[0], point[1], point[2] + delta) - f;
+}
+#endif /* USE_ACCUM_NORMAL */
+
+/**
+ * \return the id of vertex between two corners.
+ *
+ * If it wasn't previously computed, does #converge() and adds vertex to process.
+ */
+static int vertid(PROCESS *process, const CORNER *c1, const CORNER *c2)
+{
+ float v[3], no[3];
+ int vid = getedge(process->edges, c1->i, c1->j, c1->k, c2->i, c2->j, c2->k);
+
+ if (vid != -1) {
+ return vid; /* previously computed */
+ }
+
+ converge(process, c1, c2, v); /* position */
+
+#ifdef USE_ACCUM_NORMAL
+ zero_v3(no);
+#else
+ vnormal(process, v, no);
+#endif
+
+ addtovertices(process, v, no); /* save vertex */
+ vid = (int)process->curvertex - 1;
+ setedge(process, c1->i, c1->j, c1->k, c2->i, c2->j, c2->k, vid);
+
+ return vid;
+}
+
+/**
+ * Given two corners, computes approximation of surface intersection point between them.
+ * In case of small threshold, do bisection.
+ */
+static void converge(PROCESS *process, const CORNER *c1, const CORNER *c2, float r_p[3])
+{
+ float c1_value, c1_co[3];
+ float c2_value, c2_co[3];
+
+ if (c1->value < c2->value) {
+ c1_value = c2->value;
+ copy_v3_v3(c1_co, c2->co);
+ c2_value = c1->value;
+ copy_v3_v3(c2_co, c1->co);
+ }
+ else {
+ c1_value = c1->value;
+ copy_v3_v3(c1_co, c1->co);
+ c2_value = c2->value;
+ copy_v3_v3(c2_co, c2->co);
+ }
+
+ for (uint i = 0; i < process->converge_res; i++) {
+ interp_v3_v3v3(r_p, c1_co, c2_co, 0.5f);
+ float dens = metaball(process, r_p[0], r_p[1], r_p[2]);
+
+ if (dens > 0.0f) {
+ c1_value = dens;
+ copy_v3_v3(c1_co, r_p);
+ }
+ else {
+ c2_value = dens;
+ copy_v3_v3(c2_co, r_p);
+ }
+ }
+
+ float tmp = -c1_value / (c2_value - c1_value);
+ interp_v3_v3v3(r_p, c1_co, c2_co, tmp);
+}
+
+/**
+ * Adds cube at given lattice position to cube stack of process.
+ */
+static void add_cube(PROCESS *process, int i, int j, int k)
+{
+ CUBES *ncube;
+ int n;
+
+ /* test if cube has been found before */
+ if (setcenter(process, process->centers, i, j, k) == 0) {
+ /* push cube on stack: */
+ ncube = static_cast<CUBES *>(BLI_memarena_alloc(process->pgn_elements, sizeof(CUBES)));
+ ncube->next = process->cubes;
+ process->cubes = ncube;
+
+ ncube->cube.i = i;
+ ncube->cube.j = j;
+ ncube->cube.k = k;
+
+ /* set corners of initial cube: */
+ for (n = 0; n < 8; n++) {
+ ncube->cube.corners[n] = setcorner(
+ process, i + MB_BIT(n, 2), j + MB_BIT(n, 1), k + MB_BIT(n, 0));
+ }
+ }
+}
+
+static void next_lattice(int r[3], const float pos[3], const float size)
+{
+ r[0] = (int)ceil((pos[0] / size) + 0.5f);
+ r[1] = (int)ceil((pos[1] / size) + 0.5f);
+ r[2] = (int)ceil((pos[2] / size) + 0.5f);
+}
+static void prev_lattice(int r[3], const float pos[3], const float size)
+{
+ next_lattice(r, pos, size);
+ r[0]--;
+ r[1]--;
+ r[2]--;
+}
+static void closest_latice(int r[3], const float pos[3], const float size)
+{
+ r[0] = (int)floorf(pos[0] / size + 1.0f);
+ r[1] = (int)floorf(pos[1] / size + 1.0f);
+ r[2] = (int)floorf(pos[2] / size + 1.0f);
+}
+
+/**
+ * Find at most 26 cubes to start polygonization from.
+ */
+static void find_first_points(PROCESS *process, const uint em)
+{
+ const MetaElem *ml;
+ int center[3], lbn[3], rtf[3], it[3], dir[3], add[3];
+ float tmp[3], a, b;
+
+ ml = process->mainb[em];
+
+ mid_v3_v3v3(tmp, ml->bb->vec[0], ml->bb->vec[6]);
+ closest_latice(center, tmp, process->size);
+ prev_lattice(lbn, ml->bb->vec[0], process->size);
+ next_lattice(rtf, ml->bb->vec[6], process->size);
+
+ for (dir[0] = -1; dir[0] <= 1; dir[0]++) {
+ for (dir[1] = -1; dir[1] <= 1; dir[1]++) {
+ for (dir[2] = -1; dir[2] <= 1; dir[2]++) {
+ if (dir[0] == 0 && dir[1] == 0 && dir[2] == 0) {
+ continue;
+ }
+
+ copy_v3_v3_int(it, center);
+
+ b = setcorner(process, it[0], it[1], it[2])->value;
+ do {
+ it[0] += dir[0];
+ it[1] += dir[1];
+ it[2] += dir[2];
+ a = b;
+ b = setcorner(process, it[0], it[1], it[2])->value;
+
+ if (a * b < 0.0f) {
+ add[0] = it[0] - dir[0];
+ add[1] = it[1] - dir[1];
+ add[2] = it[2] - dir[2];
+ DO_MIN(it, add);
+ add_cube(process, add[0], add[1], add[2]);
+ break;
+ }
+ } while ((it[0] > lbn[0]) && (it[1] > lbn[1]) && (it[2] > lbn[2]) && (it[0] < rtf[0]) &&
+ (it[1] < rtf[1]) && (it[2] < rtf[2]));
+ }
+ }
+ }
+}
+
+/**
+ * The main polygonization proc.
+ * Allocates memory, makes cubetable,
+ * finds starting surface points
+ * and processes cubes on the stack until none left.
+ */
+static void polygonize(PROCESS *process)
+{
+ CUBE c;
+
+ process->centers = static_cast<CENTERLIST **>(
+ MEM_callocN(HASHSIZE * sizeof(CENTERLIST *), "mbproc->centers"));
+ process->corners = static_cast<CORNER **>(
+ MEM_callocN(HASHSIZE * sizeof(CORNER *), "mbproc->corners"));
+ process->edges = static_cast<EDGELIST **>(
+ MEM_callocN(2 * HASHSIZE * sizeof(EDGELIST *), "mbproc->edges"));
+ process->bvh_queue = static_cast<MetaballBVHNode **>(
+ MEM_callocN(sizeof(MetaballBVHNode *) * process->bvh_queue_size, "Metaball BVH Queue"));
+
+ makecubetable();
+
+ for (uint i = 0; i < process->totelem; i++) {
+ find_first_points(process, i);
+ }
+
+ while (process->cubes != NULL) {
+ c = process->cubes->cube;
+ process->cubes = process->cubes->next;
+
+ docube(process, &c);
+ }
+}
+
+/**
+ * Iterates over ALL objects in the scene and all of its sets, including
+ * making all duplis (not only meta-elements). Copies meta-elements to #process.mainb array.
+ * Computes bounding boxes for building BVH.
+ */
+static void init_meta(Depsgraph *depsgraph, PROCESS *process, Scene *scene, Object *ob)
+{
+ Scene *sce_iter = scene;
+ Base *base;
+ Object *bob;
+ MetaBall *mb;
+ const MetaElem *ml;
+ float obinv[4][4], obmat[4][4];
+ uint i;
+ int obnr, zero_size = 0;
+ char obname[MAX_ID_NAME];
+ SceneBaseIter iter;
+ const eEvaluationMode deg_eval_mode = DEG_get_mode(depsgraph);
+ const short parenting_dupli_transflag = (OB_DUPLIFACES | OB_DUPLIVERTS);
+
+ copy_m4_m4(obmat, ob->obmat); /* to cope with duplicators from BKE_scene_base_iter_next */
+ invert_m4_m4(obinv, ob->obmat);
+
+ BLI_split_name_num(obname, &obnr, ob->id.name + 2, '.');
+
+ /* make main array */
+ BKE_scene_base_iter_next(depsgraph, &iter, &sce_iter, 0, NULL, NULL);
+ while (BKE_scene_base_iter_next(depsgraph, &iter, &sce_iter, 1, &base, &bob)) {
+ if (bob->type == OB_MBALL) {
+ zero_size = 0;
+ ml = NULL;
+
+ /* If this metaball is the original that's used for duplication, only have it visible when
+ * the instancer is visible too. */
+ if ((base->flag_legacy & OB_FROMDUPLI) == 0 && ob->parent != NULL &&
+ (ob->parent->transflag & parenting_dupli_transflag) != 0 &&
+ (BKE_object_visibility(ob->parent, deg_eval_mode) & OB_VISIBLE_SELF) == 0) {
+ continue;
+ }
+
+ if (bob == ob && (base->flag_legacy & OB_FROMDUPLI) == 0) {
+ mb = static_cast<MetaBall *>(ob->data);
+
+ if (mb->editelems) {
+ ml = static_cast<const MetaElem *>(mb->editelems->first);
+ }
+ else {
+ ml = static_cast<const MetaElem *>(mb->elems.first);
+ }
+ }
+ else {
+ char name[MAX_ID_NAME];
+ int nr;
+
+ BLI_split_name_num(name, &nr, bob->id.name + 2, '.');
+ if (STREQ(obname, name)) {
+ mb = static_cast<MetaBall *>(bob->data);
+
+ if (mb->editelems) {
+ ml = static_cast<const MetaElem *>(mb->editelems->first);
+ }
+ else {
+ ml = static_cast<const MetaElem *>(mb->elems.first);
+ }
+ }
+ }
+
+ /* when metaball object has zero scale, then MetaElem to this MetaBall
+ * will not be put to mainb array */
+ if (has_zero_axis_m4(bob->obmat)) {
+ zero_size = 1;
+ }
+ else if (bob->parent) {
+ struct Object *pob = bob->parent;
+ while (pob) {
+ if (has_zero_axis_m4(pob->obmat)) {
+ zero_size = 1;
+ break;
+ }
+ pob = pob->parent;
+ }
+ }
+
+ if (zero_size) {
+ while (ml) {
+ ml = ml->next;
+ }
+ }
+ else {
+ while (ml) {
+ if (!(ml->flag & MB_HIDE)) {
+ float pos[4][4], rot[4][4];
+ float expx, expy, expz;
+ float tempmin[3], tempmax[3];
+
+ MetaElem *new_ml;
+
+ /* make a copy because of duplicates */
+ new_ml = static_cast<MetaElem *>(
+ BLI_memarena_alloc(process->pgn_elements, sizeof(MetaElem)));
+ *(new_ml) = *ml;
+ new_ml->bb = static_cast<BoundBox *>(
+ BLI_memarena_alloc(process->pgn_elements, sizeof(BoundBox)));
+ new_ml->mat = static_cast<float *>(
+ BLI_memarena_alloc(process->pgn_elements, sizeof(float[4][4])));
+ new_ml->imat = static_cast<float *>(
+ BLI_memarena_alloc(process->pgn_elements, sizeof(float[4][4])));
+
+ /* too big stiffness seems only ugly due to linear interpolation
+ * no need to have possibility for too big stiffness */
+ if (ml->s > 10.0f) {
+ new_ml->s = 10.0f;
+ }
+ else {
+ new_ml->s = ml->s;
+ }
+
+ /* if metaball is negative, set stiffness negative */
+ if (new_ml->flag & MB_NEGATIVE) {
+ new_ml->s = -new_ml->s;
+ }
+
+ /* Translation of MetaElem */
+ unit_m4(pos);
+ pos[3][0] = ml->x;
+ pos[3][1] = ml->y;
+ pos[3][2] = ml->z;
+
+ /* Rotation of MetaElem is stored in quat */
+ quat_to_mat4(rot, ml->quat);
+
+ /* Matrix multiply is as follows:
+ * basis object space ->
+ * world ->
+ * ml object space ->
+ * position ->
+ * rotation ->
+ * ml local space
+ */
+ mul_m4_series((float(*)[4])new_ml->mat, obinv, bob->obmat, pos, rot);
+ /* ml local space -> basis object space */
+ invert_m4_m4((float(*)[4])new_ml->imat, (float(*)[4])new_ml->mat);
+
+ /* rad2 is inverse of squared radius */
+ new_ml->rad2 = 1 / (ml->rad * ml->rad);
+
+ /* initial dimensions = radius */
+ expx = ml->rad;
+ expy = ml->rad;
+ expz = ml->rad;
+
+ switch (ml->type) {
+ case MB_BALL:
+ break;
+ case MB_CUBE: /* cube is "expanded" by expz, expy and expx */
+ expz += ml->expz;
+ ATTR_FALLTHROUGH;
+ case MB_PLANE: /* plane is "expanded" by expy and expx */
+ expy += ml->expy;
+ ATTR_FALLTHROUGH;
+ case MB_TUBE: /* tube is "expanded" by expx */
+ expx += ml->expx;
+ break;
+ case MB_ELIPSOID: /* ellipsoid is "stretched" by exp* */
+ expx *= ml->expx;
+ expy *= ml->expy;
+ expz *= ml->expz;
+ break;
+ }
+
+ /* untransformed Bounding Box of MetaElem */
+ /* TODO: its possible the elem type has been changed and the exp*
+ * values can use a fallback. */
+ copy_v3_fl3(new_ml->bb->vec[0], -expx, -expy, -expz); /* 0 */
+ copy_v3_fl3(new_ml->bb->vec[1], +expx, -expy, -expz); /* 1 */
+ copy_v3_fl3(new_ml->bb->vec[2], +expx, +expy, -expz); /* 2 */
+ copy_v3_fl3(new_ml->bb->vec[3], -expx, +expy, -expz); /* 3 */
+ copy_v3_fl3(new_ml->bb->vec[4], -expx, -expy, +expz); /* 4 */
+ copy_v3_fl3(new_ml->bb->vec[5], +expx, -expy, +expz); /* 5 */
+ copy_v3_fl3(new_ml->bb->vec[6], +expx, +expy, +expz); /* 6 */
+ copy_v3_fl3(new_ml->bb->vec[7], -expx, +expy, +expz); /* 7 */
+
+ /* transformation of Metalem bb */
+ for (i = 0; i < 8; i++) {
+ mul_m4_v3((float(*)[4])new_ml->mat, new_ml->bb->vec[i]);
+ }
+
+ /* find max and min of transformed bb */
+ INIT_MINMAX(tempmin, tempmax);
+ for (i = 0; i < 8; i++) {
+ DO_MINMAX(new_ml->bb->vec[i], tempmin, tempmax);
+ }
+
+ /* set only point 0 and 6 - AABB of Metaelem */
+ copy_v3_v3(new_ml->bb->vec[0], tempmin);
+ copy_v3_v3(new_ml->bb->vec[6], tempmax);
+
+ /* add new_ml to mainb[] */
+ if (UNLIKELY(process->totelem == process->mem)) {
+ process->mem = process->mem * 2 + 10;
+ process->mainb = static_cast<MetaElem **>(
+ MEM_reallocN(process->mainb, sizeof(MetaElem *) * process->mem));
+ }
+ process->mainb[process->totelem++] = new_ml;
+ }
+ ml = ml->next;
+ }
+ }
+ }
+ }
+
+ /* compute AABB of all Metaelems */
+ if (process->totelem > 0) {
+ copy_v3_v3(process->allbb.min, process->mainb[0]->bb->vec[0]);
+ copy_v3_v3(process->allbb.max, process->mainb[0]->bb->vec[6]);
+ for (i = 1; i < process->totelem; i++) {
+ make_box_union(process->mainb[i]->bb, &process->allbb, &process->allbb);
+ }
+ }
+}
+
+Mesh *BKE_mball_polygonize(Depsgraph *depsgraph, Scene *scene, Object *ob)
+{
+ PROCESS process = {0};
+ const bool is_render = DEG_get_mode(depsgraph) == DAG_EVAL_RENDER;
+
+ MetaBall *mb = static_cast<MetaBall *>(ob->data);
+
+ process.thresh = mb->thresh;
+
+ if (process.thresh < 0.001f) {
+ process.converge_res = 16;
+ }
+ else if (process.thresh < 0.01f) {
+ process.converge_res = 8;
+ }
+ else if (process.thresh < 0.1f) {
+ process.converge_res = 4;
+ }
+ else {
+ process.converge_res = 2;
+ }
+
+ if (!is_render && (mb->flag == MB_UPDATE_NEVER)) {
+ return NULL;
+ }
+ if ((G.moving & (G_TRANSFORM_OBJ | G_TRANSFORM_EDIT)) && mb->flag == MB_UPDATE_FAST) {
+ return NULL;
+ }
+
+ if (is_render) {
+ process.size = mb->rendersize;
+ }
+ else {
+ process.size = mb->wiresize;
+ if ((G.moving & (G_TRANSFORM_OBJ | G_TRANSFORM_EDIT)) && mb->flag == MB_UPDATE_HALFRES) {
+ process.size *= 2.0f;
+ }
+ }
+
+ process.delta = process.size * 0.001f;
+
+ process.pgn_elements = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "Metaball memarena");
+
+ /* initialize all mainb (MetaElems) */
+ init_meta(depsgraph, &process, scene, ob);
+ if (process.totelem == 0) {
+ freepolygonize(&process);
+ return NULL;
+ }
+
+ build_bvh_spatial(&process, &process.metaball_bvh, 0, process.totelem, &process.allbb);
+
+ /* Don't polygonize meta-balls with too high resolution (base mball too small)
+ * NOTE: Eps was 0.0001f but this was giving problems for blood animation for
+ * the open movie "Sintel", using 0.00001f. */
+ if (ob->scale[0] < 0.00001f * (process.allbb.max[0] - process.allbb.min[0]) ||
+ ob->scale[1] < 0.00001f * (process.allbb.max[1] - process.allbb.min[1]) ||
+ ob->scale[2] < 0.00001f * (process.allbb.max[2] - process.allbb.min[2])) {
+ freepolygonize(&process);
+ return NULL;
+ }
+
+ polygonize(&process);
+ if (process.curindex == 0) {
+ freepolygonize(&process);
+ return NULL;
+ }
+
+ freepolygonize(&process);
+
+ Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, ((ID *)ob->data)->name + 2);
+
+ mesh->totvert = (int)process.curvertex;
+ MVert *mvert = static_cast<MVert *>(
+ CustomData_add_layer(&mesh->vdata, CD_MVERT, CD_CONSTRUCT, NULL, mesh->totvert));
+ for (int i = 0; i < mesh->totvert; i++) {
+ copy_v3_v3(mvert[i].co, process.co[i]);
+ }
+ MEM_freeN(process.co);
+
+ mesh->totpoly = (int)process.curindex;
+ MPoly *mpoly = static_cast<MPoly *>(
+ CustomData_add_layer(&mesh->pdata, CD_MPOLY, CD_CONSTRUCT, NULL, mesh->totpoly));
+ MLoop *mloop = static_cast<MLoop *>(
+ CustomData_add_layer(&mesh->ldata, CD_MLOOP, CD_CONSTRUCT, NULL, mesh->totpoly * 4));
+
+ int loop_offset = 0;
+ for (int i = 0; i < mesh->totpoly; i++) {
+ const int *indices = process.indices[i];
+
+ const int count = indices[2] != indices[3] ? 4 : 3;
+ mpoly[i].loopstart = loop_offset;
+ mpoly[i].totloop = count;
+ mpoly[i].flag = ME_SMOOTH;
+
+ mloop[loop_offset].v = (uint32_t)indices[0];
+ mloop[loop_offset + 1].v = (uint32_t)indices[1];
+ mloop[loop_offset + 2].v = (uint32_t)indices[2];
+ if (count == 4) {
+ mloop[loop_offset + 3].v = (uint32_t)indices[3];
+ }
+
+ loop_offset += count;
+ }
+ MEM_freeN(process.indices);
+
+ for (int i = 0; i < mesh->totvert; i++) {
+ normalize_v3(process.no[i]);
+ }
+ mesh->runtime.vert_normals = process.no;
+ BKE_mesh_vertex_normals_clear_dirty(mesh);
+
+ mesh->totloop = loop_offset;
+
+ BKE_mesh_calc_edges(mesh, false, false);
+
+ return mesh;
+}