This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/draw/intern/draw_cache_impl_particles.c
Sergey Sharybin ca2be6912d Hair editing: Use original object's particles for drawing
This allows to rely on brush to update children positions,
and avoid tag of object. Makes it way faster to comb with
children enabled.
2018-06-15 17:23:49 +02:00

1591 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.
*
* The Original Code is Copyright (C) 2017 by Blender Foundation.
* All rights reserved.
*
* Contributor(s): Blender Foundation, Mike Erwin, Dalai Felinto
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file draw_cache_impl_particles.c
* \ingroup draw
*
* \brief Particle API for render engines
*/
#include "DRW_render.h"
#include "MEM_guardedalloc.h"
#include "BLI_utildefines.h"
#include "BLI_math_vector.h"
#include "BLI_string.h"
#include "BLI_ghash.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
#include "DNA_particle_types.h"
#include "DNA_customdata_types.h"
#include "BKE_mesh.h"
#include "BKE_modifier.h"
#include "BKE_particle.h"
#include "BKE_pointcache.h"
#include "ED_particle.h"
#include "GPU_batch.h"
#include "DEG_depsgraph_query.h"
#include "draw_cache_impl.h" /* own include */
#include "draw_hair_private.h"
static void particle_batch_cache_clear(ParticleSystem *psys);
/* ---------------------------------------------------------------------- */
/* Particle Gwn_Batch Cache */
typedef struct ParticlePointCache {
Gwn_VertBuf *pos;
Gwn_Batch *points;
int elems_count;
int point_count;
} ParticlePointCache;
typedef struct ParticleBatchCache {
/* Object mode strands for hair and points for particle,
* strands for paths when in edit mode.
*/
ParticleHairCache hair; /* Used for hair strands */
ParticlePointCache point; /* Used for particle points. */
/* Control points when in edit mode. */
ParticleHairCache edit_hair;
Gwn_VertBuf *edit_inner_pos;
Gwn_Batch *edit_inner_points;
int edit_inner_point_count;
Gwn_VertBuf *edit_tip_pos;
Gwn_Batch *edit_tip_points;
int edit_tip_point_count;
/* Settings to determine if cache is invalid. */
bool is_dirty;
} ParticleBatchCache;
/* Gwn_Batch cache management. */
typedef struct HairAttributeID {
uint pos;
uint tan;
uint ind;
} HairAttributeID;
static bool particle_batch_cache_valid(ParticleSystem *psys)
{
ParticleBatchCache *cache = psys->batch_cache;
if (cache == NULL) {
return false;
}
if (cache->is_dirty == false) {
return true;
}
else {
return false;
}
return true;
}
static void particle_batch_cache_init(ParticleSystem *psys)
{
ParticleBatchCache *cache = psys->batch_cache;
if (!cache) {
cache = psys->batch_cache = MEM_callocN(sizeof(*cache), __func__);
}
else {
memset(cache, 0, sizeof(*cache));
}
cache->is_dirty = false;
}
static ParticleBatchCache *particle_batch_cache_get(ParticleSystem *psys)
{
if (!particle_batch_cache_valid(psys)) {
particle_batch_cache_clear(psys);
particle_batch_cache_init(psys);
}
return psys->batch_cache;
}
void DRW_particle_batch_cache_dirty(ParticleSystem *psys, int mode)
{
ParticleBatchCache *cache = psys->batch_cache;
if (cache == NULL) {
return;
}
switch (mode) {
case BKE_PARTICLE_BATCH_DIRTY_ALL:
cache->is_dirty = true;
break;
default:
BLI_assert(0);
}
}
static void particle_batch_cache_clear_point(ParticlePointCache *point_cache)
{
GWN_BATCH_DISCARD_SAFE(point_cache->points);
GWN_VERTBUF_DISCARD_SAFE(point_cache->pos);
}
static void particle_batch_cache_clear_hair(ParticleHairCache *hair_cache)
{
/* TODO more granular update tagging. */
GWN_VERTBUF_DISCARD_SAFE(hair_cache->proc_point_buf);
DRW_TEXTURE_FREE_SAFE(hair_cache->point_tex);
GWN_VERTBUF_DISCARD_SAFE(hair_cache->proc_strand_buf);
DRW_TEXTURE_FREE_SAFE(hair_cache->strand_tex);
for (int i = 0; i < MAX_MTFACE; ++i) {
GWN_VERTBUF_DISCARD_SAFE(hair_cache->proc_uv_buf[i]);
DRW_TEXTURE_FREE_SAFE(hair_cache->uv_tex[i]);
}
for (int i = 0; i < MAX_MCOL; ++i) {
GWN_VERTBUF_DISCARD_SAFE(hair_cache->proc_col_buf[i]);
DRW_TEXTURE_FREE_SAFE(hair_cache->col_tex[i]);
}
for (int i = 0; i < MAX_HAIR_SUBDIV; ++i) {
GWN_VERTBUF_DISCARD_SAFE(hair_cache->final[i].proc_buf);
DRW_TEXTURE_FREE_SAFE(hair_cache->final[i].proc_tex);
for (int j = 0; j < MAX_THICKRES; ++j) {
GWN_BATCH_DISCARD_SAFE(hair_cache->final[i].proc_hairs[j]);
}
}
/* "Normal" legacy hairs */
GWN_BATCH_DISCARD_SAFE(hair_cache->hairs);
GWN_VERTBUF_DISCARD_SAFE(hair_cache->pos);
GWN_INDEXBUF_DISCARD_SAFE(hair_cache->indices);
}
static void particle_batch_cache_clear(ParticleSystem *psys)
{
ParticleBatchCache *cache = psys->batch_cache;
if (!cache) {
return;
}
particle_batch_cache_clear_point(&cache->point);
particle_batch_cache_clear_hair(&cache->hair);
particle_batch_cache_clear_hair(&cache->edit_hair);
GWN_BATCH_DISCARD_SAFE(cache->edit_inner_points);
GWN_VERTBUF_DISCARD_SAFE(cache->edit_inner_pos);
GWN_BATCH_DISCARD_SAFE(cache->edit_tip_points);
GWN_VERTBUF_DISCARD_SAFE(cache->edit_tip_pos);
}
void DRW_particle_batch_cache_free(ParticleSystem *psys)
{
particle_batch_cache_clear(psys);
MEM_SAFE_FREE(psys->batch_cache);
}
static void count_cache_segment_keys(
ParticleCacheKey **pathcache,
const int num_path_cache_keys,
ParticleHairCache *hair_cache)
{
for (int i = 0; i < num_path_cache_keys; i++) {
ParticleCacheKey *path = pathcache[i];
if (path->segments > 0) {
hair_cache->strands_count++;
hair_cache->elems_count += path->segments + 2;
hair_cache->point_count += path->segments + 1;
}
}
}
static void ensure_seg_pt_count(
PTCacheEdit *edit,
ParticleSystem *psys,
ParticleHairCache *hair_cache)
{
if ((hair_cache->pos != NULL && hair_cache->indices != NULL) ||
(hair_cache->proc_point_buf != NULL))
{
return;
}
hair_cache->strands_count = 0;
hair_cache->elems_count = 0;
hair_cache->point_count = 0;
if (edit != NULL && edit->pathcache != NULL) {
count_cache_segment_keys(edit->pathcache, edit->totcached, hair_cache);
}
else {
if (psys->pathcache &&
(!psys->childcache || (psys->part->draw & PART_DRAW_PARENT)))
{
count_cache_segment_keys(psys->pathcache, psys->totpart, hair_cache);
}
if (psys->childcache) {
const int child_count = psys->totchild * psys->part->disp / 100;
count_cache_segment_keys(psys->childcache, child_count, hair_cache);
}
}
}
static void particle_pack_mcol(MCol *mcol, unsigned short r_scol[3])
{
/* Convert to linear ushort and swizzle */
r_scol[0] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->b]);
r_scol[1] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->g]);
r_scol[2] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->r]);
}
/* Used by parent particles and simple children. */
static void particle_calculate_parent_uvs(
ParticleSystem *psys,
ParticleSystemModifierData *psmd,
const int num_uv_layers,
const int parent_index,
/*const*/ MTFace **mtfaces,
float (*r_uv)[2])
{
if (psmd == NULL) {
return;
}
const int emit_from = psmd->psys->part->from;
if (!ELEM(emit_from, PART_FROM_FACE, PART_FROM_VOLUME)) {
return;
}
ParticleData *particle = &psys->particles[parent_index];
int num = particle->num_dmcache;
if (num == DMCACHE_NOTFOUND) {
if (particle->num < psmd->mesh_final->totface) {
num = particle->num;
}
}
if (num != DMCACHE_NOTFOUND) {
MFace *mface = &psmd->mesh_final->mface[num];
for (int j = 0; j < num_uv_layers; j++) {
psys_interpolate_uvs(
mtfaces[j] + num,
mface->v4,
particle->fuv,
r_uv[j]);
}
}
}
static void particle_calculate_parent_mcol(
ParticleSystem *psys,
ParticleSystemModifierData *psmd,
const int num_uv_layers,
const int parent_index,
/*const*/ MCol **mcols,
MCol *r_mcol)
{
if (psmd == NULL) {
return;
}
const int emit_from = psmd->psys->part->from;
if (!ELEM(emit_from, PART_FROM_FACE, PART_FROM_VOLUME)) {
return;
}
ParticleData *particle = &psys->particles[parent_index];
int num = particle->num_dmcache;
if (num == DMCACHE_NOTFOUND) {
if (particle->num < psmd->mesh_final->totface) {
num = particle->num;
}
}
if (num != DMCACHE_NOTFOUND) {
MFace *mface = &psmd->mesh_final->mface[num];
for (int j = 0; j < num_uv_layers; j++) {
psys_interpolate_mcol(
mcols[j] + num,
mface->v4,
particle->fuv,
&r_mcol[j]);
}
}
}
/* Used by interpolated children. */
static void particle_interpolate_children_uvs(
ParticleSystem *psys,
ParticleSystemModifierData *psmd,
const int num_uv_layers,
const int child_index,
/*const*/ MTFace **mtfaces,
float (*r_uv)[2])
{
if (psmd == NULL) {
return;
}
const int emit_from = psmd->psys->part->from;
if (!ELEM(emit_from, PART_FROM_FACE, PART_FROM_VOLUME)) {
return;
}
ChildParticle *particle = &psys->child[child_index];
int num = particle->num;
if (num != DMCACHE_NOTFOUND) {
MFace *mface = &psmd->mesh_final->mface[num];
for (int j = 0; j < num_uv_layers; j++) {
psys_interpolate_uvs(
mtfaces[j] + num,
mface->v4,
particle->fuv,
r_uv[j]);
}
}
}
static void particle_interpolate_children_mcol(
ParticleSystem *psys,
ParticleSystemModifierData *psmd,
const int num_col_layers,
const int child_index,
/*const*/ MCol **mcols,
MCol *r_mcol)
{
if (psmd == NULL) {
return;
}
const int emit_from = psmd->psys->part->from;
if (!ELEM(emit_from, PART_FROM_FACE, PART_FROM_VOLUME)) {
return;
}
ChildParticle *particle = &psys->child[child_index];
int num = particle->num;
if (num != DMCACHE_NOTFOUND) {
MFace *mface = &psmd->mesh_final->mface[num];
for (int j = 0; j < num_col_layers; j++) {
psys_interpolate_mcol(
mcols[j] + num,
mface->v4,
particle->fuv,
&r_mcol[j]);
}
}
}
static void particle_calculate_uvs(
ParticleSystem *psys,
ParticleSystemModifierData *psmd,
const bool is_simple,
const int num_uv_layers,
const int parent_index,
const int child_index,
/*const*/ MTFace **mtfaces,
float (**r_parent_uvs)[2],
float (**r_uv)[2])
{
if (psmd == NULL) {
return;
}
if (is_simple) {
if (r_parent_uvs[parent_index] != NULL) {
*r_uv = r_parent_uvs[parent_index];
}
else {
*r_uv = MEM_callocN(sizeof(**r_uv) * num_uv_layers, "Particle UVs");
}
}
else {
*r_uv = MEM_callocN(sizeof(**r_uv) * num_uv_layers, "Particle UVs");
}
if (child_index == -1) {
/* Calculate UVs for parent particles. */
if (is_simple) {
r_parent_uvs[parent_index] = *r_uv;
}
particle_calculate_parent_uvs(
psys, psmd, num_uv_layers, parent_index, mtfaces, *r_uv);
}
else {
/* Calculate UVs for child particles. */
if (!is_simple) {
particle_interpolate_children_uvs(
psys, psmd, num_uv_layers, child_index, mtfaces, *r_uv);
}
else if (!r_parent_uvs[psys->child[child_index].parent]) {
r_parent_uvs[psys->child[child_index].parent] = *r_uv;
particle_calculate_parent_uvs(
psys, psmd, num_uv_layers, parent_index, mtfaces, *r_uv);
}
}
}
static void particle_calculate_mcol(
ParticleSystem *psys,
ParticleSystemModifierData *psmd,
const bool is_simple,
const int num_col_layers,
const int parent_index,
const int child_index,
/*const*/ MCol **mcols,
MCol **r_parent_mcol,
MCol **r_mcol)
{
if (psmd == NULL) {
return;
}
if (is_simple) {
if (r_parent_mcol[parent_index] != NULL) {
*r_mcol = r_parent_mcol[parent_index];
}
else {
*r_mcol = MEM_callocN(sizeof(**r_mcol) * num_col_layers, "Particle MCol");
}
}
else {
*r_mcol = MEM_callocN(sizeof(**r_mcol) * num_col_layers, "Particle MCol");
}
if (child_index == -1) {
/* Calculate MCols for parent particles. */
if (is_simple) {
r_parent_mcol[parent_index] = *r_mcol;
}
particle_calculate_parent_mcol(
psys, psmd, num_col_layers, parent_index, mcols, *r_mcol);
}
else {
/* Calculate MCols for child particles. */
if (!is_simple) {
particle_interpolate_children_mcol(
psys, psmd, num_col_layers, child_index, mcols, *r_mcol);
}
else if (!r_parent_mcol[psys->child[child_index].parent]) {
r_parent_mcol[psys->child[child_index].parent] = *r_mcol;
particle_calculate_parent_mcol(
psys, psmd, num_col_layers, parent_index, mcols, *r_mcol);
}
}
}
/* Will return last filled index. */
typedef enum ParticleSource {
PARTICLE_SOURCE_PARENT,
PARTICLE_SOURCE_CHILDREN,
} ParticleSource;
static int particle_batch_cache_fill_segments(
ParticleSystem *psys,
ParticleSystemModifierData *psmd,
ParticleCacheKey **path_cache,
const ParticleSource particle_source,
const int global_offset,
const int start_index,
const int num_path_keys,
const int num_uv_layers,
const int num_col_layers,
/*const*/ MTFace **mtfaces,
/*const*/ MCol **mcols,
uint *uv_id,
uint *col_id,
float (***r_parent_uvs)[2],
MCol ***r_parent_mcol,
Gwn_IndexBufBuilder *elb,
HairAttributeID *attr_id,
ParticleHairCache *hair_cache)
{
const bool is_simple = (psys->part->childtype == PART_CHILD_PARTICLES);
const bool is_child = (particle_source == PARTICLE_SOURCE_CHILDREN);
if (is_simple && *r_parent_uvs == NULL) {
/* TODO(sergey): For edit mode it should be edit->totcached. */
*r_parent_uvs = MEM_callocN(
sizeof(*r_parent_uvs) * psys->totpart,
"Parent particle UVs");
}
if (is_simple && *r_parent_mcol == NULL) {
*r_parent_mcol = MEM_callocN(
sizeof(*r_parent_mcol) * psys->totpart,
"Parent particle MCol");
}
int curr_point = start_index;
for (int i = 0; i < num_path_keys; i++) {
ParticleCacheKey *path = path_cache[i];
if (path->segments <= 0) {
continue;
}
float tangent[3];
float (*uv)[2] = NULL;
MCol *mcol = NULL;
particle_calculate_mcol(
psys, psmd,
is_simple, num_col_layers,
is_child ? psys->child[i].parent : i,
is_child ? i : -1,
mcols,
*r_parent_mcol, &mcol);
particle_calculate_uvs(
psys, psmd,
is_simple, num_uv_layers,
is_child ? psys->child[i].parent : i,
is_child ? i : -1,
mtfaces,
*r_parent_uvs, &uv);
for (int j = 0; j < path->segments; j++) {
if (j == 0) {
sub_v3_v3v3(tangent, path[j + 1].co, path[j].co);
}
else {
sub_v3_v3v3(tangent, path[j + 1].co, path[j - 1].co);
}
GWN_vertbuf_attr_set(hair_cache->pos, attr_id->pos, curr_point, path[j].co);
GWN_vertbuf_attr_set(hair_cache->pos, attr_id->tan, curr_point, tangent);
GWN_vertbuf_attr_set(hair_cache->pos, attr_id->ind, curr_point, &i);
if (psmd != NULL) {
for (int k = 0; k < num_uv_layers; k++) {
GWN_vertbuf_attr_set(
hair_cache->pos, uv_id[k], curr_point,
(is_simple && is_child) ?
(*r_parent_uvs)[psys->child[i].parent][k] : uv[k]);
}
for (int k = 0; k < num_col_layers; k++) {
/* TODO Put the conversion outside the loop */
unsigned short scol[4];
particle_pack_mcol(
(is_simple && is_child) ?
&(*r_parent_mcol)[psys->child[i].parent][k] : &mcol[k],
scol);
GWN_vertbuf_attr_set(hair_cache->pos, col_id[k], curr_point, scol);
}
}
GWN_indexbuf_add_generic_vert(elb, curr_point);
curr_point++;
}
sub_v3_v3v3(tangent, path[path->segments].co, path[path->segments - 1].co);
int global_index = i + global_offset;
GWN_vertbuf_attr_set(hair_cache->pos, attr_id->pos, curr_point, path[path->segments].co);
GWN_vertbuf_attr_set(hair_cache->pos, attr_id->tan, curr_point, tangent);
GWN_vertbuf_attr_set(hair_cache->pos, attr_id->ind, curr_point, &global_index);
if (psmd != NULL) {
for (int k = 0; k < num_uv_layers; k++) {
GWN_vertbuf_attr_set(
hair_cache->pos, uv_id[k], curr_point,
(is_simple && is_child) ?
(*r_parent_uvs)[psys->child[i].parent][k] : uv[k]);
}
for (int k = 0; k < num_col_layers; k++) {
/* TODO Put the conversion outside the loop */
unsigned short scol[4];
particle_pack_mcol(
(is_simple && is_child) ?
&(*r_parent_mcol)[psys->child[i].parent][k] : &mcol[k],
scol);
GWN_vertbuf_attr_set(hair_cache->pos, col_id[k], curr_point, scol);
}
if (!is_simple) {
MEM_freeN(uv);
MEM_freeN(mcol);
}
}
/* Finish the segment and add restart primitive. */
GWN_indexbuf_add_generic_vert(elb, curr_point);
GWN_indexbuf_add_primitive_restart(elb);
curr_point++;
}
return curr_point;
}
static void particle_batch_cache_fill_segments_proc_pos(
ParticleCacheKey **path_cache,
const int num_path_keys,
Gwn_VertBufRaw *attr_step)
{
for (int i = 0; i < num_path_keys; i++) {
ParticleCacheKey *path = path_cache[i];
if (path->segments <= 0) {
continue;
}
float total_len = 0.0f;
float *co_prev = NULL, *seg_data_first;
for (int j = 0; j <= path->segments; j++) {
float *seg_data = (float *)GWN_vertbuf_raw_step(attr_step);
copy_v3_v3(seg_data, path[j].co);
if (co_prev) {
total_len += len_v3v3(co_prev, path[j].co);
}
else {
seg_data_first = seg_data;
}
seg_data[3] = total_len;
co_prev = path[j].co;
}
if (total_len > 0.0f) {
/* Divide by total length to have a [0-1] number. */
for (int j = 0; j <= path->segments; j++, seg_data_first += 4) {
seg_data_first[3] /= total_len;
}
}
}
}
static int particle_batch_cache_fill_segments_indices(
ParticleCacheKey **path_cache,
const int start_index,
const int num_path_keys,
const int res,
Gwn_IndexBufBuilder *elb)
{
int curr_point = start_index;
for (int i = 0; i < num_path_keys; i++) {
ParticleCacheKey *path = path_cache[i];
if (path->segments <= 0) {
continue;
}
for (int k = 0; k < res; k++) {
GWN_indexbuf_add_generic_vert(elb, curr_point++);
}
GWN_indexbuf_add_primitive_restart(elb);
}
return curr_point;
}
static int particle_batch_cache_fill_strands_data(
ParticleSystem *psys,
ParticleSystemModifierData *psmd,
ParticleCacheKey **path_cache,
const ParticleSource particle_source,
const int start_index,
const int num_path_keys,
Gwn_VertBufRaw *data_step,
float (***r_parent_uvs)[2], Gwn_VertBufRaw *uv_step, MTFace **mtfaces, int num_uv_layers,
MCol ***r_parent_mcol, Gwn_VertBufRaw *col_step, MCol **mcols, int num_col_layers)
{
const bool is_simple = (psys->part->childtype == PART_CHILD_PARTICLES);
const bool is_child = (particle_source == PARTICLE_SOURCE_CHILDREN);
if (is_simple && *r_parent_uvs == NULL) {
/* TODO(sergey): For edit mode it should be edit->totcached. */
*r_parent_uvs = MEM_callocN(
sizeof(*r_parent_uvs) * psys->totpart,
"Parent particle UVs");
}
if (is_simple && *r_parent_mcol == NULL) {
*r_parent_mcol = MEM_callocN(
sizeof(*r_parent_mcol) * psys->totpart,
"Parent particle MCol");
}
int curr_point = start_index;
for (int i = 0; i < num_path_keys; i++) {
ParticleCacheKey *path = path_cache[i];
if (path->segments <= 0) {
continue;
}
uint *seg_data = (uint *)GWN_vertbuf_raw_step(data_step);
*seg_data = (curr_point & 0xFFFFFF) | (path->segments << 24);
curr_point += path->segments + 1;
if (psmd != NULL) {
float (*uv)[2] = NULL;
MCol *mcol = NULL;
particle_calculate_uvs(
psys, psmd,
is_simple, num_uv_layers,
is_child ? psys->child[i].parent : i,
is_child ? i : -1,
mtfaces,
*r_parent_uvs, &uv);
particle_calculate_mcol(
psys, psmd,
is_simple, num_col_layers,
is_child ? psys->child[i].parent : i,
is_child ? i : -1,
mcols,
*r_parent_mcol, &mcol);
for (int k = 0; k < num_uv_layers; k++) {
float *t_uv = (float *)GWN_vertbuf_raw_step(uv_step + k);
copy_v2_v2(t_uv, uv[k]);
}
for (int k = 0; k < num_col_layers; k++) {
unsigned short *scol = (unsigned short *)GWN_vertbuf_raw_step(col_step + k);
particle_pack_mcol(
(is_simple && is_child) ?
&(*r_parent_mcol)[psys->child[i].parent][k] : &mcol[k],
scol);
}
if (!is_simple) {
MEM_freeN(uv);
MEM_freeN(mcol);
}
}
}
return curr_point;
}
static void particle_batch_cache_ensure_procedural_final_points(
ParticleHairCache *cache,
int subdiv)
{
/* Same format as point_tex. */
Gwn_VertFormat format = { 0 };
GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 4, GWN_FETCH_FLOAT);
cache->final[subdiv].proc_buf = GWN_vertbuf_create_with_format(&format);
/* Create a destination buffer for the tranform feedback. Sized appropriately */
/* Thoses are points! not line segments. */
GWN_vertbuf_data_alloc(cache->final[subdiv].proc_buf, cache->final[subdiv].strands_res * cache->strands_count);
/* Create vbo immediatly to bind to texture buffer. */
GWN_vertbuf_use(cache->final[subdiv].proc_buf);
cache->final[subdiv].proc_tex = GPU_texture_create_from_vertbuf(cache->final[subdiv].proc_buf);
}
static void particle_batch_cache_ensure_procedural_strand_data(
PTCacheEdit *edit,
ParticleSystem *psys,
ModifierData *md,
ParticleHairCache *cache)
{
int active_uv = 0;
int active_col = 0;
ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md;
if (psmd != NULL && psmd->mesh_final != NULL) {
if (CustomData_has_layer(&psmd->mesh_final->ldata, CD_MLOOPUV)) {
cache->num_uv_layers = CustomData_number_of_layers(&psmd->mesh_final->ldata, CD_MLOOPUV);
active_uv = CustomData_get_active_layer(&psmd->mesh_final->ldata, CD_MLOOPUV);
}
if (CustomData_has_layer(&psmd->mesh_final->ldata, CD_MLOOPCOL)) {
cache->num_col_layers = CustomData_number_of_layers(&psmd->mesh_final->ldata, CD_MLOOPCOL);
active_col = CustomData_get_active_layer(&psmd->mesh_final->ldata, CD_MLOOPCOL);
}
}
Gwn_VertBufRaw data_step;
Gwn_VertBufRaw uv_step[MAX_MTFACE];
Gwn_VertBufRaw col_step[MAX_MCOL];
MTFace *mtfaces[MAX_MTFACE] = {NULL};
MCol *mcols[MAX_MCOL] = {NULL};
float (**parent_uvs)[2] = NULL;
MCol **parent_mcol = NULL;
Gwn_VertFormat format_data = {0};
uint data_id = GWN_vertformat_attr_add(&format_data, "data", GWN_COMP_U32, 1, GWN_FETCH_INT);
Gwn_VertFormat format_uv = {0};
uint uv_id = GWN_vertformat_attr_add(&format_uv, "uv", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
Gwn_VertFormat format_col = {0};
uint col_id = GWN_vertformat_attr_add(&format_col, "col", GWN_COMP_U16, 4, GWN_FETCH_INT_TO_FLOAT_UNIT);
memset(cache->uv_layer_names, 0, sizeof(cache->uv_layer_names));
memset(cache->col_layer_names, 0, sizeof(cache->col_layer_names));
/* Strand Data */
cache->proc_strand_buf = GWN_vertbuf_create_with_format(&format_data);
GWN_vertbuf_data_alloc(cache->proc_strand_buf, cache->strands_count);
GWN_vertbuf_attr_get_raw_data(cache->proc_strand_buf, data_id, &data_step);
/* UV layers */
for (int i = 0; i < cache->num_uv_layers; i++) {
cache->proc_uv_buf[i] = GWN_vertbuf_create_with_format(&format_uv);
GWN_vertbuf_data_alloc(cache->proc_uv_buf[i], cache->strands_count);
GWN_vertbuf_attr_get_raw_data(cache->proc_uv_buf[i], uv_id, &uv_step[i]);
const char *name = CustomData_get_layer_name(&psmd->mesh_final->ldata, CD_MLOOPUV, i);
uint hash = BLI_ghashutil_strhash_p(name);
int n = 0;
BLI_snprintf(cache->uv_layer_names[i][n++], MAX_LAYER_NAME_LEN, "u%u", hash);
BLI_snprintf(cache->uv_layer_names[i][n++], MAX_LAYER_NAME_LEN, "a%u", hash);
if (i == active_uv) {
BLI_snprintf(cache->uv_layer_names[i][n], MAX_LAYER_NAME_LEN, "u");
}
}
/* Vertex colors */
for (int i = 0; i < cache->num_col_layers; i++) {
cache->proc_col_buf[i] = GWN_vertbuf_create_with_format(&format_col);
GWN_vertbuf_data_alloc(cache->proc_col_buf[i], cache->strands_count);
GWN_vertbuf_attr_get_raw_data(cache->proc_col_buf[i], col_id, &col_step[i]);
const char *name = CustomData_get_layer_name(&psmd->mesh_final->ldata, CD_MLOOPCOL, i);
uint hash = BLI_ghashutil_strhash_p(name);
int n = 0;
BLI_snprintf(cache->col_layer_names[i][n++], MAX_LAYER_NAME_LEN, "c%u", hash);
/* We only do vcols auto name that are not overridden by uvs */
if (CustomData_get_named_layer_index(&psmd->mesh_final->ldata, CD_MLOOPUV, name) == -1) {
BLI_snprintf(cache->col_layer_names[i][n++], MAX_LAYER_NAME_LEN, "a%u", hash);
}
if (i == active_col) {
BLI_snprintf(cache->col_layer_names[i][n], MAX_LAYER_NAME_LEN, "c");
}
}
if (cache->num_uv_layers || cache->num_col_layers) {
BKE_mesh_tessface_ensure(psmd->mesh_final);
if (cache->num_uv_layers) {
for (int j = 0; j < cache->num_uv_layers; j++) {
mtfaces[j] = (MTFace *)CustomData_get_layer_n(&psmd->mesh_final->fdata, CD_MTFACE, j);
}
}
if (cache->num_col_layers) {
for (int j = 0; j < cache->num_col_layers; j++) {
mcols[j] = (MCol *)CustomData_get_layer_n(&psmd->mesh_final->fdata, CD_MCOL, j);
}
}
}
if (edit != NULL && edit->pathcache != NULL) {
particle_batch_cache_fill_strands_data(
psys, psmd, edit->pathcache, PARTICLE_SOURCE_PARENT,
0, edit->totcached,
&data_step,
&parent_uvs, uv_step, (MTFace **)mtfaces, cache->num_uv_layers,
&parent_mcol, col_step, (MCol **)mcols, cache->num_col_layers);
}
else {
int curr_point = 0;
if ((psys->pathcache != NULL) &&
(!psys->childcache || (psys->part->draw & PART_DRAW_PARENT)))
{
curr_point = particle_batch_cache_fill_strands_data(
psys, psmd, psys->pathcache, PARTICLE_SOURCE_PARENT,
0, psys->totpart,
&data_step,
&parent_uvs, uv_step, (MTFace **)mtfaces, cache->num_uv_layers,
&parent_mcol, col_step, (MCol **)mcols, cache->num_col_layers);
}
if (psys->childcache) {
const int child_count = psys->totchild * psys->part->disp / 100;
curr_point = particle_batch_cache_fill_strands_data(
psys, psmd, psys->childcache, PARTICLE_SOURCE_CHILDREN,
curr_point, child_count,
&data_step,
&parent_uvs, uv_step, (MTFace **)mtfaces, cache->num_uv_layers,
&parent_mcol, col_step, (MCol **)mcols, cache->num_col_layers);
}
}
/* Cleanup. */
if (parent_uvs != NULL) {
/* TODO(sergey): For edit mode it should be edit->totcached. */
for (int i = 0; i < psys->totpart; i++) {
MEM_SAFE_FREE(parent_uvs[i]);
}
MEM_freeN(parent_uvs);
}
if (parent_mcol != NULL) {
for (int i = 0; i < psys->totpart; i++) {
MEM_SAFE_FREE(parent_mcol[i]);
}
MEM_freeN(parent_mcol);
}
/* Create vbo immediatly to bind to texture buffer. */
GWN_vertbuf_use(cache->proc_strand_buf);
cache->strand_tex = GPU_texture_create_from_vertbuf(cache->proc_strand_buf);
for (int i = 0; i < cache->num_uv_layers; i++) {
GWN_vertbuf_use(cache->proc_uv_buf[i]);
cache->uv_tex[i] = GPU_texture_create_from_vertbuf(cache->proc_uv_buf[i]);
}
for (int i = 0; i < cache->num_col_layers; i++) {
GWN_vertbuf_use(cache->proc_col_buf[i]);
cache->col_tex[i] = GPU_texture_create_from_vertbuf(cache->proc_col_buf[i]);
}
}
static void particle_batch_cache_ensure_procedural_indices(
PTCacheEdit *edit,
ParticleSystem *psys,
ParticleHairCache *cache,
int thickness_res,
int subdiv)
{
BLI_assert(thickness_res <= MAX_THICKRES); /* Cylinder strip not currently supported. */
if (cache->final[subdiv].proc_hairs[thickness_res - 1] != NULL) {
return;
}
int verts_per_hair = cache->final[subdiv].strands_res * thickness_res;
/* +1 for primitive restart */
int element_count = (verts_per_hair + 1) * cache->strands_count;
Gwn_PrimType prim_type = (thickness_res == 1) ? GWN_PRIM_LINE_STRIP : GWN_PRIM_TRI_STRIP;
static Gwn_VertFormat format = { 0 };
GWN_vertformat_clear(&format);
/* initialize vertex format */
GWN_vertformat_attr_add(&format, "dummy", GWN_COMP_U8, 1, GWN_FETCH_INT_TO_FLOAT_UNIT);
Gwn_VertBuf *vbo = GWN_vertbuf_create_with_format(&format);
GWN_vertbuf_data_alloc(vbo, 1);
Gwn_IndexBufBuilder elb;
GWN_indexbuf_init_ex(&elb, prim_type, element_count, element_count, true);
if (edit != NULL && edit->pathcache != NULL) {
particle_batch_cache_fill_segments_indices(
edit->pathcache, 0, edit->totcached, verts_per_hair, &elb);
}
else {
int curr_point = 0;
if ((psys->pathcache != NULL) &&
(!psys->childcache || (psys->part->draw & PART_DRAW_PARENT)))
{
curr_point = particle_batch_cache_fill_segments_indices(
psys->pathcache, 0, psys->totpart, verts_per_hair, &elb);
}
if (psys->childcache) {
const int child_count = psys->totchild * psys->part->disp / 100;
curr_point = particle_batch_cache_fill_segments_indices(
psys->childcache, curr_point, child_count, verts_per_hair, &elb);
}
}
cache->final[subdiv].proc_hairs[thickness_res - 1] = GWN_batch_create_ex(
prim_type,
vbo,
GWN_indexbuf_build(&elb),
GWN_BATCH_OWNS_VBO | GWN_BATCH_OWNS_INDEX);
}
static void particle_batch_cache_ensure_procedural_pos(
PTCacheEdit *edit,
ParticleSystem *psys,
ParticleHairCache *cache)
{
if (cache->proc_point_buf != NULL) {
return;
}
/* initialize vertex format */
Gwn_VertFormat format = {0};
uint pos_id = GWN_vertformat_attr_add(&format, "posTime", GWN_COMP_F32, 4, GWN_FETCH_FLOAT);
cache->proc_point_buf = GWN_vertbuf_create_with_format(&format);
GWN_vertbuf_data_alloc(cache->proc_point_buf, cache->point_count);
Gwn_VertBufRaw pos_step;
GWN_vertbuf_attr_get_raw_data(cache->proc_point_buf, pos_id, &pos_step);
if (edit != NULL && edit->pathcache != NULL) {
particle_batch_cache_fill_segments_proc_pos(
edit->pathcache,
edit->totcached,
&pos_step);
}
else {
if ((psys->pathcache != NULL) &&
(!psys->childcache || (psys->part->draw & PART_DRAW_PARENT)))
{
particle_batch_cache_fill_segments_proc_pos(
psys->pathcache,
psys->totpart,
&pos_step);
}
if (psys->childcache) {
const int child_count = psys->totchild * psys->part->disp / 100;
particle_batch_cache_fill_segments_proc_pos(
psys->childcache,
child_count,
&pos_step);
}
}
/* Create vbo immediatly to bind to texture buffer. */
GWN_vertbuf_use(cache->proc_point_buf);
cache->point_tex = GPU_texture_create_from_vertbuf(cache->proc_point_buf);
}
static void particle_batch_cache_ensure_pos_and_seg(
PTCacheEdit *edit,
ParticleSystem *psys,
ModifierData *md,
ParticleHairCache *hair_cache)
{
if (hair_cache->pos != NULL && hair_cache->indices != NULL) {
return;
}
int curr_point = 0;
ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md;
GWN_VERTBUF_DISCARD_SAFE(hair_cache->pos);
GWN_INDEXBUF_DISCARD_SAFE(hair_cache->indices);
static Gwn_VertFormat format = { 0 };
HairAttributeID attr_id;
uint *uv_id = NULL;
uint *col_id = NULL;
int num_uv_layers = 0;
int num_col_layers = 0;
int active_uv = 0;
int active_col = 0;
MTFace **mtfaces = NULL;
MCol **mcols = NULL;
float (**parent_uvs)[2] = NULL;
MCol **parent_mcol = NULL;
if (psmd != NULL) {
if (CustomData_has_layer(&psmd->mesh_final->ldata, CD_MLOOPUV)) {
num_uv_layers = CustomData_number_of_layers(&psmd->mesh_final->ldata, CD_MLOOPUV);
active_uv = CustomData_get_active_layer(&psmd->mesh_final->ldata, CD_MLOOPUV);
}
if (CustomData_has_layer(&psmd->mesh_final->ldata, CD_MLOOPCOL)) {
num_col_layers = CustomData_number_of_layers(&psmd->mesh_final->ldata, CD_MLOOPCOL);
active_col = CustomData_get_active_layer(&psmd->mesh_final->ldata, CD_MLOOPCOL);
}
}
GWN_vertformat_clear(&format);
/* initialize vertex format */
attr_id.pos = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
attr_id.tan = GWN_vertformat_attr_add(&format, "nor", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
attr_id.ind = GWN_vertformat_attr_add(&format, "ind", GWN_COMP_I32, 1, GWN_FETCH_INT);
if (psmd) {
uv_id = MEM_mallocN(sizeof(*uv_id) * num_uv_layers, "UV attrib format");
col_id = MEM_mallocN(sizeof(*col_id) * num_col_layers, "Col attrib format");
for (int i = 0; i < num_uv_layers; i++) {
const char *name = CustomData_get_layer_name(&psmd->mesh_final->ldata, CD_MLOOPUV, i);
char uuid[32];
BLI_snprintf(uuid, sizeof(uuid), "u%u", BLI_ghashutil_strhash_p(name));
uv_id[i] = GWN_vertformat_attr_add(&format, uuid, GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
if (i == active_uv) {
GWN_vertformat_alias_add(&format, "u");
}
}
for (int i = 0; i < num_uv_layers; i++) {
const char *name = CustomData_get_layer_name(&psmd->mesh_final->ldata, CD_MLOOPUV, i);
char uuid[32];
BLI_snprintf(uuid, sizeof(uuid), "c%u", BLI_ghashutil_strhash_p(name));
col_id[i] = GWN_vertformat_attr_add(&format, uuid, GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
if (i == active_col) {
GWN_vertformat_alias_add(&format, "c");
}
}
}
hair_cache->pos = GWN_vertbuf_create_with_format(&format);
GWN_vertbuf_data_alloc(hair_cache->pos, hair_cache->point_count);
Gwn_IndexBufBuilder elb;
GWN_indexbuf_init_ex(
&elb,
GWN_PRIM_LINE_STRIP,
hair_cache->elems_count, hair_cache->point_count,
true);
if (num_uv_layers || num_col_layers) {
BKE_mesh_tessface_ensure(psmd->mesh_final);
if (num_uv_layers) {
mtfaces = MEM_mallocN(sizeof(*mtfaces) * num_uv_layers, "Faces UV layers");
for (int i = 0; i < num_uv_layers; i++) {
mtfaces[i] = (MTFace *)CustomData_get_layer_n(&psmd->mesh_final->fdata, CD_MTFACE, i);
}
}
if (num_col_layers) {
mcols = MEM_mallocN(sizeof(*mcols) * num_col_layers, "Color layers");
for (int i = 0; i < num_col_layers; i++) {
mcols[i] = (MCol *)CustomData_get_layer_n(&psmd->mesh_final->fdata, CD_MCOL, i);
}
}
}
if (edit != NULL && edit->pathcache != NULL) {
curr_point = particle_batch_cache_fill_segments(
psys, psmd, edit->pathcache, PARTICLE_SOURCE_PARENT,
0, 0, edit->totcached,
num_uv_layers, num_col_layers, mtfaces, mcols, uv_id, col_id, &parent_uvs, &parent_mcol,
&elb, &attr_id, hair_cache);
}
else {
if ((psys->pathcache != NULL) &&
(!psys->childcache || (psys->part->draw & PART_DRAW_PARENT)))
{
curr_point = particle_batch_cache_fill_segments(
psys, psmd, psys->pathcache, PARTICLE_SOURCE_PARENT,
0, 0, psys->totpart,
num_uv_layers, num_col_layers, mtfaces, mcols, uv_id, col_id, &parent_uvs, &parent_mcol,
&elb, &attr_id, hair_cache);
}
if (psys->childcache != NULL) {
const int child_count = psys->totchild * psys->part->disp / 100;
curr_point = particle_batch_cache_fill_segments(
psys, psmd, psys->childcache, PARTICLE_SOURCE_CHILDREN,
psys->totpart, curr_point, child_count,
num_uv_layers, num_col_layers, mtfaces, mcols, uv_id, col_id, &parent_uvs, &parent_mcol,
&elb, &attr_id, hair_cache);
}
}
/* Cleanup. */
if (parent_uvs != NULL) {
/* TODO(sergey): For edit mode it should be edit->totcached. */
for (int i = 0; i < psys->totpart; i++) {
MEM_SAFE_FREE(parent_uvs[i]);
}
MEM_freeN(parent_uvs);
}
if (parent_mcol != NULL) {
for (int i = 0; i < psys->totpart; i++) {
MEM_SAFE_FREE(parent_mcol[i]);
}
MEM_freeN(parent_mcol);
}
if (num_uv_layers) {
MEM_freeN(mtfaces);
}
if (num_col_layers) {
MEM_freeN(mcols);
}
if (psmd != NULL) {
MEM_freeN(uv_id);
}
hair_cache->indices = GWN_indexbuf_build(&elb);
}
static void particle_batch_cache_ensure_pos(
Object *object,
ParticleSystem *psys,
ParticlePointCache *point_cache)
{
if (point_cache->pos != NULL) {
return;
}
static Gwn_VertFormat format = { 0 };
static unsigned pos_id, rot_id, val_id;
int i, curr_point;
ParticleData *pa;
ParticleKey state;
ParticleSimulationData sim = {NULL};
const DRWContextState *draw_ctx = DRW_context_state_get();
sim.depsgraph = draw_ctx->depsgraph;
sim.scene = draw_ctx->scene;
sim.ob = object;
sim.psys = psys;
sim.psmd = psys_get_modifier(object, psys);
if (psys->part->phystype == PART_PHYS_KEYED) {
if (psys->flag & PSYS_KEYED) {
psys_count_keyed_targets(&sim);
if (psys->totkeyed == 0)
return;
}
}
GWN_VERTBUF_DISCARD_SAFE(point_cache->pos);
if (format.attrib_ct == 0) {
/* initialize vertex format */
pos_id = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 4, GWN_FETCH_FLOAT);
rot_id = GWN_vertformat_attr_add(&format, "rot", GWN_COMP_F32, 4, GWN_FETCH_FLOAT);
val_id = GWN_vertformat_attr_add(&format, "val", GWN_COMP_F32, 1, GWN_FETCH_FLOAT);
}
point_cache->pos = GWN_vertbuf_create_with_format(&format);
GWN_vertbuf_data_alloc(point_cache->pos, psys->totpart);
for (curr_point = 0, i = 0, pa = psys->particles; i < psys->totpart; i++, pa++) {
state.time = DEG_get_ctime(draw_ctx->depsgraph);
if (!psys_get_particle_state(&sim, i, &state, 0)) {
continue;
}
float val;
GWN_vertbuf_attr_set(point_cache->pos, pos_id, curr_point, pa->state.co);
GWN_vertbuf_attr_set(point_cache->pos, rot_id, curr_point, pa->state.rot);
switch (psys->part->draw_col) {
case PART_DRAW_COL_VEL:
val = len_v3(pa->state.vel) / psys->part->color_vec_max;
break;
case PART_DRAW_COL_ACC:
val = len_v3v3(
pa->state.vel,
pa->prev_state.vel) / ((pa->state.time - pa->prev_state.time) * psys->part->color_vec_max);
break;
default:
val = -1.0f;
break;
}
GWN_vertbuf_attr_set(point_cache->pos, val_id, curr_point, &val);
curr_point++;
}
if (curr_point != psys->totpart) {
GWN_vertbuf_data_resize(point_cache->pos, curr_point);
}
}
static void drw_particle_update_ptcache_edit(
Object *object_eval,
ParticleSystem *psys,
PTCacheEdit *edit)
{
if (edit->psys == NULL) {
return;
}
/* NOTE: Get flag from particle system coming from drawing object.
* this is where depsgraph will be setting flags to.
*/
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene_orig = (Scene *)DEG_get_original_id(&draw_ctx->scene->id);
Object *object_orig = DEG_get_original_object(object_eval);
if (psys->flag & PSYS_HAIR_UPDATED) {
PE_update_object(draw_ctx->depsgraph, scene_orig, object_orig, 0);
psys->flag &= ~PSYS_HAIR_UPDATED;
}
if (edit->pathcache == NULL) {
Depsgraph *depsgraph = draw_ctx->depsgraph;
psys_cache_edit_paths(
depsgraph,
scene_orig, object_orig,
edit,
DEG_get_ctime(depsgraph),
DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
}
}
static void drw_particle_update_ptcache(
Object *object_eval,
ParticleSystem *psys)
{
if ((object_eval->mode & OB_MODE_PARTICLE_EDIT) == 0) {
return;
}
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene_orig = (Scene *)DEG_get_original_id(&draw_ctx->scene->id);
Object *object_orig = DEG_get_original_object(object_eval);
PTCacheEdit *edit = PE_create_current(
draw_ctx->depsgraph, scene_orig, object_orig);
if (edit != NULL) {
drw_particle_update_ptcache_edit(object_eval, psys, edit);
}
}
typedef struct ParticleDrawSource {
Object *object;
ParticleSystem *psys;
ModifierData *md;
PTCacheEdit *edit;
} ParticleDrawSource;
static void drw_particle_get_hair_source(
Object *object,
ParticleSystem *psys,
ModifierData *md,
PTCacheEdit *edit,
ParticleDrawSource *r_draw_source)
{
r_draw_source->object = object;
r_draw_source->psys = psys;
r_draw_source->md = md;
r_draw_source->edit = edit;
if ((object->mode & OB_MODE_PARTICLE_EDIT) != 0) {
r_draw_source->object = DEG_get_original_object(object);
r_draw_source->psys = psys_orig_get(psys);
if (md != NULL) {
r_draw_source->md = modifiers_findByName(r_draw_source->object, md->name);
}
}
}
Gwn_Batch *DRW_particles_batch_cache_get_hair(
Object *object,
ParticleSystem *psys,
ModifierData *md)
{
ParticleBatchCache *cache = particle_batch_cache_get(psys);
if (cache->hair.hairs == NULL) {
drw_particle_update_ptcache(object, psys);
ParticleDrawSource source;
drw_particle_get_hair_source(object, psys, md, NULL, &source);
ensure_seg_pt_count(source.edit, source.psys, &cache->hair);
particle_batch_cache_ensure_pos_and_seg(source.edit, source.psys, source.md, &cache->hair);
cache->hair.hairs = GWN_batch_create(
GWN_PRIM_LINE_STRIP,
cache->hair.pos,
cache->hair.indices);
}
return cache->hair.hairs;
}
Gwn_Batch *DRW_particles_batch_cache_get_dots(Object *object, ParticleSystem *psys)
{
ParticleBatchCache *cache = particle_batch_cache_get(psys);
if (cache->point.points == NULL) {
particle_batch_cache_ensure_pos(object, psys, &cache->point);
cache->point.points = GWN_batch_create(GWN_PRIM_POINTS, cache->point.pos, NULL);
}
return cache->point.points;
}
Gwn_Batch *DRW_particles_batch_cache_get_edit_strands(
Object *object,
ParticleSystem *psys,
PTCacheEdit *edit)
{
ParticleBatchCache *cache = particle_batch_cache_get(psys);
if (cache->edit_hair.hairs != NULL) {
return cache->edit_hair.hairs;
}
drw_particle_update_ptcache_edit(object, psys, edit);
ensure_seg_pt_count(edit, psys, &cache->edit_hair);
particle_batch_cache_ensure_pos_and_seg(edit, psys, NULL, &cache->edit_hair);
cache->edit_hair.hairs = GWN_batch_create(
GWN_PRIM_LINE_STRIP,
cache->edit_hair.pos,
cache->edit_hair.indices);
return cache->edit_hair.hairs;
}
static void ensure_edit_inner_points_count(
const PTCacheEdit *edit,
ParticleBatchCache *cache)
{
if (cache->edit_inner_pos != NULL) {
return;
}
cache->edit_inner_point_count = 0;
for (int point_index = 0; point_index < edit->totpoint; point_index++) {
const PTCacheEditPoint *point = &edit->points[point_index];
BLI_assert(point->totkey >= 1);
cache->edit_inner_point_count += (point->totkey - 1);
}
}
static void edit_colors_get(
PTCacheEdit *edit,
float selected_color[4],
float normal_color[4])
{
rgb_uchar_to_float(selected_color, edit->sel_col);
rgb_uchar_to_float(normal_color, edit->nosel_col);
selected_color[3] = 1.0f;
normal_color[3] = 1.0f;
}
static void particle_batch_cache_ensure_edit_inner_pos(
PTCacheEdit *edit,
ParticleBatchCache *cache)
{
if (cache->edit_inner_pos != NULL) {
return;
}
static Gwn_VertFormat format = { 0 };
static unsigned pos_id, color_id;
GWN_VERTBUF_DISCARD_SAFE(cache->edit_inner_pos);
if (format.attrib_ct == 0) {
/* initialize vertex format */
pos_id = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
color_id = GWN_vertformat_attr_add(&format, "color", GWN_COMP_F32, 4, GWN_FETCH_FLOAT);
}
cache->edit_inner_pos = GWN_vertbuf_create_with_format(&format);
GWN_vertbuf_data_alloc(cache->edit_inner_pos, cache->edit_inner_point_count);
float selected_color[4], normal_color[4];
edit_colors_get(edit, selected_color, normal_color);
int global_key_index = 0;
for (int point_index = 0; point_index < edit->totpoint; point_index++) {
const PTCacheEditPoint *point = &edit->points[point_index];
for (int key_index = 0; key_index < point->totkey - 1; key_index++) {
PTCacheEditKey *key = &point->keys[key_index];
GWN_vertbuf_attr_set(cache->edit_inner_pos, pos_id, global_key_index, key->world_co);
if (key->flag & PEK_SELECT) {
GWN_vertbuf_attr_set(cache->edit_inner_pos, color_id, global_key_index, selected_color);
}
else {
GWN_vertbuf_attr_set(cache->edit_inner_pos, color_id, global_key_index, normal_color);
}
global_key_index++;
}
}
}
Gwn_Batch *DRW_particles_batch_cache_get_edit_inner_points(
Object *object,
ParticleSystem *psys,
PTCacheEdit *edit)
{
ParticleBatchCache *cache = particle_batch_cache_get(psys);
if (cache->edit_inner_points != NULL) {
return cache->edit_inner_points;
}
drw_particle_update_ptcache_edit(object, psys, edit);
ensure_edit_inner_points_count(edit, cache);
particle_batch_cache_ensure_edit_inner_pos(edit, cache);
cache->edit_inner_points = GWN_batch_create(
GWN_PRIM_POINTS,
cache->edit_inner_pos,
NULL);
return cache->edit_inner_points;
}
static void ensure_edit_tip_points_count(
const PTCacheEdit *edit,
ParticleBatchCache *cache)
{
if (cache->edit_tip_pos != NULL) {
return;
}
cache->edit_tip_point_count = edit->totpoint;
}
static void particle_batch_cache_ensure_edit_tip_pos(
PTCacheEdit *edit,
ParticleBatchCache *cache)
{
if (cache->edit_tip_pos != NULL) {
return;
}
static Gwn_VertFormat format = { 0 };
static unsigned pos_id, color_id;
GWN_VERTBUF_DISCARD_SAFE(cache->edit_tip_pos);
if (format.attrib_ct == 0) {
/* initialize vertex format */
pos_id = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
color_id = GWN_vertformat_attr_add(&format, "color", GWN_COMP_F32, 4, GWN_FETCH_FLOAT);
}
cache->edit_tip_pos = GWN_vertbuf_create_with_format(&format);
GWN_vertbuf_data_alloc(cache->edit_tip_pos, cache->edit_tip_point_count);
float selected_color[4], normal_color[4];
edit_colors_get(edit, selected_color, normal_color);
for (int point_index = 0; point_index < edit->totpoint; point_index++) {
const PTCacheEditPoint *point = &edit->points[point_index];
PTCacheEditKey *key = &point->keys[point->totkey - 1];
GWN_vertbuf_attr_set(cache->edit_tip_pos, pos_id, point_index, key->world_co);
if (key->flag & PEK_SELECT) {
GWN_vertbuf_attr_set(cache->edit_tip_pos, color_id, point_index, selected_color);
}
else {
GWN_vertbuf_attr_set(cache->edit_tip_pos, color_id, point_index, normal_color);
}
}
}
Gwn_Batch *DRW_particles_batch_cache_get_edit_tip_points(
Object *object,
ParticleSystem *psys,
PTCacheEdit *edit)
{
ParticleBatchCache *cache = particle_batch_cache_get(psys);
if (cache->edit_tip_points != NULL) {
return cache->edit_tip_points;
}
drw_particle_update_ptcache_edit(object, psys, edit);
ensure_edit_tip_points_count(edit, cache);
particle_batch_cache_ensure_edit_tip_pos(edit, cache);
cache->edit_tip_points = GWN_batch_create(
GWN_PRIM_POINTS,
cache->edit_tip_pos,
NULL);
return cache->edit_tip_points;
}
/* Ensure all textures and buffers needed for GPU accelerated drawing. */
bool particles_ensure_procedural_data(
Object *object,
ParticleSystem *psys,
ModifierData *md,
ParticleHairCache **r_hair_cache,
int subdiv,
int thickness_res)
{
bool need_ft_update = false;
drw_particle_update_ptcache(object, psys);
ParticleDrawSource source;
drw_particle_get_hair_source(object, psys, md, NULL, &source);
ParticleSettings *part = source.psys->part;
ParticleBatchCache *cache = particle_batch_cache_get(source.psys);
*r_hair_cache = &cache->hair;
(*r_hair_cache)->final[subdiv].strands_res = 1 << (part->draw_step + subdiv);
/* Refreshed on combing and simulation. */
if ((*r_hair_cache)->proc_point_buf == NULL) {
ensure_seg_pt_count(source.edit, source.psys, &cache->hair);
particle_batch_cache_ensure_procedural_pos(source.edit, source.psys, &cache->hair);
need_ft_update = true;
}
/* Refreshed if active layer or custom data changes. */
if ((*r_hair_cache)->strand_tex == NULL) {
particle_batch_cache_ensure_procedural_strand_data(source.edit, source.psys, source.md, &cache->hair);
}
/* Refreshed only on subdiv count change. */
if ((*r_hair_cache)->final[subdiv].proc_buf == NULL) {
particle_batch_cache_ensure_procedural_final_points(&cache->hair, subdiv);
need_ft_update = true;
}
if ((*r_hair_cache)->final[subdiv].proc_hairs[thickness_res - 1] == NULL) {
particle_batch_cache_ensure_procedural_indices(source.edit, source.psys, &cache->hair, thickness_res, subdiv);
}
return need_ft_update;
}