2018-07-31 10:22:19 +02:00
|
|
|
/*
|
|
|
|
|
* Copyright 2017, Blender Foundation.
|
|
|
|
|
*
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
|
|
|
|
* Contributor(s): Antonio Vazquez
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/** \file blender/draw/engines/gpencil/gpencil_cache_utils.c
|
|
|
|
|
* \ingroup draw
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "DRW_render.h"
|
|
|
|
|
|
|
|
|
|
#include "BKE_global.h"
|
|
|
|
|
|
|
|
|
|
#include "ED_gpencil.h"
|
|
|
|
|
#include "ED_view3d.h"
|
|
|
|
|
|
|
|
|
|
#include "DNA_gpencil_types.h"
|
|
|
|
|
#include "DNA_view3d_types.h"
|
|
|
|
|
|
|
|
|
|
#include "gpencil_engine.h"
|
|
|
|
|
|
|
|
|
|
#include "draw_cache_impl.h"
|
|
|
|
|
|
2018-08-15 16:07:16 +02:00
|
|
|
static bool gpencil_check_ob_duplicated(tGPencilObjectCache *cache_array, int gp_cache_used, Object *ob)
|
|
|
|
|
{
|
|
|
|
|
if (gp_cache_used == 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < gp_cache_used + 1; i++) {
|
|
|
|
|
tGPencilObjectCache *cache_elem = &cache_array[i];
|
|
|
|
|
if (STREQ(cache_elem->ob_name, ob->id.name) &&
|
2018-08-22 10:10:12 +10:00
|
|
|
(cache_elem->is_dup_ob == false))
|
2018-08-15 16:07:16 +02:00
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-22 10:10:12 +10:00
|
|
|
static bool gpencil_check_datablock_duplicated(
|
|
|
|
|
tGPencilObjectCache *cache_array, int gp_cache_used,
|
|
|
|
|
Object *ob, bGPdata *gpd)
|
2018-08-15 16:07:16 +02:00
|
|
|
{
|
|
|
|
|
if (gp_cache_used == 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < gp_cache_used + 1; i++) {
|
|
|
|
|
tGPencilObjectCache *cache_elem = &cache_array[i];
|
|
|
|
|
if (!STREQ(cache_elem->ob_name, ob->id.name) &&
|
2018-08-22 10:10:12 +10:00
|
|
|
(cache_elem->gpd == gpd))
|
2018-08-15 16:07:16 +02:00
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-31 10:22:19 +02:00
|
|
|
/* add a gpencil object to cache to defer drawing */
|
2018-07-31 20:11:55 +10:00
|
|
|
tGPencilObjectCache *gpencil_object_cache_add(
|
2018-08-15 16:07:16 +02:00
|
|
|
tGPencilObjectCache *cache_array, Object *ob,
|
2018-07-31 20:11:55 +10:00
|
|
|
int *gp_cache_size, int *gp_cache_used)
|
2018-07-31 10:22:19 +02:00
|
|
|
{
|
|
|
|
|
const DRWContextState *draw_ctx = DRW_context_state_get();
|
|
|
|
|
tGPencilObjectCache *cache_elem = NULL;
|
|
|
|
|
RegionView3D *rv3d = draw_ctx->rv3d;
|
|
|
|
|
tGPencilObjectCache *p = NULL;
|
|
|
|
|
|
|
|
|
|
/* By default a cache is created with one block with a predefined number of free slots,
|
|
|
|
|
if the size is not enough, the cache is reallocated adding a new block of free slots.
|
|
|
|
|
This is done in order to keep cache small */
|
|
|
|
|
if (*gp_cache_used + 1 > *gp_cache_size) {
|
|
|
|
|
if ((*gp_cache_size == 0) || (cache_array == NULL)) {
|
|
|
|
|
p = MEM_callocN(sizeof(struct tGPencilObjectCache) * GP_CACHE_BLOCK_SIZE, "tGPencilObjectCache");
|
|
|
|
|
*gp_cache_size = GP_CACHE_BLOCK_SIZE;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
*gp_cache_size += GP_CACHE_BLOCK_SIZE;
|
|
|
|
|
p = MEM_recallocN(cache_array, sizeof(struct tGPencilObjectCache) * *gp_cache_size);
|
|
|
|
|
}
|
|
|
|
|
cache_array = p;
|
|
|
|
|
}
|
|
|
|
|
/* zero out all pointers */
|
|
|
|
|
cache_elem = &cache_array[*gp_cache_used];
|
|
|
|
|
memset(cache_elem, 0, sizeof(*cache_elem));
|
|
|
|
|
|
2018-08-15 16:07:16 +02:00
|
|
|
cache_elem->is_dup_ob = gpencil_check_ob_duplicated(cache_array, *gp_cache_used, ob);
|
|
|
|
|
|
2018-08-16 17:10:17 +02:00
|
|
|
STRNCPY(cache_elem->ob_name, ob->id.name);
|
2018-08-15 16:07:16 +02:00
|
|
|
cache_elem->gpd = (bGPdata *)ob->data;
|
|
|
|
|
|
|
|
|
|
copy_v3_v3(cache_elem->loc, ob->loc);
|
|
|
|
|
copy_m4_m4(cache_elem->obmat, ob->obmat);
|
2018-07-31 10:22:19 +02:00
|
|
|
cache_elem->idx = *gp_cache_used;
|
|
|
|
|
|
2018-08-22 10:10:12 +10:00
|
|
|
cache_elem->is_dup_onion = gpencil_check_datablock_duplicated(
|
|
|
|
|
cache_array, *gp_cache_used,
|
|
|
|
|
ob, cache_elem->gpd);
|
2018-08-15 16:07:16 +02:00
|
|
|
|
|
|
|
|
/* save FXs */
|
|
|
|
|
cache_elem->pixfactor = cache_elem->gpd->pixfactor;
|
|
|
|
|
cache_elem->shader_fx = ob->shader_fx;
|
|
|
|
|
|
2018-07-31 10:22:19 +02:00
|
|
|
cache_elem->init_grp = 0;
|
|
|
|
|
cache_elem->end_grp = -1;
|
|
|
|
|
|
|
|
|
|
/* calculate zdepth from point of view */
|
|
|
|
|
float zdepth = 0.0;
|
|
|
|
|
if (rv3d) {
|
|
|
|
|
if (rv3d->is_persp) {
|
|
|
|
|
zdepth = ED_view3d_calc_zfac(rv3d, ob->loc, NULL);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
zdepth = -dot_v3v3(rv3d->viewinv[2], ob->loc);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* In render mode, rv3d is not available, so use the distance to camera.
|
|
|
|
|
* The real distance is not important, but the relative distance to the camera plane
|
|
|
|
|
* in order to sort by z_depth of the objects
|
|
|
|
|
*/
|
|
|
|
|
float vn[3] = { 0.0f, 0.0f, -1.0f }; /* always face down */
|
|
|
|
|
float plane_cam[4];
|
|
|
|
|
struct Object *camera = draw_ctx->scene->camera;
|
|
|
|
|
if (camera) {
|
|
|
|
|
mul_m4_v3(camera->obmat, vn);
|
|
|
|
|
normalize_v3(vn);
|
|
|
|
|
plane_from_point_normal_v3(plane_cam, camera->loc, vn);
|
|
|
|
|
zdepth = dist_squared_to_plane_v3(ob->loc, plane_cam);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
cache_elem->zdepth = zdepth;
|
|
|
|
|
/* increase slots used in cache */
|
|
|
|
|
(*gp_cache_used)++;
|
|
|
|
|
|
|
|
|
|
return cache_array;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* get current cache data */
|
2018-08-15 22:11:44 +02:00
|
|
|
static GpencilBatchCache *gpencil_batch_get_element(Object *ob)
|
2018-07-31 10:22:19 +02:00
|
|
|
{
|
|
|
|
|
bGPdata *gpd = ob->data;
|
|
|
|
|
if (gpd->runtime.batch_cache_data == NULL) {
|
|
|
|
|
gpd->runtime.batch_cache_data = BLI_ghash_str_new("GP batch cache data");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (GpencilBatchCache *) BLI_ghash_lookup(gpd->runtime.batch_cache_data, ob->id.name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* verify if cache is valid */
|
|
|
|
|
static bool gpencil_batch_cache_valid(Object *ob, bGPdata *gpd, int cfra)
|
|
|
|
|
{
|
|
|
|
|
GpencilBatchCache *cache = gpencil_batch_get_element(ob);
|
|
|
|
|
|
|
|
|
|
if (cache == NULL) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cache->is_editmode = GPENCIL_ANY_EDIT_MODE(gpd);
|
|
|
|
|
|
|
|
|
|
if (cfra != cache->cache_frame) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (gpd->flag & GP_DATA_CACHE_IS_DIRTY) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cache->is_editmode) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cache->is_dirty) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* resize the cache to the number of slots */
|
|
|
|
|
static void gpencil_batch_cache_resize(GpencilBatchCache *cache, int slots)
|
|
|
|
|
{
|
|
|
|
|
cache->cache_size = slots;
|
|
|
|
|
cache->batch_stroke = MEM_recallocN(cache->batch_stroke, sizeof(struct Gwn_Batch *) * slots);
|
|
|
|
|
cache->batch_fill = MEM_recallocN(cache->batch_fill, sizeof(struct Gwn_Batch *) * slots);
|
|
|
|
|
cache->batch_edit = MEM_recallocN(cache->batch_edit, sizeof(struct Gwn_Batch *) * slots);
|
|
|
|
|
cache->batch_edlin = MEM_recallocN(cache->batch_edlin, sizeof(struct Gwn_Batch *) * slots);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* check size and increase if no free slots */
|
|
|
|
|
void gpencil_batch_cache_check_free_slots(Object *ob)
|
|
|
|
|
{
|
|
|
|
|
GpencilBatchCache *cache = gpencil_batch_get_element(ob);
|
|
|
|
|
|
|
|
|
|
/* the memory is reallocated by chunks, not for one slot only to improve speed */
|
|
|
|
|
if (cache->cache_idx >= cache->cache_size) {
|
|
|
|
|
cache->cache_size += GPENCIL_MIN_BATCH_SLOTS_CHUNK;
|
|
|
|
|
gpencil_batch_cache_resize(cache, cache->cache_size);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* cache init */
|
|
|
|
|
static void gpencil_batch_cache_init(Object *ob, int cfra)
|
|
|
|
|
{
|
|
|
|
|
GpencilBatchCache *cache = gpencil_batch_get_element(ob);
|
|
|
|
|
bGPdata *gpd = ob->data;
|
|
|
|
|
|
|
|
|
|
if (G.debug_value >= 664) {
|
|
|
|
|
printf("gpencil_batch_cache_init: %s\n", ob->id.name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!cache) {
|
|
|
|
|
cache = MEM_callocN(sizeof(*cache), __func__);
|
|
|
|
|
BLI_ghash_insert(gpd->runtime.batch_cache_data, ob->id.name, cache);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
memset(cache, 0, sizeof(*cache));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cache->cache_size = GPENCIL_MIN_BATCH_SLOTS_CHUNK;
|
|
|
|
|
cache->batch_stroke = MEM_callocN(sizeof(struct Gwn_Batch *) * cache->cache_size, "Gpencil_Batch_Stroke");
|
|
|
|
|
cache->batch_fill = MEM_callocN(sizeof(struct Gwn_Batch *) * cache->cache_size, "Gpencil_Batch_Fill");
|
|
|
|
|
cache->batch_edit = MEM_callocN(sizeof(struct Gwn_Batch *) * cache->cache_size, "Gpencil_Batch_Edit");
|
|
|
|
|
cache->batch_edlin = MEM_callocN(sizeof(struct Gwn_Batch *) * cache->cache_size, "Gpencil_Batch_Edlin");
|
|
|
|
|
|
|
|
|
|
cache->is_editmode = GPENCIL_ANY_EDIT_MODE(gpd);
|
|
|
|
|
gpd->flag &= ~GP_DATA_CACHE_IS_DIRTY;
|
|
|
|
|
|
|
|
|
|
cache->cache_idx = 0;
|
|
|
|
|
cache->is_dirty = true;
|
|
|
|
|
cache->cache_frame = cfra;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* clear cache */
|
2018-08-15 16:07:16 +02:00
|
|
|
static void gpencil_batch_cache_clear(GpencilBatchCache *cache)
|
2018-07-31 10:22:19 +02:00
|
|
|
{
|
|
|
|
|
if (!cache) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cache->cache_size == 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cache->cache_size > 0) {
|
|
|
|
|
for (int i = 0; i < cache->cache_size; i++) {
|
|
|
|
|
GPU_BATCH_DISCARD_SAFE(cache->batch_stroke[i]);
|
|
|
|
|
GPU_BATCH_DISCARD_SAFE(cache->batch_fill[i]);
|
|
|
|
|
GPU_BATCH_DISCARD_SAFE(cache->batch_edit[i]);
|
|
|
|
|
GPU_BATCH_DISCARD_SAFE(cache->batch_edlin[i]);
|
|
|
|
|
}
|
|
|
|
|
MEM_SAFE_FREE(cache->batch_stroke);
|
|
|
|
|
MEM_SAFE_FREE(cache->batch_fill);
|
|
|
|
|
MEM_SAFE_FREE(cache->batch_edit);
|
|
|
|
|
MEM_SAFE_FREE(cache->batch_edlin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MEM_SAFE_FREE(cache);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* get cache */
|
|
|
|
|
GpencilBatchCache *gpencil_batch_cache_get(Object *ob, int cfra)
|
|
|
|
|
{
|
|
|
|
|
bGPdata *gpd = ob->data;
|
|
|
|
|
|
|
|
|
|
if (!gpencil_batch_cache_valid(ob, gpd, cfra)) {
|
|
|
|
|
if (G.debug_value >= 664) {
|
|
|
|
|
printf("gpencil_batch_cache: %s\n", gpd->id.name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GpencilBatchCache *cache = gpencil_batch_get_element(ob);
|
|
|
|
|
if (cache) {
|
2018-08-15 16:07:16 +02:00
|
|
|
gpencil_batch_cache_clear(cache);
|
2018-07-31 10:22:19 +02:00
|
|
|
BLI_ghash_remove(gpd->runtime.batch_cache_data, ob->id.name, NULL, NULL);
|
|
|
|
|
}
|
|
|
|
|
gpencil_batch_cache_init(ob, cfra);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return gpencil_batch_get_element(ob);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* set cache as dirty */
|
2018-08-23 10:14:29 -03:00
|
|
|
void DRW_gpencil_batch_cache_dirty_tag(bGPdata *gpd)
|
2018-07-31 10:22:19 +02:00
|
|
|
{
|
|
|
|
|
if (gpd->runtime.batch_cache_data == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GHashIterator *ihash = BLI_ghashIterator_new(gpd->runtime.batch_cache_data);
|
|
|
|
|
while (!BLI_ghashIterator_done(ihash)) {
|
|
|
|
|
GpencilBatchCache *cache = (GpencilBatchCache *)BLI_ghashIterator_getValue(ihash);
|
|
|
|
|
if (cache) {
|
|
|
|
|
cache->is_dirty = true;
|
|
|
|
|
}
|
|
|
|
|
BLI_ghashIterator_step(ihash);
|
|
|
|
|
}
|
|
|
|
|
BLI_ghashIterator_free(ihash);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* free batch cache */
|
|
|
|
|
void DRW_gpencil_batch_cache_free(bGPdata *gpd)
|
|
|
|
|
{
|
|
|
|
|
if (gpd->runtime.batch_cache_data == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GHashIterator *ihash = BLI_ghashIterator_new(gpd->runtime.batch_cache_data);
|
|
|
|
|
while (!BLI_ghashIterator_done(ihash)) {
|
|
|
|
|
GpencilBatchCache *cache = (GpencilBatchCache *)BLI_ghashIterator_getValue(ihash);
|
|
|
|
|
if (cache) {
|
2018-08-15 16:07:16 +02:00
|
|
|
gpencil_batch_cache_clear(cache);
|
2018-07-31 10:22:19 +02:00
|
|
|
}
|
|
|
|
|
BLI_ghashIterator_step(ihash);
|
|
|
|
|
}
|
|
|
|
|
BLI_ghashIterator_free(ihash);
|
|
|
|
|
|
|
|
|
|
/* free hash */
|
|
|
|
|
if (gpd->runtime.batch_cache_data) {
|
|
|
|
|
BLI_ghash_free(gpd->runtime.batch_cache_data, NULL, NULL);
|
|
|
|
|
gpd->runtime.batch_cache_data = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|