
Was marked as a todo in the code. This does not yet take care of correct display for multi material meshes, since it would need correct separation of faces during pbvh creation. Instead we just take material of first face in node and assume that the rest faces have the same. This will create some funky effects if one attempts to sculpt in this way. Note: This does not yet address T39517
1930 lines
48 KiB
C
1930 lines
48 KiB
C
/*
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
*
|
|
* 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.
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
/** \file blender/blenkernel/intern/pbvh.c
|
|
* \ingroup bli
|
|
*/
|
|
|
|
#include "DNA_meshdata_types.h"
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_bitmap.h"
|
|
#include "BLI_math.h"
|
|
#include "BLI_utildefines.h"
|
|
#include "BLI_ghash.h"
|
|
|
|
#include "BKE_pbvh.h"
|
|
#include "BKE_ccg.h"
|
|
#include "BKE_DerivedMesh.h"
|
|
#include "BKE_mesh.h" /* for BKE_mesh_calc_normals */
|
|
#include "BKE_global.h" /* for BKE_mesh_calc_normals */
|
|
#include "BKE_paint.h"
|
|
#include "BKE_subsurf.h"
|
|
|
|
#include "GPU_buffers.h"
|
|
|
|
#include "bmesh.h"
|
|
|
|
#include "pbvh_intern.h"
|
|
|
|
#define LEAF_LIMIT 10000
|
|
|
|
//#define PERFCNTRS
|
|
|
|
#define STACK_FIXED_DEPTH 100
|
|
|
|
typedef struct PBVHStack {
|
|
PBVHNode *node;
|
|
int revisiting;
|
|
} PBVHStack;
|
|
|
|
typedef struct PBVHIter {
|
|
PBVH *bvh;
|
|
BKE_pbvh_SearchCallback scb;
|
|
void *search_data;
|
|
|
|
PBVHStack *stack;
|
|
int stacksize;
|
|
|
|
PBVHStack stackfixed[STACK_FIXED_DEPTH];
|
|
int stackspace;
|
|
} PBVHIter;
|
|
|
|
void BB_reset(BB *bb)
|
|
{
|
|
bb->bmin[0] = bb->bmin[1] = bb->bmin[2] = FLT_MAX;
|
|
bb->bmax[0] = bb->bmax[1] = bb->bmax[2] = -FLT_MAX;
|
|
}
|
|
|
|
/* Expand the bounding box to include a new coordinate */
|
|
void BB_expand(BB *bb, const float co[3])
|
|
{
|
|
int i;
|
|
for (i = 0; i < 3; ++i) {
|
|
bb->bmin[i] = min_ff(bb->bmin[i], co[i]);
|
|
bb->bmax[i] = max_ff(bb->bmax[i], co[i]);
|
|
}
|
|
}
|
|
|
|
/* Expand the bounding box to include another bounding box */
|
|
void BB_expand_with_bb(BB *bb, BB *bb2)
|
|
{
|
|
int i;
|
|
for (i = 0; i < 3; ++i) {
|
|
bb->bmin[i] = min_ff(bb->bmin[i], bb2->bmin[i]);
|
|
bb->bmax[i] = max_ff(bb->bmax[i], bb2->bmax[i]);
|
|
}
|
|
}
|
|
|
|
/* Return 0, 1, or 2 to indicate the widest axis of the bounding box */
|
|
int BB_widest_axis(const BB *bb)
|
|
{
|
|
float dim[3];
|
|
int i;
|
|
|
|
for (i = 0; i < 3; ++i)
|
|
dim[i] = bb->bmax[i] - bb->bmin[i];
|
|
|
|
if (dim[0] > dim[1]) {
|
|
if (dim[0] > dim[2])
|
|
return 0;
|
|
else
|
|
return 2;
|
|
}
|
|
else {
|
|
if (dim[1] > dim[2])
|
|
return 1;
|
|
else
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
void BBC_update_centroid(BBC *bbc)
|
|
{
|
|
int i;
|
|
for (i = 0; i < 3; ++i)
|
|
bbc->bcentroid[i] = (bbc->bmin[i] + bbc->bmax[i]) * 0.5f;
|
|
}
|
|
|
|
/* Not recursive */
|
|
static void update_node_vb(PBVH *bvh, PBVHNode *node)
|
|
{
|
|
BB vb;
|
|
|
|
BB_reset(&vb);
|
|
|
|
if (node->flag & PBVH_Leaf) {
|
|
PBVHVertexIter vd;
|
|
|
|
BKE_pbvh_vertex_iter_begin(bvh, node, vd, PBVH_ITER_ALL)
|
|
{
|
|
BB_expand(&vb, vd.co);
|
|
}
|
|
BKE_pbvh_vertex_iter_end;
|
|
}
|
|
else {
|
|
BB_expand_with_bb(&vb,
|
|
&bvh->nodes[node->children_offset].vb);
|
|
BB_expand_with_bb(&vb,
|
|
&bvh->nodes[node->children_offset + 1].vb);
|
|
}
|
|
|
|
node->vb = vb;
|
|
}
|
|
|
|
//void BKE_pbvh_node_BB_reset(PBVHNode *node)
|
|
//{
|
|
// BB_reset(&node->vb);
|
|
//}
|
|
//
|
|
//void BKE_pbvh_node_BB_expand(PBVHNode *node, float co[3])
|
|
//{
|
|
// BB_expand(&node->vb, co);
|
|
//}
|
|
|
|
static int face_materials_match(const MFace *f1, const MFace *f2)
|
|
{
|
|
return ((f1->flag & ME_SMOOTH) == (f2->flag & ME_SMOOTH) &&
|
|
(f1->mat_nr == f2->mat_nr));
|
|
}
|
|
|
|
static int grid_materials_match(const DMFlagMat *f1, const DMFlagMat *f2)
|
|
{
|
|
return ((f1->flag & ME_SMOOTH) == (f2->flag & ME_SMOOTH) &&
|
|
(f1->mat_nr == f2->mat_nr));
|
|
}
|
|
|
|
/* Adapted from BLI_kdopbvh.c */
|
|
/* Returns the index of the first element on the right of the partition */
|
|
static int partition_indices(int *prim_indices, int lo, int hi, int axis,
|
|
float mid, BBC *prim_bbc)
|
|
{
|
|
int i = lo, j = hi;
|
|
for (;; ) {
|
|
for (; prim_bbc[prim_indices[i]].bcentroid[axis] < mid; i++) ;
|
|
for (; mid < prim_bbc[prim_indices[j]].bcentroid[axis]; j--) ;
|
|
|
|
if (!(i < j))
|
|
return i;
|
|
|
|
SWAP(int, prim_indices[i], prim_indices[j]);
|
|
i++;
|
|
}
|
|
}
|
|
|
|
/* Returns the index of the first element on the right of the partition */
|
|
static int partition_indices_material(PBVH *bvh, int lo, int hi)
|
|
{
|
|
const MFace *faces = bvh->faces;
|
|
const DMFlagMat *flagmats = bvh->grid_flag_mats;
|
|
const int *indices = bvh->prim_indices;
|
|
const void *first;
|
|
int i = lo, j = hi;
|
|
|
|
if (bvh->faces)
|
|
first = &faces[bvh->prim_indices[lo]];
|
|
else
|
|
first = &flagmats[bvh->prim_indices[lo]];
|
|
|
|
for (;; ) {
|
|
if (bvh->faces) {
|
|
for (; face_materials_match(first, &faces[indices[i]]); i++) ;
|
|
for (; !face_materials_match(first, &faces[indices[j]]); j--) ;
|
|
}
|
|
else {
|
|
for (; grid_materials_match(first, &flagmats[indices[i]]); i++) ;
|
|
for (; !grid_materials_match(first, &flagmats[indices[j]]); j--) ;
|
|
}
|
|
|
|
if (!(i < j))
|
|
return i;
|
|
|
|
SWAP(int, bvh->prim_indices[i], bvh->prim_indices[j]);
|
|
i++;
|
|
}
|
|
}
|
|
|
|
void pbvh_grow_nodes(PBVH *bvh, int totnode)
|
|
{
|
|
if (totnode > bvh->node_mem_count) {
|
|
PBVHNode *prev = bvh->nodes;
|
|
bvh->node_mem_count *= 1.33;
|
|
if (bvh->node_mem_count < totnode)
|
|
bvh->node_mem_count = totnode;
|
|
bvh->nodes = MEM_mallocN(sizeof(PBVHNode) * bvh->node_mem_count,
|
|
"bvh nodes");
|
|
memcpy(bvh->nodes, prev, bvh->totnode * sizeof(PBVHNode));
|
|
memset(bvh->nodes + bvh->totnode, 0, (bvh->node_mem_count - bvh->totnode) * sizeof(PBVHNode));
|
|
MEM_freeN(prev);
|
|
}
|
|
|
|
bvh->totnode = totnode;
|
|
}
|
|
|
|
/* Add a vertex to the map, with a positive value for unique vertices and
|
|
* a negative value for additional vertices */
|
|
static int map_insert_vert(PBVH *bvh, GHash *map,
|
|
unsigned int *face_verts,
|
|
unsigned int *uniq_verts, int vertex)
|
|
{
|
|
void *key, **value_p;
|
|
|
|
key = SET_INT_IN_POINTER(vertex);
|
|
value_p = BLI_ghash_lookup_p(map, key);
|
|
|
|
if (value_p == NULL) {
|
|
void *value;
|
|
if (BLI_BITMAP_GET(bvh->vert_bitmap, vertex)) {
|
|
value = SET_INT_IN_POINTER(~(*face_verts));
|
|
++(*face_verts);
|
|
}
|
|
else {
|
|
BLI_BITMAP_SET(bvh->vert_bitmap, vertex);
|
|
value = SET_INT_IN_POINTER(*uniq_verts);
|
|
++(*uniq_verts);
|
|
}
|
|
|
|
BLI_ghash_insert(map, key, value);
|
|
return GET_INT_FROM_POINTER(value);
|
|
}
|
|
else {
|
|
return GET_INT_FROM_POINTER(*value_p);
|
|
}
|
|
}
|
|
|
|
/* Find vertices used by the faces in this node and update the draw buffers */
|
|
static void build_mesh_leaf_node(PBVH *bvh, PBVHNode *node)
|
|
{
|
|
GHashIterator *iter;
|
|
GHash *map;
|
|
int i, j, totface;
|
|
|
|
node->uniq_verts = node->face_verts = 0;
|
|
totface = node->totprim;
|
|
|
|
/* reserve size is rough guess */
|
|
map = BLI_ghash_int_new_ex("build_mesh_leaf_node gh", 2 * totface);
|
|
|
|
node->face_vert_indices = MEM_callocN(sizeof(int) * 4 * totface,
|
|
"bvh node face vert indices");
|
|
|
|
for (i = 0; i < totface; ++i) {
|
|
MFace *f = bvh->faces + node->prim_indices[i];
|
|
int sides = f->v4 ? 4 : 3;
|
|
|
|
for (j = 0; j < sides; ++j) {
|
|
node->face_vert_indices[i][j] =
|
|
map_insert_vert(bvh, map, &node->face_verts,
|
|
&node->uniq_verts, (&f->v1)[j]);
|
|
}
|
|
}
|
|
|
|
node->vert_indices = MEM_callocN(sizeof(int) *
|
|
(node->uniq_verts + node->face_verts),
|
|
"bvh node vert indices");
|
|
|
|
/* Build the vertex list, unique verts first */
|
|
for (iter = BLI_ghashIterator_new(map), i = 0;
|
|
BLI_ghashIterator_done(iter) == false;
|
|
BLI_ghashIterator_step(iter), ++i)
|
|
{
|
|
void *value = BLI_ghashIterator_getValue(iter);
|
|
int ndx = GET_INT_FROM_POINTER(value);
|
|
|
|
if (ndx < 0)
|
|
ndx = -ndx + node->uniq_verts - 1;
|
|
|
|
node->vert_indices[ndx] =
|
|
GET_INT_FROM_POINTER(BLI_ghashIterator_getKey(iter));
|
|
}
|
|
|
|
BLI_ghashIterator_free(iter);
|
|
|
|
for (i = 0; i < totface; ++i) {
|
|
MFace *f = bvh->faces + node->prim_indices[i];
|
|
int sides = f->v4 ? 4 : 3;
|
|
|
|
for (j = 0; j < sides; ++j) {
|
|
if (node->face_vert_indices[i][j] < 0)
|
|
node->face_vert_indices[i][j] =
|
|
-node->face_vert_indices[i][j] +
|
|
node->uniq_verts - 1;
|
|
}
|
|
}
|
|
|
|
BKE_pbvh_node_mark_rebuild_draw(node);
|
|
|
|
BLI_ghash_free(map, NULL, NULL);
|
|
}
|
|
|
|
static void update_vb(PBVH *bvh, PBVHNode *node, BBC *prim_bbc,
|
|
int offset, int count)
|
|
{
|
|
int i;
|
|
|
|
BB_reset(&node->vb);
|
|
for (i = offset + count - 1; i >= offset; --i) {
|
|
BB_expand_with_bb(&node->vb, (BB *)(&prim_bbc[bvh->prim_indices[i]]));
|
|
}
|
|
node->orig_vb = node->vb;
|
|
}
|
|
|
|
static void build_leaf(PBVH *bvh, int node_index, BBC *prim_bbc,
|
|
int offset, int count)
|
|
{
|
|
bvh->nodes[node_index].flag |= PBVH_Leaf;
|
|
|
|
bvh->nodes[node_index].prim_indices = bvh->prim_indices + offset;
|
|
bvh->nodes[node_index].totprim = count;
|
|
|
|
/* Still need vb for searches */
|
|
update_vb(bvh, &bvh->nodes[node_index], prim_bbc, offset, count);
|
|
|
|
if (bvh->faces)
|
|
build_mesh_leaf_node(bvh, bvh->nodes + node_index);
|
|
else
|
|
BKE_pbvh_node_mark_rebuild_draw(bvh->nodes + node_index);
|
|
}
|
|
|
|
/* Return zero if all primitives in the node can be drawn with the
|
|
* same material (including flat/smooth shading), non-zerootherwise */
|
|
static int leaf_needs_material_split(PBVH *bvh, int offset, int count)
|
|
{
|
|
int i, prim;
|
|
|
|
if (count <= 1)
|
|
return 0;
|
|
|
|
if (bvh->faces) {
|
|
const MFace *first = &bvh->faces[bvh->prim_indices[offset]];
|
|
|
|
for (i = offset + count - 1; i > offset; --i) {
|
|
prim = bvh->prim_indices[i];
|
|
if (!face_materials_match(first, &bvh->faces[prim]))
|
|
return 1;
|
|
}
|
|
}
|
|
else {
|
|
const DMFlagMat *first = &bvh->grid_flag_mats[bvh->prim_indices[offset]];
|
|
|
|
for (i = offset + count - 1; i > offset; --i) {
|
|
prim = bvh->prim_indices[i];
|
|
if (!grid_materials_match(first, &bvh->grid_flag_mats[prim]))
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Recursively build a node in the tree
|
|
*
|
|
* vb is the voxel box around all of the primitives contained in
|
|
* this node.
|
|
*
|
|
* cb is the bounding box around all the centroids of the primitives
|
|
* contained in this node
|
|
*
|
|
* offset and start indicate a range in the array of primitive indices
|
|
*/
|
|
|
|
static void build_sub(PBVH *bvh, int node_index, BB *cb, BBC *prim_bbc,
|
|
int offset, int count)
|
|
{
|
|
int i, axis, end, below_leaf_limit;
|
|
BB cb_backing;
|
|
|
|
/* Decide whether this is a leaf or not */
|
|
below_leaf_limit = count <= bvh->leaf_limit;
|
|
if (below_leaf_limit) {
|
|
if (!leaf_needs_material_split(bvh, offset, count)) {
|
|
build_leaf(bvh, node_index, prim_bbc, offset, count);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Add two child nodes */
|
|
bvh->nodes[node_index].children_offset = bvh->totnode;
|
|
pbvh_grow_nodes(bvh, bvh->totnode + 2);
|
|
|
|
/* Update parent node bounding box */
|
|
update_vb(bvh, &bvh->nodes[node_index], prim_bbc, offset, count);
|
|
|
|
if (!below_leaf_limit) {
|
|
/* Find axis with widest range of primitive centroids */
|
|
if (!cb) {
|
|
cb = &cb_backing;
|
|
BB_reset(cb);
|
|
for (i = offset + count - 1; i >= offset; --i)
|
|
BB_expand(cb, prim_bbc[bvh->prim_indices[i]].bcentroid);
|
|
}
|
|
axis = BB_widest_axis(cb);
|
|
|
|
/* Partition primitives along that axis */
|
|
end = partition_indices(bvh->prim_indices,
|
|
offset, offset + count - 1,
|
|
axis,
|
|
(cb->bmax[axis] + cb->bmin[axis]) * 0.5f,
|
|
prim_bbc);
|
|
}
|
|
else {
|
|
/* Partition primitives by material */
|
|
end = partition_indices_material(bvh, offset, offset + count - 1);
|
|
}
|
|
|
|
/* Build children */
|
|
build_sub(bvh, bvh->nodes[node_index].children_offset, NULL,
|
|
prim_bbc, offset, end - offset);
|
|
build_sub(bvh, bvh->nodes[node_index].children_offset + 1, NULL,
|
|
prim_bbc, end, offset + count - end);
|
|
}
|
|
|
|
static void pbvh_build(PBVH *bvh, BB *cb, BBC *prim_bbc, int totprim)
|
|
{
|
|
int i;
|
|
|
|
if (totprim != bvh->totprim) {
|
|
bvh->totprim = totprim;
|
|
if (bvh->nodes) MEM_freeN(bvh->nodes);
|
|
if (bvh->prim_indices) MEM_freeN(bvh->prim_indices);
|
|
bvh->prim_indices = MEM_callocN(sizeof(int) * totprim,
|
|
"bvh prim indices");
|
|
for (i = 0; i < totprim; ++i)
|
|
bvh->prim_indices[i] = i;
|
|
bvh->totnode = 0;
|
|
if (bvh->node_mem_count < 100) {
|
|
bvh->node_mem_count = 100;
|
|
bvh->nodes = MEM_callocN(sizeof(PBVHNode) *
|
|
bvh->node_mem_count,
|
|
"bvh initial nodes");
|
|
}
|
|
}
|
|
|
|
bvh->totnode = 1;
|
|
build_sub(bvh, 0, cb, prim_bbc, 0, totprim);
|
|
}
|
|
|
|
/* Do a full rebuild with on Mesh data structure */
|
|
void BKE_pbvh_build_mesh(PBVH *bvh, MFace *faces, MVert *verts, int totface, int totvert, struct CustomData *vdata)
|
|
{
|
|
BBC *prim_bbc = NULL;
|
|
BB cb;
|
|
int i, j;
|
|
|
|
bvh->type = PBVH_FACES;
|
|
bvh->faces = faces;
|
|
bvh->verts = verts;
|
|
bvh->vert_bitmap = BLI_BITMAP_NEW(totvert, "bvh->vert_bitmap");
|
|
bvh->totvert = totvert;
|
|
bvh->leaf_limit = LEAF_LIMIT;
|
|
bvh->vdata = vdata;
|
|
|
|
BB_reset(&cb);
|
|
|
|
/* For each face, store the AABB and the AABB centroid */
|
|
prim_bbc = MEM_mallocN(sizeof(BBC) * totface, "prim_bbc");
|
|
|
|
for (i = 0; i < totface; ++i) {
|
|
MFace *f = faces + i;
|
|
const int sides = f->v4 ? 4 : 3;
|
|
BBC *bbc = prim_bbc + i;
|
|
|
|
BB_reset((BB *)bbc);
|
|
|
|
for (j = 0; j < sides; ++j)
|
|
BB_expand((BB *)bbc, verts[(&f->v1)[j]].co);
|
|
|
|
BBC_update_centroid(bbc);
|
|
|
|
BB_expand(&cb, bbc->bcentroid);
|
|
}
|
|
|
|
if (totface)
|
|
pbvh_build(bvh, &cb, prim_bbc, totface);
|
|
|
|
MEM_freeN(prim_bbc);
|
|
MEM_freeN(bvh->vert_bitmap);
|
|
}
|
|
|
|
/* Do a full rebuild with on Grids data structure */
|
|
void BKE_pbvh_build_grids(PBVH *bvh, CCGElem **grids, DMGridAdjacency *gridadj,
|
|
int totgrid, CCGKey *key, void **gridfaces, DMFlagMat *flagmats, BLI_bitmap **grid_hidden)
|
|
{
|
|
BBC *prim_bbc = NULL;
|
|
BB cb;
|
|
int gridsize = key->grid_size;
|
|
int i, j;
|
|
|
|
bvh->type = PBVH_GRIDS;
|
|
bvh->grids = grids;
|
|
bvh->gridadj = gridadj;
|
|
bvh->gridfaces = gridfaces;
|
|
bvh->grid_flag_mats = flagmats;
|
|
bvh->totgrid = totgrid;
|
|
bvh->gridkey = *key;
|
|
bvh->grid_hidden = grid_hidden;
|
|
bvh->leaf_limit = max_ii(LEAF_LIMIT / ((gridsize - 1) * (gridsize - 1)), 1);
|
|
|
|
BB_reset(&cb);
|
|
|
|
/* For each grid, store the AABB and the AABB centroid */
|
|
prim_bbc = MEM_mallocN(sizeof(BBC) * totgrid, "prim_bbc");
|
|
|
|
for (i = 0; i < totgrid; ++i) {
|
|
CCGElem *grid = grids[i];
|
|
BBC *bbc = prim_bbc + i;
|
|
|
|
BB_reset((BB *)bbc);
|
|
|
|
for (j = 0; j < gridsize * gridsize; ++j)
|
|
BB_expand((BB *)bbc, CCG_elem_offset_co(key, grid, j));
|
|
|
|
BBC_update_centroid(bbc);
|
|
|
|
BB_expand(&cb, bbc->bcentroid);
|
|
}
|
|
|
|
if (totgrid)
|
|
pbvh_build(bvh, &cb, prim_bbc, totgrid);
|
|
|
|
MEM_freeN(prim_bbc);
|
|
}
|
|
|
|
PBVH *BKE_pbvh_new(void)
|
|
{
|
|
PBVH *bvh = MEM_callocN(sizeof(PBVH), "pbvh");
|
|
|
|
return bvh;
|
|
}
|
|
|
|
void BKE_pbvh_free(PBVH *bvh)
|
|
{
|
|
PBVHNode *node;
|
|
int i;
|
|
|
|
for (i = 0; i < bvh->totnode; ++i) {
|
|
node = &bvh->nodes[i];
|
|
|
|
if (node->flag & PBVH_Leaf) {
|
|
if (node->draw_buffers)
|
|
GPU_free_pbvh_buffers(node->draw_buffers);
|
|
if (node->vert_indices)
|
|
MEM_freeN(node->vert_indices);
|
|
if (node->face_vert_indices)
|
|
MEM_freeN(node->face_vert_indices);
|
|
BKE_pbvh_node_layer_disp_free(node);
|
|
|
|
if (node->bm_faces)
|
|
BLI_ghash_free(node->bm_faces, NULL, NULL);
|
|
if (node->bm_unique_verts)
|
|
BLI_gset_free(node->bm_unique_verts, NULL);
|
|
if (node->bm_other_verts)
|
|
BLI_gset_free(node->bm_other_verts, NULL);
|
|
}
|
|
}
|
|
|
|
if (bvh->deformed) {
|
|
if (bvh->verts) {
|
|
/* if pbvh was deformed, new memory was allocated for verts/faces -- free it */
|
|
|
|
MEM_freeN(bvh->verts);
|
|
if (bvh->faces)
|
|
MEM_freeN(bvh->faces);
|
|
}
|
|
}
|
|
|
|
if (bvh->nodes)
|
|
MEM_freeN(bvh->nodes);
|
|
|
|
if (bvh->prim_indices)
|
|
MEM_freeN(bvh->prim_indices);
|
|
|
|
if (bvh->bm_vert_to_node)
|
|
BLI_ghash_free(bvh->bm_vert_to_node, NULL, NULL);
|
|
if (bvh->bm_face_to_node)
|
|
BLI_ghash_free(bvh->bm_face_to_node, NULL, NULL);
|
|
|
|
MEM_freeN(bvh);
|
|
}
|
|
|
|
void BKE_pbvh_free_layer_disp(PBVH *bvh)
|
|
{
|
|
int i;
|
|
for (i = 0; i < bvh->totnode; ++i)
|
|
BKE_pbvh_node_layer_disp_free(&bvh->nodes[i]);
|
|
}
|
|
|
|
static void pbvh_iter_begin(PBVHIter *iter, PBVH *bvh, BKE_pbvh_SearchCallback scb, void *search_data)
|
|
{
|
|
iter->bvh = bvh;
|
|
iter->scb = scb;
|
|
iter->search_data = search_data;
|
|
|
|
iter->stack = iter->stackfixed;
|
|
iter->stackspace = STACK_FIXED_DEPTH;
|
|
|
|
iter->stack[0].node = bvh->nodes;
|
|
iter->stack[0].revisiting = 0;
|
|
iter->stacksize = 1;
|
|
}
|
|
|
|
static void pbvh_iter_end(PBVHIter *iter)
|
|
{
|
|
if (iter->stackspace > STACK_FIXED_DEPTH)
|
|
MEM_freeN(iter->stack);
|
|
}
|
|
|
|
static void pbvh_stack_push(PBVHIter *iter, PBVHNode *node, int revisiting)
|
|
{
|
|
if (iter->stacksize == iter->stackspace) {
|
|
PBVHStack *newstack;
|
|
|
|
iter->stackspace *= 2;
|
|
newstack = MEM_callocN(sizeof(PBVHStack) * iter->stackspace, "PBVHStack");
|
|
memcpy(newstack, iter->stack, sizeof(PBVHStack) * iter->stacksize);
|
|
|
|
if (iter->stackspace > STACK_FIXED_DEPTH)
|
|
MEM_freeN(iter->stack);
|
|
iter->stack = newstack;
|
|
}
|
|
|
|
iter->stack[iter->stacksize].node = node;
|
|
iter->stack[iter->stacksize].revisiting = revisiting;
|
|
iter->stacksize++;
|
|
}
|
|
|
|
static PBVHNode *pbvh_iter_next(PBVHIter *iter)
|
|
{
|
|
PBVHNode *node;
|
|
int revisiting;
|
|
|
|
/* purpose here is to traverse tree, visiting child nodes before their
|
|
* parents, this order is necessary for e.g. computing bounding boxes */
|
|
|
|
while (iter->stacksize) {
|
|
/* pop node */
|
|
iter->stacksize--;
|
|
node = iter->stack[iter->stacksize].node;
|
|
|
|
/* on a mesh with no faces this can happen
|
|
* can remove this check if we know meshes have at least 1 face */
|
|
if (node == NULL)
|
|
return NULL;
|
|
|
|
revisiting = iter->stack[iter->stacksize].revisiting;
|
|
|
|
/* revisiting node already checked */
|
|
if (revisiting)
|
|
return node;
|
|
|
|
if (iter->scb && !iter->scb(node, iter->search_data))
|
|
continue; /* don't traverse, outside of search zone */
|
|
|
|
if (node->flag & PBVH_Leaf) {
|
|
/* immediately hit leaf node */
|
|
return node;
|
|
}
|
|
else {
|
|
/* come back later when children are done */
|
|
pbvh_stack_push(iter, node, 1);
|
|
|
|
/* push two child nodes on the stack */
|
|
pbvh_stack_push(iter, iter->bvh->nodes + node->children_offset + 1, 0);
|
|
pbvh_stack_push(iter, iter->bvh->nodes + node->children_offset, 0);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static PBVHNode *pbvh_iter_next_occluded(PBVHIter *iter)
|
|
{
|
|
PBVHNode *node;
|
|
|
|
while (iter->stacksize) {
|
|
/* pop node */
|
|
iter->stacksize--;
|
|
node = iter->stack[iter->stacksize].node;
|
|
|
|
/* on a mesh with no faces this can happen
|
|
* can remove this check if we know meshes have at least 1 face */
|
|
if (node == NULL) return NULL;
|
|
|
|
if (iter->scb && !iter->scb(node, iter->search_data)) continue; /* don't traverse, outside of search zone */
|
|
|
|
if (node->flag & PBVH_Leaf) {
|
|
/* immediately hit leaf node */
|
|
return node;
|
|
}
|
|
else {
|
|
pbvh_stack_push(iter, iter->bvh->nodes + node->children_offset + 1, 0);
|
|
pbvh_stack_push(iter, iter->bvh->nodes + node->children_offset, 0);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void BKE_pbvh_search_gather(PBVH *bvh,
|
|
BKE_pbvh_SearchCallback scb, void *search_data,
|
|
PBVHNode ***r_array, int *r_tot)
|
|
{
|
|
PBVHIter iter;
|
|
PBVHNode **array = NULL, *node;
|
|
int tot = 0, space = 0;
|
|
|
|
pbvh_iter_begin(&iter, bvh, scb, search_data);
|
|
|
|
while ((node = pbvh_iter_next(&iter))) {
|
|
if (node->flag & PBVH_Leaf) {
|
|
if (tot == space) {
|
|
/* resize array if needed */
|
|
space = (tot == 0) ? 32 : space * 2;
|
|
array = MEM_recallocN_id(array, sizeof(PBVHNode *) * space, __func__);
|
|
}
|
|
|
|
array[tot] = node;
|
|
tot++;
|
|
}
|
|
}
|
|
|
|
pbvh_iter_end(&iter);
|
|
|
|
if (tot == 0 && array) {
|
|
MEM_freeN(array);
|
|
array = NULL;
|
|
}
|
|
|
|
*r_array = array;
|
|
*r_tot = tot;
|
|
}
|
|
|
|
void BKE_pbvh_search_callback(PBVH *bvh,
|
|
BKE_pbvh_SearchCallback scb, void *search_data,
|
|
BKE_pbvh_HitCallback hcb, void *hit_data)
|
|
{
|
|
PBVHIter iter;
|
|
PBVHNode *node;
|
|
|
|
pbvh_iter_begin(&iter, bvh, scb, search_data);
|
|
|
|
while ((node = pbvh_iter_next(&iter)))
|
|
if (node->flag & PBVH_Leaf)
|
|
hcb(node, hit_data);
|
|
|
|
pbvh_iter_end(&iter);
|
|
}
|
|
|
|
typedef struct node_tree {
|
|
PBVHNode *data;
|
|
|
|
struct node_tree *left;
|
|
struct node_tree *right;
|
|
} node_tree;
|
|
|
|
static void node_tree_insert(node_tree *tree, node_tree *new_node)
|
|
{
|
|
if (new_node->data->tmin < tree->data->tmin) {
|
|
if (tree->left) {
|
|
node_tree_insert(tree->left, new_node);
|
|
}
|
|
else {
|
|
tree->left = new_node;
|
|
}
|
|
}
|
|
else {
|
|
if (tree->right) {
|
|
node_tree_insert(tree->right, new_node);
|
|
}
|
|
else {
|
|
tree->right = new_node;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void traverse_tree(node_tree *tree, BKE_pbvh_HitOccludedCallback hcb, void *hit_data, float *tmin)
|
|
{
|
|
if (tree->left) traverse_tree(tree->left, hcb, hit_data, tmin);
|
|
|
|
hcb(tree->data, hit_data, tmin);
|
|
|
|
if (tree->right) traverse_tree(tree->right, hcb, hit_data, tmin);
|
|
}
|
|
|
|
static void free_tree(node_tree *tree)
|
|
{
|
|
if (tree->left) {
|
|
free_tree(tree->left);
|
|
tree->left = NULL;
|
|
}
|
|
|
|
if (tree->right) {
|
|
free_tree(tree->right);
|
|
tree->right = NULL;
|
|
}
|
|
|
|
free(tree);
|
|
}
|
|
|
|
float BKE_pbvh_node_get_tmin(PBVHNode *node)
|
|
{
|
|
return node->tmin;
|
|
}
|
|
|
|
static void BKE_pbvh_search_callback_occluded(PBVH *bvh,
|
|
BKE_pbvh_SearchCallback scb, void *search_data,
|
|
BKE_pbvh_HitOccludedCallback hcb, void *hit_data)
|
|
{
|
|
PBVHIter iter;
|
|
PBVHNode *node;
|
|
node_tree *tree = NULL;
|
|
|
|
pbvh_iter_begin(&iter, bvh, scb, search_data);
|
|
|
|
while ((node = pbvh_iter_next_occluded(&iter))) {
|
|
if (node->flag & PBVH_Leaf) {
|
|
node_tree *new_node = malloc(sizeof(node_tree));
|
|
|
|
new_node->data = node;
|
|
|
|
new_node->left = NULL;
|
|
new_node->right = NULL;
|
|
|
|
if (tree) {
|
|
node_tree_insert(tree, new_node);
|
|
}
|
|
else {
|
|
tree = new_node;
|
|
}
|
|
}
|
|
}
|
|
|
|
pbvh_iter_end(&iter);
|
|
|
|
if (tree) {
|
|
float tmin = FLT_MAX;
|
|
traverse_tree(tree, hcb, hit_data, &tmin);
|
|
free_tree(tree);
|
|
}
|
|
}
|
|
|
|
static bool update_search_cb(PBVHNode *node, void *data_v)
|
|
{
|
|
int flag = GET_INT_FROM_POINTER(data_v);
|
|
|
|
if (node->flag & PBVH_Leaf)
|
|
return (node->flag & flag) != 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void pbvh_update_normals(PBVH *bvh, PBVHNode **nodes,
|
|
int totnode, float (*face_nors)[3])
|
|
{
|
|
float (*vnor)[3];
|
|
int n;
|
|
|
|
if (bvh->type == PBVH_BMESH) {
|
|
pbvh_bmesh_normals_update(nodes, totnode);
|
|
return;
|
|
}
|
|
|
|
if (bvh->type != PBVH_FACES)
|
|
return;
|
|
|
|
/* could be per node to save some memory, but also means
|
|
* we have to store for each vertex which node it is in */
|
|
vnor = MEM_callocN(sizeof(float) * 3 * bvh->totvert, "bvh temp vnors");
|
|
|
|
/* subtle assumptions:
|
|
* - We know that for all edited vertices, the nodes with faces
|
|
* adjacent to these vertices have been marked with PBVH_UpdateNormals.
|
|
* This is true because if the vertex is inside the brush radius, the
|
|
* bounding box of it's adjacent faces will be as well.
|
|
* - However this is only true for the vertices that have actually been
|
|
* edited, not for all vertices in the nodes marked for update, so we
|
|
* can only update vertices marked with ME_VERT_PBVH_UPDATE.
|
|
*/
|
|
|
|
#pragma omp parallel for private(n) schedule(static)
|
|
for (n = 0; n < totnode; n++) {
|
|
PBVHNode *node = nodes[n];
|
|
|
|
if ((node->flag & PBVH_UpdateNormals)) {
|
|
int i, j, totface, *faces;
|
|
|
|
faces = node->prim_indices;
|
|
totface = node->totprim;
|
|
|
|
for (i = 0; i < totface; ++i) {
|
|
MFace *f = bvh->faces + faces[i];
|
|
float fn[3];
|
|
unsigned int *fv = &f->v1;
|
|
int sides = (f->v4) ? 4 : 3;
|
|
|
|
if (f->v4)
|
|
normal_quad_v3(fn, bvh->verts[f->v1].co, bvh->verts[f->v2].co,
|
|
bvh->verts[f->v3].co, bvh->verts[f->v4].co);
|
|
else
|
|
normal_tri_v3(fn, bvh->verts[f->v1].co, bvh->verts[f->v2].co,
|
|
bvh->verts[f->v3].co);
|
|
|
|
for (j = 0; j < sides; ++j) {
|
|
int v = fv[j];
|
|
|
|
if (bvh->verts[v].flag & ME_VERT_PBVH_UPDATE) {
|
|
/* this seems like it could be very slow but profile
|
|
* does not show this, so just leave it for now? */
|
|
#pragma omp atomic
|
|
vnor[v][0] += fn[0];
|
|
#pragma omp atomic
|
|
vnor[v][1] += fn[1];
|
|
#pragma omp atomic
|
|
vnor[v][2] += fn[2];
|
|
}
|
|
}
|
|
|
|
if (face_nors)
|
|
copy_v3_v3(face_nors[faces[i]], fn);
|
|
}
|
|
}
|
|
}
|
|
|
|
#pragma omp parallel for private(n) schedule(static)
|
|
for (n = 0; n < totnode; n++) {
|
|
PBVHNode *node = nodes[n];
|
|
|
|
if (node->flag & PBVH_UpdateNormals) {
|
|
int i, *verts, totvert;
|
|
|
|
verts = node->vert_indices;
|
|
totvert = node->uniq_verts;
|
|
|
|
for (i = 0; i < totvert; ++i) {
|
|
const int v = verts[i];
|
|
MVert *mvert = &bvh->verts[v];
|
|
|
|
if (mvert->flag & ME_VERT_PBVH_UPDATE) {
|
|
float no[3];
|
|
|
|
copy_v3_v3(no, vnor[v]);
|
|
normalize_v3(no);
|
|
normal_float_to_short_v3(mvert->no, no);
|
|
|
|
mvert->flag &= ~ME_VERT_PBVH_UPDATE;
|
|
}
|
|
}
|
|
|
|
node->flag &= ~PBVH_UpdateNormals;
|
|
}
|
|
}
|
|
|
|
MEM_freeN(vnor);
|
|
}
|
|
|
|
void pbvh_update_BB_redraw(PBVH *bvh, PBVHNode **nodes, int totnode, int flag)
|
|
{
|
|
int n;
|
|
|
|
/* update BB, redraw flag */
|
|
#pragma omp parallel for private(n) schedule(static)
|
|
for (n = 0; n < totnode; n++) {
|
|
PBVHNode *node = nodes[n];
|
|
|
|
if ((flag & PBVH_UpdateBB) && (node->flag & PBVH_UpdateBB))
|
|
/* don't clear flag yet, leave it for flushing later */
|
|
update_node_vb(bvh, node);
|
|
|
|
if ((flag & PBVH_UpdateOriginalBB) && (node->flag & PBVH_UpdateOriginalBB))
|
|
node->orig_vb = node->vb;
|
|
|
|
if ((flag & PBVH_UpdateRedraw) && (node->flag & PBVH_UpdateRedraw))
|
|
node->flag &= ~PBVH_UpdateRedraw;
|
|
}
|
|
}
|
|
|
|
static void pbvh_update_draw_buffers(PBVH *bvh, PBVHNode **nodes, int totnode)
|
|
{
|
|
PBVHNode *node;
|
|
int n;
|
|
|
|
/* can't be done in parallel with OpenGL */
|
|
for (n = 0; n < totnode; n++) {
|
|
node = nodes[n];
|
|
|
|
if (node->flag & PBVH_RebuildDrawBuffers) {
|
|
GPU_free_pbvh_buffers(node->draw_buffers);
|
|
switch (bvh->type) {
|
|
case PBVH_GRIDS:
|
|
node->draw_buffers =
|
|
GPU_build_grid_pbvh_buffers(node->prim_indices,
|
|
node->totprim,
|
|
bvh->grid_hidden,
|
|
bvh->gridkey.grid_size);
|
|
break;
|
|
case PBVH_FACES:
|
|
node->draw_buffers =
|
|
GPU_build_pbvh_mesh_buffers(node->face_vert_indices,
|
|
bvh->faces, bvh->verts,
|
|
node->prim_indices,
|
|
node->totprim);
|
|
break;
|
|
case PBVH_BMESH:
|
|
node->draw_buffers =
|
|
GPU_build_bmesh_pbvh_buffers(bvh->flags &
|
|
PBVH_DYNTOPO_SMOOTH_SHADING);
|
|
break;
|
|
}
|
|
|
|
node->flag &= ~PBVH_RebuildDrawBuffers;
|
|
}
|
|
|
|
if (node->flag & PBVH_UpdateDrawBuffers) {
|
|
switch (bvh->type) {
|
|
case PBVH_GRIDS:
|
|
GPU_update_grid_pbvh_buffers(node->draw_buffers,
|
|
bvh->grids,
|
|
bvh->grid_flag_mats,
|
|
node->prim_indices,
|
|
node->totprim,
|
|
&bvh->gridkey,
|
|
bvh->show_diffuse_color);
|
|
break;
|
|
case PBVH_FACES:
|
|
GPU_update_mesh_pbvh_buffers(node->draw_buffers,
|
|
bvh->verts,
|
|
node->vert_indices,
|
|
node->uniq_verts +
|
|
node->face_verts,
|
|
CustomData_get_layer(bvh->vdata,
|
|
CD_PAINT_MASK),
|
|
node->face_vert_indices,
|
|
bvh->show_diffuse_color);
|
|
break;
|
|
case PBVH_BMESH:
|
|
GPU_update_bmesh_pbvh_buffers(node->draw_buffers,
|
|
bvh->bm,
|
|
node->bm_faces,
|
|
node->bm_unique_verts,
|
|
node->bm_other_verts,
|
|
bvh->show_diffuse_color);
|
|
break;
|
|
}
|
|
|
|
node->flag &= ~PBVH_UpdateDrawBuffers;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int pbvh_flush_bb(PBVH *bvh, PBVHNode *node, int flag)
|
|
{
|
|
int update = 0;
|
|
|
|
/* difficult to multithread well, we just do single threaded recursive */
|
|
if (node->flag & PBVH_Leaf) {
|
|
if (flag & PBVH_UpdateBB) {
|
|
update |= (node->flag & PBVH_UpdateBB);
|
|
node->flag &= ~PBVH_UpdateBB;
|
|
}
|
|
|
|
if (flag & PBVH_UpdateOriginalBB) {
|
|
update |= (node->flag & PBVH_UpdateOriginalBB);
|
|
node->flag &= ~PBVH_UpdateOriginalBB;
|
|
}
|
|
|
|
return update;
|
|
}
|
|
else {
|
|
update |= pbvh_flush_bb(bvh, bvh->nodes + node->children_offset, flag);
|
|
update |= pbvh_flush_bb(bvh, bvh->nodes + node->children_offset + 1, flag);
|
|
|
|
if (update & PBVH_UpdateBB)
|
|
update_node_vb(bvh, node);
|
|
if (update & PBVH_UpdateOriginalBB)
|
|
node->orig_vb = node->vb;
|
|
}
|
|
|
|
return update;
|
|
}
|
|
|
|
void BKE_pbvh_update(PBVH *bvh, int flag, float (*face_nors)[3])
|
|
{
|
|
PBVHNode **nodes;
|
|
int totnode;
|
|
|
|
if (!bvh->nodes)
|
|
return;
|
|
|
|
BKE_pbvh_search_gather(bvh, update_search_cb, SET_INT_IN_POINTER(flag),
|
|
&nodes, &totnode);
|
|
|
|
if (flag & PBVH_UpdateNormals)
|
|
pbvh_update_normals(bvh, nodes, totnode, face_nors);
|
|
|
|
if (flag & (PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateRedraw))
|
|
pbvh_update_BB_redraw(bvh, nodes, totnode, flag);
|
|
|
|
if (flag & (PBVH_UpdateBB | PBVH_UpdateOriginalBB))
|
|
pbvh_flush_bb(bvh, bvh->nodes, flag);
|
|
|
|
if (nodes) MEM_freeN(nodes);
|
|
}
|
|
|
|
void BKE_pbvh_redraw_BB(PBVH *bvh, float bb_min[3], float bb_max[3])
|
|
{
|
|
PBVHIter iter;
|
|
PBVHNode *node;
|
|
BB bb;
|
|
|
|
BB_reset(&bb);
|
|
|
|
pbvh_iter_begin(&iter, bvh, NULL, NULL);
|
|
|
|
while ((node = pbvh_iter_next(&iter)))
|
|
if (node->flag & PBVH_UpdateRedraw)
|
|
BB_expand_with_bb(&bb, &node->vb);
|
|
|
|
pbvh_iter_end(&iter);
|
|
|
|
copy_v3_v3(bb_min, bb.bmin);
|
|
copy_v3_v3(bb_max, bb.bmax);
|
|
}
|
|
|
|
void BKE_pbvh_get_grid_updates(PBVH *bvh, int clear, void ***gridfaces, int *totface)
|
|
{
|
|
PBVHIter iter;
|
|
PBVHNode *node;
|
|
GHashIterator *hiter;
|
|
GHash *map;
|
|
void *face, **faces;
|
|
unsigned i;
|
|
int tot;
|
|
|
|
map = BLI_ghash_ptr_new("pbvh_get_grid_updates gh");
|
|
|
|
pbvh_iter_begin(&iter, bvh, NULL, NULL);
|
|
|
|
while ((node = pbvh_iter_next(&iter))) {
|
|
if (node->flag & PBVH_UpdateNormals) {
|
|
for (i = 0; i < node->totprim; ++i) {
|
|
face = bvh->gridfaces[node->prim_indices[i]];
|
|
if (!BLI_ghash_lookup(map, face))
|
|
BLI_ghash_insert(map, face, face);
|
|
}
|
|
|
|
if (clear)
|
|
node->flag &= ~PBVH_UpdateNormals;
|
|
}
|
|
}
|
|
|
|
pbvh_iter_end(&iter);
|
|
|
|
tot = BLI_ghash_size(map);
|
|
if (tot == 0) {
|
|
*totface = 0;
|
|
*gridfaces = NULL;
|
|
BLI_ghash_free(map, NULL, NULL);
|
|
return;
|
|
}
|
|
|
|
faces = MEM_callocN(sizeof(void *) * tot, "PBVH Grid Faces");
|
|
|
|
for (hiter = BLI_ghashIterator_new(map), i = 0;
|
|
BLI_ghashIterator_done(hiter) == false;
|
|
BLI_ghashIterator_step(hiter), ++i)
|
|
{
|
|
faces[i] = BLI_ghashIterator_getKey(hiter);
|
|
}
|
|
|
|
BLI_ghashIterator_free(hiter);
|
|
|
|
BLI_ghash_free(map, NULL, NULL);
|
|
|
|
*totface = tot;
|
|
*gridfaces = faces;
|
|
}
|
|
|
|
/***************************** PBVH Access ***********************************/
|
|
|
|
PBVHType BKE_pbvh_type(const PBVH *bvh)
|
|
{
|
|
return bvh->type;
|
|
}
|
|
|
|
void BKE_pbvh_bounding_box(const PBVH *bvh, float min[3], float max[3])
|
|
{
|
|
if (bvh->totnode) {
|
|
const BB *bb = &bvh->nodes[0].vb;
|
|
copy_v3_v3(min, bb->bmin);
|
|
copy_v3_v3(max, bb->bmax);
|
|
}
|
|
else {
|
|
zero_v3(min);
|
|
zero_v3(max);
|
|
}
|
|
}
|
|
|
|
BLI_bitmap **BKE_pbvh_grid_hidden(const PBVH *bvh)
|
|
{
|
|
BLI_assert(bvh->type == PBVH_GRIDS);
|
|
return bvh->grid_hidden;
|
|
}
|
|
|
|
void BKE_pbvh_get_grid_key(const PBVH *bvh, CCGKey *key)
|
|
{
|
|
BLI_assert(bvh->type == PBVH_GRIDS);
|
|
*key = bvh->gridkey;
|
|
}
|
|
|
|
BMesh *BKE_pbvh_get_bmesh(PBVH *bvh)
|
|
{
|
|
BLI_assert(bvh->type == PBVH_BMESH);
|
|
return bvh->bm;
|
|
}
|
|
|
|
/***************************** Node Access ***********************************/
|
|
|
|
void BKE_pbvh_node_mark_update(PBVHNode *node)
|
|
{
|
|
node->flag |= PBVH_UpdateNormals | PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw;
|
|
}
|
|
|
|
void BKE_pbvh_node_mark_rebuild_draw(PBVHNode *node)
|
|
{
|
|
node->flag |= PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw;
|
|
}
|
|
|
|
void BKE_pbvh_node_mark_redraw(PBVHNode *node)
|
|
{
|
|
node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw;
|
|
}
|
|
|
|
void BKE_pbvh_node_fully_hidden_set(PBVHNode *node, int fully_hidden)
|
|
{
|
|
BLI_assert(node->flag & PBVH_Leaf);
|
|
|
|
if (fully_hidden)
|
|
node->flag |= PBVH_FullyHidden;
|
|
else
|
|
node->flag &= ~PBVH_FullyHidden;
|
|
}
|
|
|
|
void BKE_pbvh_node_get_verts(PBVH *bvh, PBVHNode *node, int **vert_indices, MVert **verts)
|
|
{
|
|
if (vert_indices) *vert_indices = node->vert_indices;
|
|
if (verts) *verts = bvh->verts;
|
|
}
|
|
|
|
void BKE_pbvh_node_num_verts(PBVH *bvh, PBVHNode *node, int *uniquevert, int *totvert)
|
|
{
|
|
int tot;
|
|
|
|
switch (bvh->type) {
|
|
case PBVH_GRIDS:
|
|
tot = node->totprim * bvh->gridkey.grid_area;
|
|
if (totvert) *totvert = tot;
|
|
if (uniquevert) *uniquevert = tot;
|
|
break;
|
|
case PBVH_FACES:
|
|
if (totvert) *totvert = node->uniq_verts + node->face_verts;
|
|
if (uniquevert) *uniquevert = node->uniq_verts;
|
|
break;
|
|
case PBVH_BMESH:
|
|
tot = BLI_gset_size(node->bm_unique_verts);
|
|
if (totvert) *totvert = tot + BLI_gset_size(node->bm_other_verts);
|
|
if (uniquevert) *uniquevert = tot;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void BKE_pbvh_node_get_grids(PBVH *bvh, PBVHNode *node, int **grid_indices, int *totgrid, int *maxgrid, int *gridsize, CCGElem ***griddata, DMGridAdjacency **gridadj)
|
|
{
|
|
switch (bvh->type) {
|
|
case PBVH_GRIDS:
|
|
if (grid_indices) *grid_indices = node->prim_indices;
|
|
if (totgrid) *totgrid = node->totprim;
|
|
if (maxgrid) *maxgrid = bvh->totgrid;
|
|
if (gridsize) *gridsize = bvh->gridkey.grid_size;
|
|
if (griddata) *griddata = bvh->grids;
|
|
if (gridadj) *gridadj = bvh->gridadj;
|
|
break;
|
|
case PBVH_FACES:
|
|
case PBVH_BMESH:
|
|
if (grid_indices) *grid_indices = NULL;
|
|
if (totgrid) *totgrid = 0;
|
|
if (maxgrid) *maxgrid = 0;
|
|
if (gridsize) *gridsize = 0;
|
|
if (griddata) *griddata = NULL;
|
|
if (gridadj) *gridadj = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void BKE_pbvh_node_get_BB(PBVHNode *node, float bb_min[3], float bb_max[3])
|
|
{
|
|
copy_v3_v3(bb_min, node->vb.bmin);
|
|
copy_v3_v3(bb_max, node->vb.bmax);
|
|
}
|
|
|
|
void BKE_pbvh_node_get_original_BB(PBVHNode *node, float bb_min[3], float bb_max[3])
|
|
{
|
|
copy_v3_v3(bb_min, node->orig_vb.bmin);
|
|
copy_v3_v3(bb_max, node->orig_vb.bmax);
|
|
}
|
|
|
|
void BKE_pbvh_node_get_proxies(PBVHNode *node, PBVHProxyNode **proxies, int *proxy_count)
|
|
{
|
|
if (node->proxy_count > 0) {
|
|
if (proxies) *proxies = node->proxies;
|
|
if (proxy_count) *proxy_count = node->proxy_count;
|
|
}
|
|
else {
|
|
if (proxies) *proxies = NULL;
|
|
if (proxy_count) *proxy_count = 0;
|
|
}
|
|
}
|
|
|
|
/********************************* Raycast ***********************************/
|
|
|
|
typedef struct {
|
|
IsectRayAABBData ray;
|
|
int original;
|
|
} RaycastData;
|
|
|
|
static bool ray_aabb_intersect(PBVHNode *node, void *data_v)
|
|
{
|
|
RaycastData *rcd = data_v;
|
|
float bb_min[3], bb_max[3];
|
|
|
|
if (rcd->original)
|
|
BKE_pbvh_node_get_original_BB(node, bb_min, bb_max);
|
|
else
|
|
BKE_pbvh_node_get_BB(node, bb_min, bb_max);
|
|
|
|
return isect_ray_aabb(&rcd->ray, bb_min, bb_max, &node->tmin);
|
|
}
|
|
|
|
void BKE_pbvh_raycast(PBVH *bvh, BKE_pbvh_HitOccludedCallback cb, void *data,
|
|
const float ray_start[3], const float ray_normal[3],
|
|
int original)
|
|
{
|
|
RaycastData rcd;
|
|
|
|
isect_ray_aabb_initialize(&rcd.ray, ray_start, ray_normal);
|
|
rcd.original = original;
|
|
|
|
BKE_pbvh_search_callback_occluded(bvh, ray_aabb_intersect, &rcd, cb, data);
|
|
}
|
|
|
|
bool ray_face_intersection(const float ray_start[3],
|
|
const float ray_normal[3],
|
|
const float t0[3], const float t1[3],
|
|
const float t2[3], const float t3[3],
|
|
float *fdist)
|
|
{
|
|
float dist;
|
|
|
|
if ((isect_ray_tri_epsilon_v3(ray_start, ray_normal, t0, t1, t2, &dist, NULL, 0.1f) && dist < *fdist) ||
|
|
(t3 && isect_ray_tri_epsilon_v3(ray_start, ray_normal, t0, t2, t3, &dist, NULL, 0.1f) && dist < *fdist))
|
|
{
|
|
*fdist = dist;
|
|
return 1;
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static bool pbvh_faces_node_raycast(PBVH *bvh, const PBVHNode *node,
|
|
float (*origco)[3],
|
|
const float ray_start[3],
|
|
const float ray_normal[3], float *dist)
|
|
{
|
|
const MVert *vert = bvh->verts;
|
|
const int *faces = node->prim_indices;
|
|
int i, totface = node->totprim;
|
|
bool hit = false;
|
|
|
|
for (i = 0; i < totface; ++i) {
|
|
const MFace *f = bvh->faces + faces[i];
|
|
const int *face_verts = node->face_vert_indices[i];
|
|
|
|
if (paint_is_face_hidden(f, vert))
|
|
continue;
|
|
|
|
if (origco) {
|
|
/* intersect with backuped original coordinates */
|
|
hit |= ray_face_intersection(ray_start, ray_normal,
|
|
origco[face_verts[0]],
|
|
origco[face_verts[1]],
|
|
origco[face_verts[2]],
|
|
f->v4 ? origco[face_verts[3]] : NULL,
|
|
dist);
|
|
}
|
|
else {
|
|
/* intersect with current coordinates */
|
|
hit |= ray_face_intersection(ray_start, ray_normal,
|
|
vert[f->v1].co,
|
|
vert[f->v2].co,
|
|
vert[f->v3].co,
|
|
f->v4 ? vert[f->v4].co : NULL,
|
|
dist);
|
|
}
|
|
}
|
|
|
|
return hit;
|
|
}
|
|
|
|
static bool pbvh_grids_node_raycast(
|
|
PBVH *bvh, PBVHNode *node,
|
|
float (*origco)[3],
|
|
const float ray_start[3], const float ray_normal[3],
|
|
float *dist)
|
|
{
|
|
int totgrid = node->totprim;
|
|
int gridsize = bvh->gridkey.grid_size;
|
|
int i, x, y;
|
|
bool hit = false;
|
|
|
|
for (i = 0; i < totgrid; ++i) {
|
|
CCGElem *grid = bvh->grids[node->prim_indices[i]];
|
|
BLI_bitmap *gh;
|
|
|
|
if (!grid)
|
|
continue;
|
|
|
|
gh = bvh->grid_hidden[node->prim_indices[i]];
|
|
|
|
for (y = 0; y < gridsize - 1; ++y) {
|
|
for (x = 0; x < gridsize - 1; ++x) {
|
|
/* check if grid face is hidden */
|
|
if (gh) {
|
|
if (paint_is_grid_face_hidden(gh, gridsize, x, y))
|
|
continue;
|
|
}
|
|
|
|
if (origco) {
|
|
hit |= ray_face_intersection(ray_start, ray_normal,
|
|
origco[y * gridsize + x],
|
|
origco[y * gridsize + x + 1],
|
|
origco[(y + 1) * gridsize + x + 1],
|
|
origco[(y + 1) * gridsize + x],
|
|
dist);
|
|
}
|
|
else {
|
|
hit |= ray_face_intersection(ray_start, ray_normal,
|
|
CCG_grid_elem_co(&bvh->gridkey, grid, x, y),
|
|
CCG_grid_elem_co(&bvh->gridkey, grid, x + 1, y),
|
|
CCG_grid_elem_co(&bvh->gridkey, grid, x + 1, y + 1),
|
|
CCG_grid_elem_co(&bvh->gridkey, grid, x, y + 1),
|
|
dist);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (origco)
|
|
origco += gridsize * gridsize;
|
|
}
|
|
|
|
return hit;
|
|
}
|
|
|
|
bool BKE_pbvh_node_raycast(
|
|
PBVH *bvh, PBVHNode *node, float (*origco)[3], int use_origco,
|
|
const float ray_start[3], const float ray_normal[3],
|
|
float *dist)
|
|
{
|
|
bool hit = false;
|
|
|
|
if (node->flag & PBVH_FullyHidden)
|
|
return 0;
|
|
|
|
switch (bvh->type) {
|
|
case PBVH_FACES:
|
|
hit |= pbvh_faces_node_raycast(bvh, node, origco,
|
|
ray_start, ray_normal, dist);
|
|
break;
|
|
case PBVH_GRIDS:
|
|
hit |= pbvh_grids_node_raycast(bvh, node, origco,
|
|
ray_start, ray_normal, dist);
|
|
break;
|
|
case PBVH_BMESH:
|
|
hit = pbvh_bmesh_node_raycast(node, ray_start, ray_normal, dist, use_origco);
|
|
break;
|
|
}
|
|
|
|
return hit;
|
|
}
|
|
|
|
void BKE_pbvh_raycast_project_ray_root (PBVH *bvh, bool original, float ray_start[3], float ray_end[3], float ray_normal[3])
|
|
{
|
|
if (bvh->nodes) {
|
|
float rootmin_start, rootmin_end;
|
|
float bb_min_root[3], bb_max_root[3], bb_center[3], bb_diff[3];
|
|
IsectRayAABBData ray;
|
|
float ray_normal_inv[3];
|
|
float offset = 1.0f + 1e-3f;
|
|
float offset_vec[3] = {1e-3f, 1e-3f, 1e-3f};
|
|
|
|
if (original)
|
|
BKE_pbvh_node_get_original_BB(bvh->nodes, bb_min_root, bb_max_root);
|
|
else
|
|
BKE_pbvh_node_get_BB(bvh->nodes, bb_min_root, bb_max_root);
|
|
|
|
/* slightly offset min and max in case we have a zero width node (due to a plane mesh for instance),
|
|
* or faces very close to the bounding box boundary. */
|
|
mid_v3_v3v3(bb_center, bb_max_root, bb_min_root);
|
|
/* diff should be same for both min/max since it's calculated from center */
|
|
sub_v3_v3v3(bb_diff, bb_max_root, bb_center);
|
|
/* handles case of zero width bb */
|
|
add_v3_v3(bb_diff, offset_vec);
|
|
madd_v3_v3v3fl(bb_max_root, bb_center, bb_diff, offset);
|
|
madd_v3_v3v3fl(bb_min_root, bb_center, bb_diff, -offset);
|
|
|
|
/* first project start ray */
|
|
isect_ray_aabb_initialize(&ray, ray_start, ray_normal);
|
|
if (!isect_ray_aabb(&ray, bb_min_root, bb_max_root, &rootmin_start))
|
|
return;
|
|
|
|
/* then the end ray */
|
|
mul_v3_v3fl(ray_normal_inv, ray_normal, -1.0);
|
|
isect_ray_aabb_initialize(&ray, ray_end, ray_normal_inv);
|
|
/* unlikely to fail exiting if entering succeeded, still keep this here */
|
|
if (!isect_ray_aabb(&ray, bb_min_root, bb_max_root, &rootmin_end))
|
|
return;
|
|
|
|
madd_v3_v3v3fl(ray_start, ray_start, ray_normal, rootmin_start);
|
|
madd_v3_v3v3fl(ray_end, ray_end, ray_normal_inv, rootmin_end);
|
|
}
|
|
}
|
|
|
|
|
|
//#include <GL/glew.h>
|
|
|
|
typedef struct {
|
|
DMSetMaterial setMaterial;
|
|
bool wireframe;
|
|
} PBVHNodeDrawData;
|
|
|
|
void BKE_pbvh_node_draw(PBVHNode *node, void *data_v)
|
|
{
|
|
PBVHNodeDrawData *data = data_v;
|
|
|
|
#if 0
|
|
/* XXX: Just some quick code to show leaf nodes in different colors */
|
|
float col[3]; int i;
|
|
|
|
if (0) { //is_partial) {
|
|
col[0] = (rand() / (float)RAND_MAX); col[1] = col[2] = 0.6;
|
|
}
|
|
else {
|
|
srand((long long)node);
|
|
for (i = 0; i < 3; ++i)
|
|
col[i] = (rand() / (float)RAND_MAX) * 0.3 + 0.7;
|
|
}
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, col);
|
|
|
|
glColor3f(1, 0, 0);
|
|
#endif
|
|
|
|
if (!(node->flag & PBVH_FullyHidden)) {
|
|
GPU_draw_pbvh_buffers(node->draw_buffers,
|
|
data->setMaterial,
|
|
data->wireframe);
|
|
}
|
|
}
|
|
|
|
typedef enum {
|
|
ISECT_INSIDE,
|
|
ISECT_OUTSIDE,
|
|
ISECT_INTERSECT
|
|
} PlaneAABBIsect;
|
|
|
|
/* Adapted from:
|
|
* http://www.gamedev.net/community/forums/topic.asp?topic_id=512123
|
|
* Returns true if the AABB is at least partially within the frustum
|
|
* (ok, not a real frustum), false otherwise.
|
|
*/
|
|
static PlaneAABBIsect test_planes_aabb(const float bb_min[3],
|
|
const float bb_max[3],
|
|
const float (*planes)[4])
|
|
{
|
|
float vmin[3], vmax[3];
|
|
PlaneAABBIsect ret = ISECT_INSIDE;
|
|
int i, axis;
|
|
|
|
for (i = 0; i < 4; ++i) {
|
|
for (axis = 0; axis < 3; ++axis) {
|
|
if (planes[i][axis] > 0) {
|
|
vmin[axis] = bb_min[axis];
|
|
vmax[axis] = bb_max[axis];
|
|
}
|
|
else {
|
|
vmin[axis] = bb_max[axis];
|
|
vmax[axis] = bb_min[axis];
|
|
}
|
|
}
|
|
|
|
if (dot_v3v3(planes[i], vmin) + planes[i][3] > 0)
|
|
return ISECT_OUTSIDE;
|
|
else if (dot_v3v3(planes[i], vmax) + planes[i][3] >= 0)
|
|
ret = ISECT_INTERSECT;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool BKE_pbvh_node_planes_contain_AABB(PBVHNode *node, void *data)
|
|
{
|
|
float bb_min[3], bb_max[3];
|
|
|
|
BKE_pbvh_node_get_BB(node, bb_min, bb_max);
|
|
return test_planes_aabb(bb_min, bb_max, data) != ISECT_OUTSIDE;
|
|
}
|
|
|
|
bool BKE_pbvh_node_planes_exclude_AABB(PBVHNode *node, void *data)
|
|
{
|
|
float bb_min[3], bb_max[3];
|
|
|
|
BKE_pbvh_node_get_BB(node, bb_min, bb_max);
|
|
return test_planes_aabb(bb_min, bb_max, data) != ISECT_INSIDE;
|
|
}
|
|
|
|
static void pbvh_node_check_diffuse_changed(PBVH *bvh, PBVHNode *node)
|
|
{
|
|
if (!node->draw_buffers)
|
|
return;
|
|
|
|
if (GPU_pbvh_buffers_diffuse_changed(node->draw_buffers, node->bm_faces, bvh->show_diffuse_color))
|
|
node->flag |= PBVH_UpdateDrawBuffers;
|
|
}
|
|
|
|
void BKE_pbvh_draw(PBVH *bvh, float (*planes)[4], float (*face_nors)[3],
|
|
DMSetMaterial setMaterial, bool wireframe)
|
|
{
|
|
PBVHNodeDrawData draw_data = {setMaterial, wireframe};
|
|
PBVHNode **nodes;
|
|
int a, totnode;
|
|
|
|
for (a = 0; a < bvh->totnode; a++)
|
|
pbvh_node_check_diffuse_changed(bvh, &bvh->nodes[a]);
|
|
|
|
BKE_pbvh_search_gather(bvh, update_search_cb, SET_INT_IN_POINTER(PBVH_UpdateNormals | PBVH_UpdateDrawBuffers),
|
|
&nodes, &totnode);
|
|
|
|
pbvh_update_normals(bvh, nodes, totnode, face_nors);
|
|
pbvh_update_draw_buffers(bvh, nodes, totnode);
|
|
|
|
if (nodes) MEM_freeN(nodes);
|
|
|
|
if (planes) {
|
|
BKE_pbvh_search_callback(bvh, BKE_pbvh_node_planes_contain_AABB,
|
|
planes, BKE_pbvh_node_draw, &draw_data);
|
|
}
|
|
else {
|
|
BKE_pbvh_search_callback(bvh, NULL, NULL, BKE_pbvh_node_draw, &draw_data);
|
|
}
|
|
}
|
|
|
|
void BKE_pbvh_grids_update(PBVH *bvh, CCGElem **grids, DMGridAdjacency *gridadj, void **gridfaces,
|
|
DMFlagMat *flagmats, BLI_bitmap **grid_hidden)
|
|
{
|
|
int a;
|
|
|
|
bvh->grids = grids;
|
|
bvh->gridadj = gridadj;
|
|
bvh->gridfaces = gridfaces;
|
|
|
|
if (flagmats != bvh->grid_flag_mats || bvh->grid_hidden != grid_hidden) {
|
|
bvh->grid_flag_mats = flagmats;
|
|
bvh->grid_hidden = grid_hidden;
|
|
|
|
for (a = 0; a < bvh->totnode; ++a)
|
|
BKE_pbvh_node_mark_rebuild_draw(&bvh->nodes[a]);
|
|
}
|
|
}
|
|
|
|
/* Get the node's displacement layer, creating it if necessary */
|
|
float *BKE_pbvh_node_layer_disp_get(PBVH *bvh, PBVHNode *node)
|
|
{
|
|
if (!node->layer_disp) {
|
|
int totvert = 0;
|
|
BKE_pbvh_node_num_verts(bvh, node, &totvert, NULL);
|
|
node->layer_disp = MEM_callocN(sizeof(float) * totvert, "layer disp");
|
|
}
|
|
return node->layer_disp;
|
|
}
|
|
|
|
/* If the node has a displacement layer, free it and set to null */
|
|
void BKE_pbvh_node_layer_disp_free(PBVHNode *node)
|
|
{
|
|
if (node->layer_disp) {
|
|
MEM_freeN(node->layer_disp);
|
|
node->layer_disp = NULL;
|
|
}
|
|
}
|
|
|
|
float (*BKE_pbvh_get_vertCos(PBVH *pbvh))[3]
|
|
{
|
|
int a;
|
|
float (*vertCos)[3] = NULL;
|
|
|
|
if (pbvh->verts) {
|
|
float *co;
|
|
MVert *mvert = pbvh->verts;
|
|
|
|
vertCos = MEM_callocN(3 * pbvh->totvert * sizeof(float), "BKE_pbvh_get_vertCoords");
|
|
co = (float *)vertCos;
|
|
|
|
for (a = 0; a < pbvh->totvert; a++, mvert++, co += 3) {
|
|
copy_v3_v3(co, mvert->co);
|
|
}
|
|
}
|
|
|
|
return vertCos;
|
|
}
|
|
|
|
void BKE_pbvh_apply_vertCos(PBVH *pbvh, float (*vertCos)[3])
|
|
{
|
|
int a;
|
|
|
|
if (!pbvh->deformed) {
|
|
if (pbvh->verts) {
|
|
/* if pbvh is not already deformed, verts/faces points to the */
|
|
/* original data and applying new coords to this arrays would lead to */
|
|
/* unneeded deformation -- duplicate verts/faces to avoid this */
|
|
|
|
pbvh->verts = MEM_dupallocN(pbvh->verts);
|
|
pbvh->faces = MEM_dupallocN(pbvh->faces);
|
|
|
|
pbvh->deformed = 1;
|
|
}
|
|
}
|
|
|
|
if (pbvh->verts) {
|
|
MVert *mvert = pbvh->verts;
|
|
/* copy new verts coords */
|
|
for (a = 0; a < pbvh->totvert; ++a, ++mvert) {
|
|
copy_v3_v3(mvert->co, vertCos[a]);
|
|
mvert->flag |= ME_VERT_PBVH_UPDATE;
|
|
}
|
|
|
|
/* coordinates are new -- normals should also be updated */
|
|
BKE_mesh_calc_normals_tessface(pbvh->verts, pbvh->totvert, pbvh->faces, pbvh->totprim, NULL);
|
|
|
|
for (a = 0; a < pbvh->totnode; ++a)
|
|
BKE_pbvh_node_mark_update(&pbvh->nodes[a]);
|
|
|
|
BKE_pbvh_update(pbvh, PBVH_UpdateBB, NULL);
|
|
BKE_pbvh_update(pbvh, PBVH_UpdateOriginalBB, NULL);
|
|
|
|
}
|
|
}
|
|
|
|
bool BKE_pbvh_isDeformed(PBVH *pbvh)
|
|
{
|
|
return pbvh->deformed;
|
|
}
|
|
/* Proxies */
|
|
|
|
PBVHProxyNode *BKE_pbvh_node_add_proxy(PBVH *bvh, PBVHNode *node)
|
|
{
|
|
int index, totverts;
|
|
|
|
#pragma omp critical
|
|
{
|
|
|
|
index = node->proxy_count;
|
|
|
|
node->proxy_count++;
|
|
|
|
if (node->proxies)
|
|
node->proxies = MEM_reallocN(node->proxies, node->proxy_count * sizeof(PBVHProxyNode));
|
|
else
|
|
node->proxies = MEM_mallocN(sizeof(PBVHProxyNode), "PBVHNodeProxy");
|
|
|
|
BKE_pbvh_node_num_verts(bvh, node, &totverts, NULL);
|
|
node->proxies[index].co = MEM_callocN(sizeof(float[3]) * totverts, "PBVHNodeProxy.co");
|
|
}
|
|
|
|
return node->proxies + index;
|
|
}
|
|
|
|
void BKE_pbvh_node_free_proxies(PBVHNode *node)
|
|
{
|
|
#pragma omp critical
|
|
{
|
|
int p;
|
|
|
|
for (p = 0; p < node->proxy_count; p++) {
|
|
MEM_freeN(node->proxies[p].co);
|
|
node->proxies[p].co = NULL;
|
|
}
|
|
|
|
MEM_freeN(node->proxies);
|
|
node->proxies = NULL;
|
|
|
|
node->proxy_count = 0;
|
|
}
|
|
}
|
|
|
|
void BKE_pbvh_gather_proxies(PBVH *pbvh, PBVHNode ***r_array, int *r_tot)
|
|
{
|
|
PBVHNode **array = NULL, *node;
|
|
int tot = 0, space = 0;
|
|
int n;
|
|
|
|
for (n = 0; n < pbvh->totnode; n++) {
|
|
node = pbvh->nodes + n;
|
|
|
|
if (node->proxy_count > 0) {
|
|
if (tot == space) {
|
|
/* resize array if needed */
|
|
space = (tot == 0) ? 32 : space * 2;
|
|
array = MEM_recallocN_id(array, sizeof(PBVHNode *) * space, __func__);
|
|
}
|
|
|
|
array[tot] = node;
|
|
tot++;
|
|
}
|
|
}
|
|
|
|
if (tot == 0 && array) {
|
|
MEM_freeN(array);
|
|
array = NULL;
|
|
}
|
|
|
|
*r_array = array;
|
|
*r_tot = tot;
|
|
}
|
|
|
|
void pbvh_vertex_iter_init(PBVH *bvh, PBVHNode *node,
|
|
PBVHVertexIter *vi, int mode)
|
|
{
|
|
struct CCGElem **grids;
|
|
struct MVert *verts;
|
|
int *grid_indices, *vert_indices;
|
|
int totgrid, gridsize, uniq_verts, totvert;
|
|
|
|
vi->grid = NULL;
|
|
vi->no = NULL;
|
|
vi->fno = NULL;
|
|
vi->mvert = NULL;
|
|
|
|
BKE_pbvh_node_get_grids(bvh, node, &grid_indices, &totgrid, NULL, &gridsize, &grids, NULL);
|
|
BKE_pbvh_node_num_verts(bvh, node, &uniq_verts, &totvert);
|
|
BKE_pbvh_node_get_verts(bvh, node, &vert_indices, &verts);
|
|
vi->key = &bvh->gridkey;
|
|
|
|
vi->grids = grids;
|
|
vi->grid_indices = grid_indices;
|
|
vi->totgrid = (grids) ? totgrid : 1;
|
|
vi->gridsize = gridsize;
|
|
|
|
if (mode == PBVH_ITER_ALL)
|
|
vi->totvert = totvert;
|
|
else
|
|
vi->totvert = uniq_verts;
|
|
vi->vert_indices = vert_indices;
|
|
vi->mverts = verts;
|
|
|
|
if (bvh->type == PBVH_BMESH) {
|
|
BLI_gsetIterator_init(&vi->bm_unique_verts, node->bm_unique_verts);
|
|
BLI_gsetIterator_init(&vi->bm_other_verts, node->bm_other_verts);
|
|
vi->bm_vdata = &bvh->bm->vdata;
|
|
vi->cd_vert_mask_offset = CustomData_get_offset(vi->bm_vdata, CD_PAINT_MASK);
|
|
}
|
|
|
|
vi->gh = NULL;
|
|
if (vi->grids && mode == PBVH_ITER_UNIQUE)
|
|
vi->grid_hidden = bvh->grid_hidden;
|
|
|
|
vi->mask = NULL;
|
|
if (bvh->type == PBVH_FACES)
|
|
vi->vmask = CustomData_get_layer(bvh->vdata, CD_PAINT_MASK);
|
|
}
|
|
|
|
void pbvh_show_diffuse_color_set(PBVH *bvh, bool show_diffuse_color)
|
|
{
|
|
bvh->show_diffuse_color = show_diffuse_color;
|
|
}
|