We have to discard the batch in smooth case, because we are modifying the index buffer (flat shading don't need it, only changes vertex buffer on redraw, which is safe). Many thanks to @fclem for his help on debuging/understanding what was wrong here!
1101 lines
32 KiB
C
1101 lines
32 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.
|
|
*
|
|
* The Original Code is Copyright (C) 2005 Blender Foundation.
|
|
* All rights reserved.
|
|
*
|
|
* The Original Code is: all of this file.
|
|
*
|
|
* Contributor(s): Brecht Van Lommel.
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
/** \file blender/gpu/intern/gpu_buffers.c
|
|
* \ingroup gpu
|
|
*
|
|
* Mesh drawing using OpenGL VBO (Vertex Buffer Objects)
|
|
*/
|
|
|
|
#include <limits.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_bitmap.h"
|
|
#include "BLI_math.h"
|
|
#include "BLI_utildefines.h"
|
|
#include "BLI_ghash.h"
|
|
#include "BLI_threads.h"
|
|
|
|
#include "DNA_meshdata_types.h"
|
|
|
|
#include "BKE_ccg.h"
|
|
#include "BKE_DerivedMesh.h"
|
|
#include "BKE_paint.h"
|
|
#include "BKE_mesh.h"
|
|
#include "BKE_pbvh.h"
|
|
|
|
#include "GPU_buffers.h"
|
|
#include "GPU_draw.h"
|
|
#include "GPU_immediate.h"
|
|
#include "GPU_batch.h"
|
|
|
|
#include "bmesh.h"
|
|
|
|
static ThreadMutex buffer_mutex = BLI_MUTEX_INITIALIZER;
|
|
|
|
/* multires global buffer, can be used for many grids having the same grid size */
|
|
typedef struct GridCommonGPUBuffer {
|
|
Gwn_IndexBuf *mres_buffer;
|
|
int mres_prev_gridsize;
|
|
unsigned mres_prev_totquad;
|
|
} GridCommonGPUBuffer;
|
|
|
|
/* XXX: the rest of the code in this file is used for optimized PBVH
|
|
* drawing and doesn't interact at all with the buffer code above */
|
|
|
|
struct GPU_PBVH_Buffers {
|
|
Gwn_IndexBuf *index_buf, *index_buf_fast;
|
|
Gwn_VertBuf *vert_buf;
|
|
|
|
Gwn_Batch *triangles;
|
|
Gwn_Batch *triangles_fast;
|
|
|
|
/* mesh pointers in case buffer allocation fails */
|
|
const MPoly *mpoly;
|
|
const MLoop *mloop;
|
|
const MLoopTri *looptri;
|
|
const MVert *mvert;
|
|
|
|
const int *face_indices;
|
|
int face_indices_len;
|
|
const float *vmask;
|
|
|
|
/* grid pointers */
|
|
CCGKey gridkey;
|
|
CCGElem **grids;
|
|
const DMFlagMat *grid_flag_mats;
|
|
BLI_bitmap * const *grid_hidden;
|
|
const int *grid_indices;
|
|
int totgrid;
|
|
bool has_hidden;
|
|
bool is_index_buf_global; /* Means index_buf uses global bvh's grid_common_gpu_buffer, **DO NOT** free it! */
|
|
|
|
bool use_bmesh;
|
|
|
|
unsigned int tot_tri, tot_quad;
|
|
|
|
/* The PBVH ensures that either all faces in the node are
|
|
* smooth-shaded or all faces are flat-shaded */
|
|
bool smooth;
|
|
|
|
bool show_diffuse_color;
|
|
bool show_mask;
|
|
|
|
float diffuse_color[4];
|
|
};
|
|
|
|
static struct {
|
|
uint pos, nor, col;
|
|
} g_vbo_id = {0};
|
|
|
|
static void gpu_material_diffuse_get(int UNUSED(nr), float diff[4])
|
|
{
|
|
/* TODO: sculpt diffuse color option not supported in 2.8 yet. */
|
|
diff[0] = 0.8f;
|
|
diff[1] = 0.8f;
|
|
diff[2] = 0.8f;
|
|
diff[3] = 1.0f;
|
|
}
|
|
|
|
/* Allocates a non-initialized buffer to be sent to GPU.
|
|
* Return is false it indicates that the memory map failed. */
|
|
static bool gpu_pbvh_vert_buf_data_set(GPU_PBVH_Buffers *buffers, unsigned int vert_ct)
|
|
{
|
|
if (buffers->vert_buf == NULL) {
|
|
/* Initialize vertex buffer */
|
|
/* match 'VertexBufferFormat' */
|
|
|
|
static Gwn_VertFormat format = {0};
|
|
if (format.attrib_ct == 0) {
|
|
g_vbo_id.pos = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
|
|
g_vbo_id.nor = GWN_vertformat_attr_add(&format, "nor", GWN_COMP_I16, 3, GWN_FETCH_INT_TO_FLOAT_UNIT);
|
|
g_vbo_id.col = GWN_vertformat_attr_add(&format, "color", GWN_COMP_U8, 3, GWN_FETCH_INT_TO_FLOAT_UNIT);
|
|
}
|
|
#if 0
|
|
buffers->vert_buf = GWN_vertbuf_create_with_format_ex(&format, GWN_USAGE_DYNAMIC);
|
|
GWN_vertbuf_data_alloc(buffers->vert_buf, vert_ct);
|
|
}
|
|
else if (vert_ct != buffers->vert_buf->vertex_ct) {
|
|
GWN_vertbuf_data_resize(buffers->vert_buf, vert_ct);
|
|
}
|
|
#else
|
|
buffers->vert_buf = GWN_vertbuf_create_with_format_ex(&format, GWN_USAGE_STATIC);
|
|
}
|
|
GWN_vertbuf_data_alloc(buffers->vert_buf, vert_ct);
|
|
#endif
|
|
return buffers->vert_buf->data != NULL;
|
|
}
|
|
|
|
static void gpu_pbvh_batch_init(GPU_PBVH_Buffers *buffers)
|
|
{
|
|
/* force flushing to the GPU */
|
|
if (buffers->vert_buf->data) {
|
|
GWN_vertbuf_use(buffers->vert_buf);
|
|
}
|
|
|
|
if (buffers->triangles == NULL) {
|
|
buffers->triangles = GWN_batch_create(
|
|
GWN_PRIM_TRIS, buffers->vert_buf,
|
|
/* can be NULL */
|
|
buffers->index_buf);
|
|
}
|
|
|
|
if ((buffers->triangles_fast == NULL) && buffers->index_buf_fast) {
|
|
buffers->triangles_fast = GWN_batch_create(
|
|
GWN_PRIM_TRIS, buffers->vert_buf,
|
|
/* can be NULL */
|
|
buffers->index_buf_fast);
|
|
}
|
|
}
|
|
|
|
static float gpu_color_from_mask(float mask)
|
|
{
|
|
return 1.0f - mask * 0.75f;
|
|
}
|
|
|
|
static void gpu_color_from_mask_copy(float mask, const float diffuse_color[4], unsigned char out[3])
|
|
{
|
|
float mask_color;
|
|
|
|
mask_color = gpu_color_from_mask(mask) * 255.0f;
|
|
|
|
out[0] = diffuse_color[0] * mask_color;
|
|
out[1] = diffuse_color[1] * mask_color;
|
|
out[2] = diffuse_color[2] * mask_color;
|
|
}
|
|
|
|
static void gpu_color_from_mask_quad_copy(const CCGKey *key,
|
|
CCGElem *a, CCGElem *b,
|
|
CCGElem *c, CCGElem *d,
|
|
const float *diffuse_color,
|
|
unsigned char out[3])
|
|
{
|
|
float mask_color =
|
|
gpu_color_from_mask((*CCG_elem_mask(key, a) +
|
|
*CCG_elem_mask(key, b) +
|
|
*CCG_elem_mask(key, c) +
|
|
*CCG_elem_mask(key, d)) * 0.25f) * 255.0f;
|
|
|
|
out[0] = diffuse_color[0] * mask_color;
|
|
out[1] = diffuse_color[1] * mask_color;
|
|
out[2] = diffuse_color[2] * mask_color;
|
|
}
|
|
|
|
void GPU_pbvh_mesh_buffers_update(
|
|
GPU_PBVH_Buffers *buffers, const MVert *mvert,
|
|
const int *vert_indices, int totvert, const float *vmask,
|
|
const int (*face_vert_indices)[3],
|
|
const int update_flags)
|
|
{
|
|
const bool show_diffuse_color = (update_flags & GPU_PBVH_BUFFERS_SHOW_DIFFUSE_COLOR) != 0;
|
|
const bool show_mask = (update_flags & GPU_PBVH_BUFFERS_SHOW_MASK) != 0;
|
|
|
|
buffers->vmask = vmask;
|
|
buffers->show_diffuse_color = show_diffuse_color;
|
|
buffers->show_mask = show_mask;
|
|
|
|
{
|
|
int totelem = (buffers->smooth ? totvert : (buffers->tot_tri * 3));
|
|
float diffuse_color[4] = {0.8f, 0.8f, 0.8f, 0.8f};
|
|
|
|
if (show_diffuse_color) {
|
|
const MLoopTri *lt = &buffers->looptri[buffers->face_indices[0]];
|
|
const MPoly *mp = &buffers->mpoly[lt->poly];
|
|
|
|
gpu_material_diffuse_get(mp->mat_nr + 1, diffuse_color);
|
|
}
|
|
|
|
copy_v4_v4(buffers->diffuse_color, diffuse_color);
|
|
|
|
uchar diffuse_color_ub[4];
|
|
rgba_float_to_uchar(diffuse_color_ub, diffuse_color);
|
|
|
|
/* Build VBO */
|
|
if (gpu_pbvh_vert_buf_data_set(buffers, totelem)) {
|
|
/* Vertex data is shared if smooth-shaded, but separate
|
|
* copies are made for flat shading because normals
|
|
* shouldn't be shared. */
|
|
if (buffers->smooth) {
|
|
for (uint i = 0; i < totvert; ++i) {
|
|
const MVert *v = &mvert[vert_indices[i]];
|
|
GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.pos, i, v->co);
|
|
GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.nor, i, v->no);
|
|
}
|
|
|
|
for (uint i = 0; i < buffers->face_indices_len; i++) {
|
|
const MLoopTri *lt = &buffers->looptri[buffers->face_indices[i]];
|
|
for (uint j = 0; j < 3; j++) {
|
|
int vidx = face_vert_indices[i][j];
|
|
if (vmask && show_mask) {
|
|
int v_index = buffers->mloop[lt->tri[j]].v;
|
|
uchar color_ub[3];
|
|
gpu_color_from_mask_copy(vmask[v_index], diffuse_color, color_ub);
|
|
GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vidx, color_ub);
|
|
}
|
|
else {
|
|
GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vidx, diffuse_color_ub);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
/* calculate normal for each polygon only once */
|
|
unsigned int mpoly_prev = UINT_MAX;
|
|
short no[3];
|
|
int vbo_index = 0;
|
|
|
|
for (uint i = 0; i < buffers->face_indices_len; i++) {
|
|
const MLoopTri *lt = &buffers->looptri[buffers->face_indices[i]];
|
|
const unsigned int vtri[3] = {
|
|
buffers->mloop[lt->tri[0]].v,
|
|
buffers->mloop[lt->tri[1]].v,
|
|
buffers->mloop[lt->tri[2]].v,
|
|
};
|
|
|
|
if (paint_is_face_hidden(lt, mvert, buffers->mloop))
|
|
continue;
|
|
|
|
/* Face normal and mask */
|
|
if (lt->poly != mpoly_prev) {
|
|
const MPoly *mp = &buffers->mpoly[lt->poly];
|
|
float fno[3];
|
|
BKE_mesh_calc_poly_normal(mp, &buffers->mloop[mp->loopstart], mvert, fno);
|
|
normal_float_to_short_v3(no, fno);
|
|
mpoly_prev = lt->poly;
|
|
}
|
|
|
|
uchar color_ub[3];
|
|
if (vmask && show_mask) {
|
|
float fmask = (vmask[vtri[0]] + vmask[vtri[1]] + vmask[vtri[2]]) / 3.0f;
|
|
gpu_color_from_mask_copy(fmask, diffuse_color, color_ub);
|
|
}
|
|
else {
|
|
copy_v3_v3_uchar(color_ub, diffuse_color_ub);
|
|
}
|
|
|
|
for (uint j = 0; j < 3; j++) {
|
|
const MVert *v = &mvert[vtri[j]];
|
|
|
|
GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.pos, vbo_index, v->co);
|
|
GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.nor, vbo_index, no);
|
|
GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vbo_index, color_ub);
|
|
|
|
vbo_index++;
|
|
}
|
|
}
|
|
}
|
|
|
|
gpu_pbvh_batch_init(buffers);
|
|
}
|
|
}
|
|
|
|
buffers->mvert = mvert;
|
|
}
|
|
|
|
GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(
|
|
const int (*face_vert_indices)[3],
|
|
const MPoly *mpoly, const MLoop *mloop, const MLoopTri *looptri,
|
|
const MVert *mvert,
|
|
const int *face_indices,
|
|
const int face_indices_len)
|
|
{
|
|
GPU_PBVH_Buffers *buffers;
|
|
int i, tottri;
|
|
|
|
buffers = MEM_callocN(sizeof(GPU_PBVH_Buffers), "GPU_Buffers");
|
|
|
|
/* smooth or flat for all */
|
|
#if 0
|
|
buffers->smooth = mpoly[looptri[face_indices[0]].poly].flag & ME_SMOOTH;
|
|
#else
|
|
/* for DrawManager we dont support mixed smooth/flat */
|
|
buffers->smooth = (mpoly[0].flag & ME_SMOOTH) != 0;
|
|
#endif
|
|
|
|
buffers->show_diffuse_color = false;
|
|
buffers->show_mask = true;
|
|
|
|
/* Count the number of visible triangles */
|
|
for (i = 0, tottri = 0; i < face_indices_len; ++i) {
|
|
const MLoopTri *lt = &looptri[face_indices[i]];
|
|
if (!paint_is_face_hidden(lt, mvert, mloop))
|
|
tottri++;
|
|
}
|
|
|
|
if (tottri == 0) {
|
|
buffers->tot_tri = 0;
|
|
|
|
buffers->mpoly = mpoly;
|
|
buffers->mloop = mloop;
|
|
buffers->looptri = looptri;
|
|
buffers->face_indices = face_indices;
|
|
buffers->face_indices_len = 0;
|
|
|
|
return buffers;
|
|
}
|
|
|
|
/* An element index buffer is used for smooth shading, but flat
|
|
* shading requires separate vertex normals so an index buffer is
|
|
* can't be used there. */
|
|
if (buffers->smooth) {
|
|
/* Fill the triangle buffer */
|
|
buffers->index_buf = NULL;
|
|
Gwn_IndexBufBuilder elb;
|
|
GWN_indexbuf_init(&elb, GWN_PRIM_TRIS, tottri, INT_MAX);
|
|
|
|
for (i = 0; i < face_indices_len; ++i) {
|
|
const MLoopTri *lt = &looptri[face_indices[i]];
|
|
|
|
/* Skip hidden faces */
|
|
if (paint_is_face_hidden(lt, mvert, mloop))
|
|
continue;
|
|
|
|
GWN_indexbuf_add_tri_verts(&elb, UNPACK3(face_vert_indices[i]));
|
|
}
|
|
buffers->index_buf = GWN_indexbuf_build(&elb);
|
|
}
|
|
else {
|
|
if (!buffers->is_index_buf_global) {
|
|
GWN_INDEXBUF_DISCARD_SAFE(buffers->index_buf);
|
|
}
|
|
buffers->index_buf = NULL;
|
|
buffers->is_index_buf_global = false;
|
|
}
|
|
|
|
buffers->tot_tri = tottri;
|
|
|
|
buffers->mpoly = mpoly;
|
|
buffers->mloop = mloop;
|
|
buffers->looptri = looptri;
|
|
|
|
buffers->face_indices = face_indices;
|
|
buffers->face_indices_len = face_indices_len;
|
|
|
|
return buffers;
|
|
}
|
|
|
|
void GPU_pbvh_grid_buffers_update(
|
|
GPU_PBVH_Buffers *buffers, CCGElem **grids,
|
|
const DMFlagMat *grid_flag_mats, int *grid_indices,
|
|
int totgrid, const CCGKey *key,
|
|
const int update_flags)
|
|
{
|
|
const bool show_diffuse_color = (update_flags & GPU_PBVH_BUFFERS_SHOW_DIFFUSE_COLOR) != 0;
|
|
const bool show_mask = (update_flags & GPU_PBVH_BUFFERS_SHOW_MASK) != 0;
|
|
int i, j, k, x, y;
|
|
|
|
buffers->show_diffuse_color = show_diffuse_color;
|
|
buffers->show_mask = show_mask;
|
|
buffers->smooth = grid_flag_mats[grid_indices[0]].flag & ME_SMOOTH;
|
|
|
|
/* Build VBO */
|
|
if (buffers->index_buf) {
|
|
const int has_mask = key->has_mask;
|
|
float diffuse_color[4] = {0.8f, 0.8f, 0.8f, 1.0f};
|
|
|
|
if (show_diffuse_color) {
|
|
const DMFlagMat *flags = &grid_flag_mats[grid_indices[0]];
|
|
|
|
gpu_material_diffuse_get(flags->mat_nr + 1, diffuse_color);
|
|
}
|
|
|
|
copy_v4_v4(buffers->diffuse_color, diffuse_color);
|
|
|
|
uint vbo_index_offset = 0;
|
|
/* Build VBO */
|
|
if (gpu_pbvh_vert_buf_data_set(buffers, totgrid * key->grid_area)) {
|
|
for (i = 0; i < totgrid; ++i) {
|
|
CCGElem *grid = grids[grid_indices[i]];
|
|
int vbo_index = vbo_index_offset;
|
|
|
|
for (y = 0; y < key->grid_size; y++) {
|
|
for (x = 0; x < key->grid_size; x++) {
|
|
CCGElem *elem = CCG_grid_elem(key, grid, x, y);
|
|
GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.pos, vbo_index, CCG_elem_co(key, elem));
|
|
|
|
if (buffers->smooth) {
|
|
short no_short[3];
|
|
normal_float_to_short_v3(no_short, CCG_elem_no(key, elem));
|
|
GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.nor, vbo_index, no_short);
|
|
|
|
if (has_mask) {
|
|
uchar color_ub[3];
|
|
if (show_mask) {
|
|
gpu_color_from_mask_copy(*CCG_elem_mask(key, elem),
|
|
diffuse_color, color_ub);
|
|
}
|
|
else {
|
|
unit_float_to_uchar_clamp_v3(color_ub, diffuse_color);
|
|
}
|
|
GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vbo_index, color_ub);
|
|
}
|
|
}
|
|
vbo_index += 1;
|
|
}
|
|
}
|
|
|
|
if (!buffers->smooth) {
|
|
for (j = 0; j < key->grid_size - 1; j++) {
|
|
for (k = 0; k < key->grid_size - 1; k++) {
|
|
CCGElem *elems[4] = {
|
|
CCG_grid_elem(key, grid, k, j + 1),
|
|
CCG_grid_elem(key, grid, k + 1, j + 1),
|
|
CCG_grid_elem(key, grid, k + 1, j),
|
|
CCG_grid_elem(key, grid, k, j)
|
|
};
|
|
float fno[3];
|
|
|
|
normal_quad_v3(fno,
|
|
CCG_elem_co(key, elems[0]),
|
|
CCG_elem_co(key, elems[1]),
|
|
CCG_elem_co(key, elems[2]),
|
|
CCG_elem_co(key, elems[3]));
|
|
|
|
vbo_index = vbo_index_offset + ((j + 1) * key->grid_size + k);
|
|
short no_short[3];
|
|
normal_float_to_short_v3(no_short, fno);
|
|
GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.nor, vbo_index, no_short);
|
|
|
|
if (has_mask) {
|
|
uchar color_ub[3];
|
|
if (show_mask) {
|
|
gpu_color_from_mask_quad_copy(key,
|
|
elems[0],
|
|
elems[1],
|
|
elems[2],
|
|
elems[3],
|
|
diffuse_color,
|
|
color_ub);
|
|
}
|
|
else {
|
|
unit_float_to_uchar_clamp_v3(color_ub, diffuse_color);
|
|
}
|
|
GWN_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vbo_index, color_ub);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
vbo_index_offset += key->grid_area;
|
|
}
|
|
|
|
gpu_pbvh_batch_init(buffers);
|
|
}
|
|
}
|
|
|
|
buffers->grids = grids;
|
|
buffers->grid_indices = grid_indices;
|
|
buffers->totgrid = totgrid;
|
|
buffers->grid_flag_mats = grid_flag_mats;
|
|
buffers->gridkey = *key;
|
|
|
|
//printf("node updated %p\n", buffers);
|
|
}
|
|
|
|
/* Build the element array buffer of grid indices using either
|
|
* unsigned shorts or unsigned ints. */
|
|
#define FILL_QUAD_BUFFER(max_vert_, tot_quad_, buffer_) \
|
|
{ \
|
|
int offset = 0; \
|
|
int i, j, k; \
|
|
\
|
|
Gwn_IndexBufBuilder elb; \
|
|
GWN_indexbuf_init( \
|
|
&elb, GWN_PRIM_TRIS, tot_quad_ * 2, max_vert_); \
|
|
\
|
|
/* Fill the buffer */ \
|
|
for (i = 0; i < totgrid; ++i) { \
|
|
BLI_bitmap *gh = NULL; \
|
|
if (grid_hidden) \
|
|
gh = grid_hidden[(grid_indices)[i]]; \
|
|
\
|
|
for (j = 0; j < gridsize - 1; ++j) { \
|
|
for (k = 0; k < gridsize - 1; ++k) { \
|
|
/* Skip hidden grid face */ \
|
|
if (gh && paint_is_grid_face_hidden( \
|
|
gh, gridsize, k, j)) \
|
|
{ \
|
|
continue; \
|
|
} \
|
|
GWN_indexbuf_add_generic_vert(&elb, offset + j * gridsize + k + 1); \
|
|
GWN_indexbuf_add_generic_vert(&elb, offset + j * gridsize + k); \
|
|
GWN_indexbuf_add_generic_vert(&elb, offset + (j + 1) * gridsize + k); \
|
|
\
|
|
GWN_indexbuf_add_generic_vert(&elb, offset + (j + 1) * gridsize + k + 1); \
|
|
GWN_indexbuf_add_generic_vert(&elb, offset + j * gridsize + k + 1); \
|
|
GWN_indexbuf_add_generic_vert(&elb, offset + (j + 1) * gridsize + k); \
|
|
} \
|
|
} \
|
|
\
|
|
offset += gridsize * gridsize; \
|
|
} \
|
|
buffer_ = GWN_indexbuf_build(&elb); \
|
|
} (void)0
|
|
/* end FILL_QUAD_BUFFER */
|
|
|
|
static Gwn_IndexBuf *gpu_get_grid_buffer(
|
|
int gridsize, unsigned *totquad, GridCommonGPUBuffer **grid_common_gpu_buffer,
|
|
/* remove this arg when gawain gets base-vertex support! */
|
|
int totgrid)
|
|
{
|
|
/* used in the FILL_QUAD_BUFFER macro */
|
|
BLI_bitmap * const *grid_hidden = NULL;
|
|
const int *grid_indices = NULL;
|
|
// int totgrid = 1;
|
|
|
|
GridCommonGPUBuffer *gridbuff = *grid_common_gpu_buffer;
|
|
|
|
if (gridbuff == NULL) {
|
|
*grid_common_gpu_buffer = gridbuff = MEM_mallocN(sizeof(GridCommonGPUBuffer), __func__);
|
|
gridbuff->mres_buffer = NULL;
|
|
gridbuff->mres_prev_gridsize = -1;
|
|
gridbuff->mres_prev_totquad = 0;
|
|
}
|
|
|
|
/* VBO is already built */
|
|
if (gridbuff->mres_buffer && gridbuff->mres_prev_gridsize == gridsize) {
|
|
*totquad = gridbuff->mres_prev_totquad;
|
|
return gridbuff->mres_buffer;
|
|
}
|
|
/* we can't reuse old, delete the existing buffer */
|
|
else if (gridbuff->mres_buffer) {
|
|
GWN_indexbuf_discard(gridbuff->mres_buffer);
|
|
gridbuff->mres_buffer = NULL;
|
|
}
|
|
|
|
/* Build new VBO */
|
|
*totquad = (gridsize - 1) * (gridsize - 1) * totgrid;
|
|
int max_vert = gridsize * gridsize * totgrid;
|
|
|
|
FILL_QUAD_BUFFER(max_vert, *totquad, gridbuff->mres_buffer);
|
|
|
|
gridbuff->mres_prev_gridsize = gridsize;
|
|
gridbuff->mres_prev_totquad = *totquad;
|
|
return gridbuff->mres_buffer;
|
|
}
|
|
|
|
#define FILL_FAST_BUFFER() \
|
|
{ \
|
|
Gwn_IndexBufBuilder elb; \
|
|
GWN_indexbuf_init(&elb, GWN_PRIM_TRIS, 6 * totgrid, INT_MAX); \
|
|
for (int i = 0; i < totgrid; i++) { \
|
|
GWN_indexbuf_add_generic_vert(&elb, i * gridsize * gridsize + gridsize - 1); \
|
|
GWN_indexbuf_add_generic_vert(&elb, i * gridsize * gridsize); \
|
|
GWN_indexbuf_add_generic_vert(&elb, (i + 1) * gridsize * gridsize - gridsize); \
|
|
GWN_indexbuf_add_generic_vert(&elb, (i + 1) * gridsize * gridsize - 1); \
|
|
GWN_indexbuf_add_generic_vert(&elb, i * gridsize * gridsize + gridsize - 1); \
|
|
GWN_indexbuf_add_generic_vert(&elb, (i + 1) * gridsize * gridsize - gridsize); \
|
|
} \
|
|
buffers->index_buf_fast = GWN_indexbuf_build(&elb); \
|
|
} (void)0
|
|
|
|
GPU_PBVH_Buffers *GPU_pbvh_grid_buffers_build(
|
|
int *grid_indices, int totgrid, BLI_bitmap **grid_hidden, int gridsize, const CCGKey *UNUSED(key),
|
|
GridCommonGPUBuffer **grid_common_gpu_buffer)
|
|
{
|
|
GPU_PBVH_Buffers *buffers;
|
|
int totquad;
|
|
int fully_visible_totquad = (gridsize - 1) * (gridsize - 1) * totgrid;
|
|
|
|
buffers = MEM_callocN(sizeof(GPU_PBVH_Buffers), "GPU_Buffers");
|
|
buffers->grid_hidden = grid_hidden;
|
|
buffers->totgrid = totgrid;
|
|
|
|
buffers->show_diffuse_color = false;
|
|
buffers->show_mask = true;
|
|
|
|
/* Count the number of quads */
|
|
totquad = BKE_pbvh_count_grid_quads(grid_hidden, grid_indices, totgrid, gridsize);
|
|
|
|
/* totally hidden node, return here to avoid BufferData with zero below. */
|
|
if (totquad == 0)
|
|
return buffers;
|
|
|
|
/* create and fill indices of the fast buffer too */
|
|
FILL_FAST_BUFFER();
|
|
|
|
if (totquad == fully_visible_totquad) {
|
|
buffers->index_buf = gpu_get_grid_buffer(
|
|
gridsize, &buffers->tot_quad, grid_common_gpu_buffer, totgrid);
|
|
buffers->has_hidden = false;
|
|
buffers->is_index_buf_global = true;
|
|
}
|
|
else {
|
|
uint max_vert = totgrid * gridsize * gridsize;
|
|
buffers->tot_quad = totquad;
|
|
|
|
FILL_QUAD_BUFFER(max_vert, totquad, buffers->index_buf);
|
|
|
|
buffers->has_hidden = false;
|
|
buffers->is_index_buf_global = false;
|
|
}
|
|
|
|
#ifdef USE_BASE_ELEM
|
|
/* Build coord/normal VBO */
|
|
if (GLEW_ARB_draw_elements_base_vertex /* 3.2 */) {
|
|
int i;
|
|
buffers->baseelemarray = MEM_mallocN(sizeof(int) * totgrid * 2, "GPU_PBVH_Buffers.baseelemarray");
|
|
buffers->baseindex = MEM_mallocN(sizeof(void *) * totgrid, "GPU_PBVH_Buffers.baseindex");
|
|
for (i = 0; i < totgrid; i++) {
|
|
buffers->baseelemarray[i] = buffers->tot_quad * 6;
|
|
buffers->baseelemarray[i + totgrid] = i * key->grid_area;
|
|
buffers->baseindex[i] = NULL;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return buffers;
|
|
}
|
|
|
|
#undef FILL_QUAD_BUFFER
|
|
|
|
/* Output a BMVert into a VertexBufferFormat array
|
|
*
|
|
* The vertex is skipped if hidden, otherwise the output goes into
|
|
* index '*v_index' in the 'vert_data' array and '*v_index' is
|
|
* incremented.
|
|
*/
|
|
static void gpu_bmesh_vert_to_buffer_copy__gwn(
|
|
BMVert *v,
|
|
Gwn_VertBuf *vert_buf,
|
|
int *v_index,
|
|
const float fno[3],
|
|
const float *fmask,
|
|
const int cd_vert_mask_offset,
|
|
const float diffuse_color[4],
|
|
const bool show_mask)
|
|
{
|
|
if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN)) {
|
|
|
|
/* Set coord, normal, and mask */
|
|
GWN_vertbuf_attr_set(vert_buf, g_vbo_id.pos, *v_index, v->co);
|
|
|
|
{
|
|
short no_short[3];
|
|
normal_float_to_short_v3(no_short, fno ? fno : v->no);
|
|
GWN_vertbuf_attr_set(vert_buf, g_vbo_id.nor, *v_index, no_short);
|
|
}
|
|
|
|
{
|
|
uchar color_ub[3];
|
|
float effective_mask;
|
|
if (show_mask) {
|
|
effective_mask = fmask ? *fmask
|
|
: BM_ELEM_CD_GET_FLOAT(v, cd_vert_mask_offset);
|
|
}
|
|
else {
|
|
effective_mask = 0.0f;
|
|
}
|
|
|
|
gpu_color_from_mask_copy(
|
|
effective_mask,
|
|
diffuse_color,
|
|
color_ub);
|
|
GWN_vertbuf_attr_set(vert_buf, g_vbo_id.col, *v_index, color_ub);
|
|
}
|
|
|
|
/* Assign index for use in the triangle index buffer */
|
|
/* note: caller must set: bm->elem_index_dirty |= BM_VERT; */
|
|
BM_elem_index_set(v, (*v_index)); /* set_dirty! */
|
|
|
|
(*v_index)++;
|
|
}
|
|
}
|
|
|
|
/* Return the total number of vertices that don't have BM_ELEM_HIDDEN set */
|
|
static int gpu_bmesh_vert_visible_count(GSet *bm_unique_verts,
|
|
GSet *bm_other_verts)
|
|
{
|
|
GSetIterator gs_iter;
|
|
int totvert = 0;
|
|
|
|
GSET_ITER (gs_iter, bm_unique_verts) {
|
|
BMVert *v = BLI_gsetIterator_getKey(&gs_iter);
|
|
if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN))
|
|
totvert++;
|
|
}
|
|
GSET_ITER (gs_iter, bm_other_verts) {
|
|
BMVert *v = BLI_gsetIterator_getKey(&gs_iter);
|
|
if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN))
|
|
totvert++;
|
|
}
|
|
|
|
return totvert;
|
|
}
|
|
|
|
/* Return the total number of visible faces */
|
|
static int gpu_bmesh_face_visible_count(GSet *bm_faces)
|
|
{
|
|
GSetIterator gh_iter;
|
|
int totface = 0;
|
|
|
|
GSET_ITER (gh_iter, bm_faces) {
|
|
BMFace *f = BLI_gsetIterator_getKey(&gh_iter);
|
|
|
|
if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN))
|
|
totface++;
|
|
}
|
|
|
|
return totface;
|
|
}
|
|
|
|
/* Creates a vertex buffer (coordinate, normal, color) and, if smooth
|
|
* shading, an element index buffer. */
|
|
void GPU_pbvh_bmesh_buffers_update(
|
|
GPU_PBVH_Buffers *buffers,
|
|
BMesh *bm,
|
|
GSet *bm_faces,
|
|
GSet *bm_unique_verts,
|
|
GSet *bm_other_verts,
|
|
const int update_flags)
|
|
{
|
|
const bool show_diffuse_color = (update_flags & GPU_PBVH_BUFFERS_SHOW_DIFFUSE_COLOR) != 0;
|
|
const bool show_mask = (update_flags & GPU_PBVH_BUFFERS_SHOW_MASK) != 0;
|
|
int tottri, totvert, maxvert = 0;
|
|
float diffuse_color[4] = {0.8f, 0.8f, 0.8f, 1.0f};
|
|
|
|
/* TODO, make mask layer optional for bmesh buffer */
|
|
const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK);
|
|
|
|
buffers->show_diffuse_color = show_diffuse_color;
|
|
buffers->show_mask = show_mask;
|
|
|
|
/* Count visible triangles */
|
|
tottri = gpu_bmesh_face_visible_count(bm_faces);
|
|
|
|
if (buffers->smooth) {
|
|
/* Smooth needs to recreate index buffer, so we have to invalidate the batch. */
|
|
GWN_BATCH_DISCARD_SAFE(buffers->triangles);
|
|
/* Count visible vertices */
|
|
totvert = gpu_bmesh_vert_visible_count(bm_unique_verts, bm_other_verts);
|
|
}
|
|
else {
|
|
totvert = tottri * 3;
|
|
}
|
|
|
|
if (!tottri) {
|
|
buffers->tot_tri = 0;
|
|
return;
|
|
}
|
|
|
|
if (show_diffuse_color) {
|
|
/* due to dynamic nature of dyntopo, only get first material */
|
|
GSetIterator gs_iter;
|
|
BMFace *f;
|
|
BLI_gsetIterator_init(&gs_iter, bm_faces);
|
|
f = BLI_gsetIterator_getKey(&gs_iter);
|
|
gpu_material_diffuse_get(f->mat_nr + 1, diffuse_color);
|
|
}
|
|
|
|
copy_v4_v4(buffers->diffuse_color, diffuse_color);
|
|
|
|
/* Fill vertex buffer */
|
|
if (gpu_pbvh_vert_buf_data_set(buffers, totvert)) {
|
|
int v_index = 0;
|
|
|
|
if (buffers->smooth) {
|
|
GSetIterator gs_iter;
|
|
|
|
/* Vertices get an index assigned for use in the triangle
|
|
* index buffer */
|
|
bm->elem_index_dirty |= BM_VERT;
|
|
|
|
GSET_ITER (gs_iter, bm_unique_verts) {
|
|
gpu_bmesh_vert_to_buffer_copy__gwn(
|
|
BLI_gsetIterator_getKey(&gs_iter),
|
|
buffers->vert_buf, &v_index, NULL, NULL,
|
|
cd_vert_mask_offset, diffuse_color,
|
|
show_mask);
|
|
}
|
|
|
|
GSET_ITER (gs_iter, bm_other_verts) {
|
|
gpu_bmesh_vert_to_buffer_copy__gwn(
|
|
BLI_gsetIterator_getKey(&gs_iter),
|
|
buffers->vert_buf, &v_index, NULL, NULL,
|
|
cd_vert_mask_offset, diffuse_color,
|
|
show_mask);
|
|
}
|
|
|
|
maxvert = v_index;
|
|
}
|
|
else {
|
|
GSetIterator gs_iter;
|
|
|
|
GSET_ITER (gs_iter, bm_faces) {
|
|
BMFace *f = BLI_gsetIterator_getKey(&gs_iter);
|
|
|
|
BLI_assert(f->len == 3);
|
|
|
|
if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
|
|
BMVert *v[3];
|
|
float fmask = 0;
|
|
int i;
|
|
|
|
#if 0
|
|
BM_iter_as_array(bm, BM_VERTS_OF_FACE, f, (void **)v, 3);
|
|
#endif
|
|
BM_face_as_array_vert_tri(f, v);
|
|
|
|
/* Average mask value */
|
|
for (i = 0; i < 3; i++) {
|
|
fmask += BM_ELEM_CD_GET_FLOAT(v[i], cd_vert_mask_offset);
|
|
}
|
|
fmask /= 3.0f;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
gpu_bmesh_vert_to_buffer_copy__gwn(
|
|
v[i], buffers->vert_buf,
|
|
&v_index, f->no, &fmask,
|
|
cd_vert_mask_offset, diffuse_color,
|
|
show_mask);
|
|
}
|
|
}
|
|
}
|
|
|
|
buffers->tot_tri = tottri;
|
|
}
|
|
|
|
/* gpu_bmesh_vert_to_buffer_copy sets dirty index values */
|
|
bm->elem_index_dirty |= BM_VERT;
|
|
}
|
|
else {
|
|
/* Memory map failed */
|
|
return;
|
|
}
|
|
|
|
if (buffers->smooth) {
|
|
/* Fill the triangle buffer */
|
|
buffers->index_buf = NULL;
|
|
Gwn_IndexBufBuilder elb;
|
|
GWN_indexbuf_init(&elb, GWN_PRIM_TRIS, tottri, maxvert);
|
|
|
|
/* Initialize triangle index buffer */
|
|
buffers->is_index_buf_global = false;
|
|
|
|
/* Fill triangle index buffer */
|
|
|
|
{
|
|
GSetIterator gs_iter;
|
|
|
|
GSET_ITER (gs_iter, bm_faces) {
|
|
BMFace *f = BLI_gsetIterator_getKey(&gs_iter);
|
|
|
|
if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
|
|
BMLoop *l_iter;
|
|
BMLoop *l_first;
|
|
|
|
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
|
|
do {
|
|
GWN_indexbuf_add_generic_vert(&elb, BM_elem_index_get(l_iter->v));
|
|
} while ((l_iter = l_iter->next) != l_first);
|
|
}
|
|
}
|
|
|
|
buffers->tot_tri = tottri;
|
|
|
|
if (buffers->index_buf == NULL) {
|
|
buffers->index_buf = GWN_indexbuf_build(&elb);
|
|
}
|
|
else {
|
|
GWN_indexbuf_build_in_place(&elb, buffers->index_buf);
|
|
}
|
|
}
|
|
}
|
|
else if (buffers->index_buf) {
|
|
if (!buffers->is_index_buf_global) {
|
|
GWN_INDEXBUF_DISCARD_SAFE(buffers->index_buf);
|
|
}
|
|
buffers->index_buf = NULL;
|
|
buffers->is_index_buf_global = false;
|
|
}
|
|
|
|
gpu_pbvh_batch_init(buffers);
|
|
}
|
|
|
|
GPU_PBVH_Buffers *GPU_pbvh_bmesh_buffers_build(bool smooth_shading)
|
|
{
|
|
GPU_PBVH_Buffers *buffers;
|
|
|
|
buffers = MEM_callocN(sizeof(GPU_PBVH_Buffers), "GPU_Buffers");
|
|
buffers->use_bmesh = true;
|
|
buffers->smooth = smooth_shading;
|
|
buffers->show_diffuse_color = false;
|
|
buffers->show_mask = true;
|
|
|
|
return buffers;
|
|
}
|
|
|
|
Gwn_Batch *GPU_pbvh_buffers_batch_get(GPU_PBVH_Buffers *buffers, bool fast)
|
|
{
|
|
return (fast && buffers->triangles_fast) ?
|
|
buffers->triangles_fast : buffers->triangles;
|
|
}
|
|
|
|
bool GPU_pbvh_buffers_diffuse_changed(GPU_PBVH_Buffers *buffers, GSet *bm_faces, bool show_diffuse_color)
|
|
{
|
|
float diffuse_color[4];
|
|
|
|
if (buffers->show_diffuse_color != show_diffuse_color)
|
|
return true;
|
|
|
|
if (buffers->show_diffuse_color == false)
|
|
return false;
|
|
|
|
if (buffers->looptri) {
|
|
const MLoopTri *lt = &buffers->looptri[buffers->face_indices[0]];
|
|
const MPoly *mp = &buffers->mpoly[lt->poly];
|
|
|
|
gpu_material_diffuse_get(mp->mat_nr + 1, diffuse_color);
|
|
}
|
|
else if (buffers->use_bmesh) {
|
|
/* due to dynamic nature of dyntopo, only get first material */
|
|
if (BLI_gset_len(bm_faces) > 0) {
|
|
GSetIterator gs_iter;
|
|
BMFace *f;
|
|
|
|
BLI_gsetIterator_init(&gs_iter, bm_faces);
|
|
f = BLI_gsetIterator_getKey(&gs_iter);
|
|
gpu_material_diffuse_get(f->mat_nr + 1, diffuse_color);
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
const DMFlagMat *flags = &buffers->grid_flag_mats[buffers->grid_indices[0]];
|
|
|
|
gpu_material_diffuse_get(flags->mat_nr + 1, diffuse_color);
|
|
}
|
|
|
|
return !equals_v3v3(diffuse_color, buffers->diffuse_color);
|
|
}
|
|
|
|
bool GPU_pbvh_buffers_mask_changed(GPU_PBVH_Buffers *buffers, bool show_mask)
|
|
{
|
|
return (buffers->show_mask != show_mask);
|
|
}
|
|
|
|
void GPU_pbvh_buffers_free(GPU_PBVH_Buffers *buffers)
|
|
{
|
|
if (buffers) {
|
|
GWN_BATCH_DISCARD_SAFE(buffers->triangles);
|
|
GWN_BATCH_DISCARD_SAFE(buffers->triangles_fast);
|
|
if (!buffers->is_index_buf_global) {
|
|
GWN_INDEXBUF_DISCARD_SAFE(buffers->index_buf);
|
|
}
|
|
GWN_INDEXBUF_DISCARD_SAFE(buffers->index_buf_fast);
|
|
GWN_VERTBUF_DISCARD_SAFE(buffers->vert_buf);
|
|
|
|
#ifdef USE_BASE_ELEM
|
|
if (buffers->baseelemarray)
|
|
MEM_freeN(buffers->baseelemarray);
|
|
if (buffers->baseindex)
|
|
MEM_freeN(buffers->baseindex);
|
|
#endif
|
|
|
|
MEM_freeN(buffers);
|
|
}
|
|
}
|
|
|
|
void GPU_pbvh_multires_buffers_free(GridCommonGPUBuffer **grid_common_gpu_buffer)
|
|
{
|
|
GridCommonGPUBuffer *gridbuff = *grid_common_gpu_buffer;
|
|
|
|
if (gridbuff) {
|
|
if (gridbuff->mres_buffer) {
|
|
BLI_mutex_lock(&buffer_mutex);
|
|
GWN_INDEXBUF_DISCARD_SAFE(gridbuff->mres_buffer);
|
|
BLI_mutex_unlock(&buffer_mutex);
|
|
}
|
|
MEM_freeN(gridbuff);
|
|
*grid_common_gpu_buffer = NULL;
|
|
}
|
|
}
|
|
|
|
/* debug function, draws the pbvh BB */
|
|
void GPU_pbvh_BB_draw(float min[3], float max[3], bool leaf, unsigned int pos)
|
|
{
|
|
if (leaf)
|
|
immUniformColor4f(0.0, 1.0, 0.0, 0.5);
|
|
else
|
|
immUniformColor4f(1.0, 0.0, 0.0, 0.5);
|
|
|
|
/* TODO(merwin): revisit this after we have mutable VertexBuffers
|
|
* could keep a static batch & index buffer, change the VBO contents per draw
|
|
*/
|
|
|
|
immBegin(GWN_PRIM_LINES, 24);
|
|
|
|
/* top */
|
|
immVertex3f(pos, min[0], min[1], max[2]);
|
|
immVertex3f(pos, min[0], max[1], max[2]);
|
|
|
|
immVertex3f(pos, min[0], max[1], max[2]);
|
|
immVertex3f(pos, max[0], max[1], max[2]);
|
|
|
|
immVertex3f(pos, max[0], max[1], max[2]);
|
|
immVertex3f(pos, max[0], min[1], max[2]);
|
|
|
|
immVertex3f(pos, max[0], min[1], max[2]);
|
|
immVertex3f(pos, min[0], min[1], max[2]);
|
|
|
|
/* bottom */
|
|
immVertex3f(pos, min[0], min[1], min[2]);
|
|
immVertex3f(pos, min[0], max[1], min[2]);
|
|
|
|
immVertex3f(pos, min[0], max[1], min[2]);
|
|
immVertex3f(pos, max[0], max[1], min[2]);
|
|
|
|
immVertex3f(pos, max[0], max[1], min[2]);
|
|
immVertex3f(pos, max[0], min[1], min[2]);
|
|
|
|
immVertex3f(pos, max[0], min[1], min[2]);
|
|
immVertex3f(pos, min[0], min[1], min[2]);
|
|
|
|
/* sides */
|
|
immVertex3f(pos, min[0], min[1], min[2]);
|
|
immVertex3f(pos, min[0], min[1], max[2]);
|
|
|
|
immVertex3f(pos, min[0], max[1], min[2]);
|
|
immVertex3f(pos, min[0], max[1], max[2]);
|
|
|
|
immVertex3f(pos, max[0], max[1], min[2]);
|
|
immVertex3f(pos, max[0], max[1], max[2]);
|
|
|
|
immVertex3f(pos, max[0], min[1], min[2]);
|
|
immVertex3f(pos, max[0], min[1], max[2]);
|
|
|
|
immEnd();
|
|
}
|
|
|
|
void GPU_pbvh_fix_linking()
|
|
{
|
|
}
|