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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/draw/intern/draw_cache_impl_particles.c')
-rw-r--r--source/blender/draw/intern/draw_cache_impl_particles.c1587
1 files changed, 1587 insertions, 0 deletions
diff --git a/source/blender/draw/intern/draw_cache_impl_particles.c b/source/blender/draw/intern/draw_cache_impl_particles.c
new file mode 100644
index 00000000000..d1e05b525ef
--- /dev/null
+++ b/source/blender/draw/intern/draw_cache_impl_particles.c
@@ -0,0 +1,1587 @@
+/*
+ * ***** 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);
+ }
+}
+
+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;
+}