Blender V5.0
draw_cache_impl_particles.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2017 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include "BLI_color.hh"
13#include "DNA_curves_types.h"
14#include "DNA_scene_types.h"
15#include "DRW_render.hh"
16
17#include "MEM_guardedalloc.h"
18
19#include "BLI_alloca.h"
20#include "BLI_math_color.h"
21#include "BLI_math_vector.h"
22#include "BLI_offset_indices.hh"
23#include "BLI_string_utf8.h"
24#include "BLI_utildefines.h"
25
27#include "DNA_mesh_types.h"
28#include "DNA_meshdata_types.h"
29#include "DNA_modifier_types.h"
30#include "DNA_particle_types.h"
31
32#include "BKE_customdata.hh"
33#include "BKE_mesh.hh"
35#include "BKE_particle.h"
36#include "BKE_pointcache.h"
37
38#include "ED_particle.hh"
39
40#include "GPU_batch.hh"
41#include "GPU_capabilities.hh"
42#include "GPU_material.hh"
43
45
47
48#include "draw_attributes.hh"
49#include "draw_cache_impl.hh" /* own include */
50#include "draw_hair_private.hh"
51
52namespace blender::draw {
53
55
56/* ---------------------------------------------------------------------- */
57/* Particle gpu::Batch Cache */
58
60 /* Output of the subdivision stage: vertex buff sized to subdiv level. */
62
63 /* Just contains a huge index buffer used to draw the final hair. */
64 blender::gpu::Batch *proc_hairs[MAX_THICKRES];
65
66 int strands_res; /* points per hair, at least 2 */
67};
68
72 blender::gpu::Batch *hairs;
76
77 /* Equivalent to the new Curves data structure.
78 * Allows to create the eval cache. */
81
83};
84
91
93 /* Object mode strands for hair and points for particle,
94 * strands for paths when in edit mode.
95 */
96 ParticleHairCache hair; /* Used for hair strands */
97 ParticlePointCache point; /* Used for particle points. */
98
99 /* Control points when in edit mode. */
101
103 gpu::Batch *edit_strands;
104
106 gpu::Batch *edit_inner_points;
108
110 gpu::Batch *edit_tip_points;
112
113 /* Settings to determine if cache is invalid. */
116};
117
118/* gpu::Batch cache management. */
119
125
127 float pos[3];
129};
130
131static const GPUVertFormat *edit_points_vert_format_get(uint *r_pos_id, uint *r_selection_id)
132{
133 static uint pos_id, selection_id;
134 static const GPUVertFormat edit_point_format = [&]() {
136 pos_id = GPU_vertformat_attr_add(&format, "pos", gpu::VertAttrType::SFLOAT_32_32_32);
137 selection_id = GPU_vertformat_attr_add(&format, "selection", gpu::VertAttrType::SFLOAT_32);
138 return format;
139 }();
140 *r_pos_id = pos_id;
141 *r_selection_id = selection_id;
142 return &edit_point_format;
143}
144
146{
147 ParticleBatchCache *cache = static_cast<ParticleBatchCache *>(psys->batch_cache);
148
149 if (cache == nullptr) {
150 return false;
151 }
152
153 if (cache->is_dirty == false) {
154 return true;
155 }
156
157 return false;
158
159 return true;
160}
161
163{
164 ParticleBatchCache *cache = static_cast<ParticleBatchCache *>(psys->batch_cache);
165
166 if (!cache) {
167 cache = MEM_new<ParticleBatchCache>(__func__);
168 psys->batch_cache = cache;
169 }
170 else {
171 cache->edit_hair.eval_cache = {};
172 cache->hair.eval_cache = {};
173 }
174
175 cache->is_dirty = false;
176}
177
179{
180 if (!particle_batch_cache_valid(psys)) {
183 }
184 return static_cast<ParticleBatchCache *>(psys->batch_cache);
185}
186
188{
189 ParticleBatchCache *cache = static_cast<ParticleBatchCache *>(psys->batch_cache);
190 if (cache == nullptr) {
191 return;
192 }
193 switch (mode) {
195 cache->is_dirty = true;
196 break;
197 default:
198 BLI_assert(0);
199 }
200}
201
203{
204 GPU_BATCH_DISCARD_SAFE(point_cache->points);
205 GPU_VERTBUF_DISCARD_SAFE(point_cache->pos);
206}
207
209{
210 /* TODO: more granular update tagging. */
211
212 /* "Normal" legacy hairs */
213 GPU_BATCH_DISCARD_SAFE(hair_cache->hairs);
214 GPU_VERTBUF_DISCARD_SAFE(hair_cache->pos);
216
218 hair_cache->points_by_curve_storage.clear();
219
220 hair_cache->eval_cache.clear();
221}
222
224{
225 ParticleBatchCache *cache = static_cast<ParticleBatchCache *>(psys->batch_cache);
226 if (!cache) {
227 return;
228 }
229
230 /* All memory allocated by `cache` must be freed. */
231
233
236
241}
242
244{
246 ParticleBatchCache *batch_cache = static_cast<ParticleBatchCache *>(psys->batch_cache);
247 MEM_delete(batch_cache);
248 psys->batch_cache = nullptr;
249}
250
252{
253 for (const auto &particle : parent) {
254 callback(Span<ParticleCacheKey>(particle, particle->segments + 1));
255 }
256 for (const auto &particle : children) {
257 callback(Span<ParticleCacheKey>(particle, particle->segments + 1));
258 }
259}
260
262{
263 if (edit && edit->pathcache) {
264 /* Edit particles only display their parent. */
265 return {{edit->pathcache, edit->totcached}, {}};
266 }
267
268 ParticleSpans spans;
269 const bool display_parent = !psys->childcache || (psys->part->draw & PART_DRAW_PARENT);
270 if (psys->pathcache && display_parent) {
271 spans.parent = {psys->pathcache, psys->totpart};
272 }
273
274 if (psys->childcache) {
275 spans.children = {psys->childcache, psys->totchild * psys->part->disp / 100};
276 }
277 return spans;
278}
279
281{
282 if (!points_by_curve_storage_.is_empty()) {
283 return points_by_curve_storage_.as_span();
284 }
285
286 int total = 0;
287 points_by_curve_storage_.append(total);
289 total += strand.size();
290 points_by_curve_storage_.append(total);
291 });
292 return points_by_curve_storage_.as_span();
293}
294
296{
297 if (additional_subdivision_ == 0) {
298 return points_by_curve();
299 }
300
301 if (!evaluated_points_by_curve_storage_.is_empty()) {
302 return evaluated_points_by_curve_storage_.as_span();
303 }
304 int segment_multiplier = this->resolution();
305
306 int total = 0;
307 evaluated_points_by_curve_storage_.append(total);
309 int size = strand.size();
310 total += (size > 1) ? size * segment_multiplier : 1;
311 evaluated_points_by_curve_storage_.append(total);
312 });
313 return evaluated_points_by_curve_storage_.as_span();
314}
315
317 const int num_path_cache_keys,
318 ParticleHairCache *hair_cache)
319{
320 for (int i = 0; i < num_path_cache_keys; i++) {
321 ParticleCacheKey *path = pathcache[i];
322 if (path->segments > 0) {
323 hair_cache->strands_len++;
324 hair_cache->elems_len += path->segments + 2;
325 hair_cache->point_len += path->segments + 1;
326 }
327 }
328}
329
331 ParticleSystem *psys,
332 ParticleHairCache *hair_cache)
333{
334 if (hair_cache->pos != nullptr && hair_cache->indices != nullptr) {
335 return;
336 }
337
338 hair_cache->strands_len = 0;
339 hair_cache->elems_len = 0;
340 hair_cache->point_len = 0;
341
342 if (edit != nullptr && edit->pathcache != nullptr) {
343 count_cache_segment_keys(edit->pathcache, edit->totcached, hair_cache);
344 }
345 else {
346 if (psys->pathcache && (!psys->childcache || (psys->part->draw & PART_DRAW_PARENT))) {
347 count_cache_segment_keys(psys->pathcache, psys->totpart, hair_cache);
348 }
349 if (psys->childcache) {
350 const int child_count = psys->totchild * psys->part->disp / 100;
351 count_cache_segment_keys(psys->childcache, child_count, hair_cache);
352 }
353 }
354}
355
356static void particle_pack_mcol(MCol *mcol, ushort r_scol[3])
357{
358 /* Convert to linear ushort and swizzle */
363 r_scol[0] = unit_float_to_ushort_clamp(col[2]);
364 r_scol[1] = unit_float_to_ushort_clamp(col[1]);
365 r_scol[2] = unit_float_to_ushort_clamp(col[0]);
366}
367
368/* Used by parent particles and simple children. */
371 const int num_uv_layers,
372 const int parent_index,
373 const MTFace **mtfaces,
374 float (*r_uv)[2])
375{
376 if (psmd == nullptr) {
377 return;
378 }
379 const int emit_from = psmd->psys->part->from;
380 if (!ELEM(emit_from, PART_FROM_FACE, PART_FROM_VOLUME)) {
381 return;
382 }
383 ParticleData *particle = &psys->particles[parent_index];
384 int num = particle->num_dmcache;
386 if (particle->num < psmd->mesh_final->totface_legacy) {
387 num = particle->num;
388 }
389 }
391 const MFace *mfaces = static_cast<const MFace *>(
393 if (UNLIKELY(mfaces == nullptr)) {
395 "A mesh with polygons should always have a generated 'CD_MFACE' layer!");
396 return;
397 }
398 const MFace *mface = &mfaces[num];
399 for (int j = 0; j < num_uv_layers; j++) {
400 psys_interpolate_uvs(mtfaces[j] + num, mface->v4, particle->fuv, r_uv[j]);
401 }
402 }
403}
404
407 const int num_col_layers,
408 const int parent_index,
409 const MCol **mcols,
410 MCol *r_mcol)
411{
412 if (psmd == nullptr) {
413 return;
414 }
415 const int emit_from = psmd->psys->part->from;
416 if (!ELEM(emit_from, PART_FROM_FACE, PART_FROM_VOLUME)) {
417 return;
418 }
419 ParticleData *particle = &psys->particles[parent_index];
420 int num = particle->num_dmcache;
422 if (particle->num < psmd->mesh_final->totface_legacy) {
423 num = particle->num;
424 }
425 }
427 const MFace *mfaces = static_cast<const MFace *>(
429 if (UNLIKELY(mfaces == nullptr)) {
431 "A mesh with polygons should always have a generated 'CD_MFACE' layer!");
432 return;
433 }
434 const MFace *mface = &mfaces[num];
435 for (int j = 0; j < num_col_layers; j++) {
436 /* CustomDataLayer CD_MCOL has 4 structs per face. */
437 psys_interpolate_mcol(mcols[j] + num * 4, mface->v4, particle->fuv, &r_mcol[j]);
438 }
439 }
440}
441
442/* Used by interpolated children. */
445 const int num_uv_layers,
446 const int child_index,
447 const MTFace **mtfaces,
448 float (*r_uv)[2])
449{
450 if (psmd == nullptr) {
451 return;
452 }
453 const int emit_from = psmd->psys->part->from;
454 if (!ELEM(emit_from, PART_FROM_FACE, PART_FROM_VOLUME)) {
455 return;
456 }
457 ChildParticle *particle = &psys->child[child_index];
458 int num = particle->num;
459 if (num != DMCACHE_NOTFOUND) {
460 const MFace *mfaces = static_cast<const MFace *>(
462 const MFace *mface = &mfaces[num];
463 for (int j = 0; j < num_uv_layers; j++) {
464 psys_interpolate_uvs(mtfaces[j] + num, mface->v4, particle->fuv, r_uv[j]);
465 }
466 }
467}
468
471 const int num_col_layers,
472 const int child_index,
473 const MCol **mcols,
474 MCol *r_mcol)
475{
476 if (psmd == nullptr) {
477 return;
478 }
479 const int emit_from = psmd->psys->part->from;
480 if (!ELEM(emit_from, PART_FROM_FACE, PART_FROM_VOLUME)) {
481 return;
482 }
483 ChildParticle *particle = &psys->child[child_index];
484 int num = particle->num;
485 if (num != DMCACHE_NOTFOUND) {
486 const MFace *mfaces = static_cast<const MFace *>(
488 const MFace *mface = &mfaces[num];
489 for (int j = 0; j < num_col_layers; j++) {
490 /* CustomDataLayer CD_MCOL has 4 structs per face. */
491 psys_interpolate_mcol(mcols[j] + num * 4, mface->v4, particle->fuv, &r_mcol[j]);
492 }
493 }
494}
495
498 const bool is_simple,
499 const int num_uv_layers,
500 const int parent_index,
501 const int child_index,
502 const MTFace **mtfaces,
503 float (**r_parent_uvs)[2],
504 float (**r_uv)[2])
505{
506 if (psmd == nullptr) {
507 return;
508 }
509 if (is_simple) {
510 if (r_parent_uvs[parent_index] != nullptr) {
511 *r_uv = r_parent_uvs[parent_index];
512 }
513 else {
514 *r_uv = MEM_calloc_arrayN<float[2]>(num_uv_layers, "Particle UVs");
515 }
516 }
517 else {
518 *r_uv = MEM_calloc_arrayN<float[2]>(num_uv_layers, "Particle UVs");
519 }
520 if (child_index == -1) {
521 /* Calculate UVs for parent particles. */
522 if (is_simple) {
523 r_parent_uvs[parent_index] = *r_uv;
524 }
525 particle_calculate_parent_uvs(psys, psmd, num_uv_layers, parent_index, mtfaces, *r_uv);
526 }
527 else {
528 /* Calculate UVs for child particles. */
529 if (!is_simple) {
530 particle_interpolate_children_uvs(psys, psmd, num_uv_layers, child_index, mtfaces, *r_uv);
531 }
532 else if (!r_parent_uvs[psys->child[child_index].parent]) {
533 r_parent_uvs[psys->child[child_index].parent] = *r_uv;
534 particle_calculate_parent_uvs(psys, psmd, num_uv_layers, parent_index, mtfaces, *r_uv);
535 }
536 }
537}
538
541 const bool is_simple,
542 const int num_col_layers,
543 const int parent_index,
544 const int child_index,
545 const MCol **mcols,
546 MCol **r_parent_mcol,
547 MCol **r_mcol)
548{
549 if (psmd == nullptr) {
550 return;
551 }
552 if (is_simple) {
553 if (r_parent_mcol[parent_index] != nullptr) {
554 *r_mcol = r_parent_mcol[parent_index];
555 }
556 else {
557 *r_mcol = MEM_calloc_arrayN<MCol>(num_col_layers, "Particle MCol");
558 }
559 }
560 else {
561 *r_mcol = MEM_calloc_arrayN<MCol>(num_col_layers, "Particle MCol");
562 }
563 if (child_index == -1) {
564 /* Calculate MCols for parent particles. */
565 if (is_simple) {
566 r_parent_mcol[parent_index] = *r_mcol;
567 }
568 particle_calculate_parent_mcol(psys, psmd, num_col_layers, parent_index, mcols, *r_mcol);
569 }
570 else {
571 /* Calculate MCols for child particles. */
572 if (!is_simple) {
573 particle_interpolate_children_mcol(psys, psmd, num_col_layers, child_index, mcols, *r_mcol);
574 }
575 else if (!r_parent_mcol[psys->child[child_index].parent]) {
576 r_parent_mcol[psys->child[child_index].parent] = *r_mcol;
577 particle_calculate_parent_mcol(psys, psmd, num_col_layers, parent_index, mcols, *r_mcol);
578 }
579 }
580}
581
582/* Will return last filled index. */
589 ParticleCacheKey **path_cache,
590 const ParticleSource particle_source,
591 const int global_offset,
592 const int start_index,
593 const int num_path_keys,
594 const int num_uv_layers,
595 const int num_col_layers,
596 const MTFace **mtfaces,
597 const MCol **mcols,
598 uint *uv_id,
599 uint *col_id,
600 float (***r_parent_uvs)[2],
601 MCol ***r_parent_mcol,
604 ParticleHairCache *hair_cache)
605{
606 const bool is_simple = (psys->part->childtype == PART_CHILD_PARTICLES);
607 const bool is_child = (particle_source == PARTICLE_SOURCE_CHILDREN);
608 if (is_simple && *r_parent_uvs == nullptr) {
609 /* TODO(sergey): For edit mode it should be edit->totcached. */
610 *r_parent_uvs = static_cast<float (**)[2]>(
611 MEM_callocN(sizeof(*r_parent_uvs) * psys->totpart, "Parent particle UVs"));
612 }
613 if (is_simple && *r_parent_mcol == nullptr) {
614 *r_parent_mcol = static_cast<MCol **>(
615 MEM_callocN(sizeof(*r_parent_mcol) * psys->totpart, "Parent particle MCol"));
616 }
617 int curr_point = start_index;
618 for (int i = 0; i < num_path_keys; i++) {
619 ParticleCacheKey *path = path_cache[i];
620 if (path->segments <= 0) {
621 continue;
622 }
623 float tangent[3];
624 float (*uv)[2] = nullptr;
625 MCol *mcol = nullptr;
627 psmd,
628 is_simple,
629 num_col_layers,
630 is_child ? psys->child[i].parent : i,
631 is_child ? i : -1,
632 mcols,
633 *r_parent_mcol,
634 &mcol);
636 psmd,
637 is_simple,
638 num_uv_layers,
639 is_child ? psys->child[i].parent : i,
640 is_child ? i : -1,
641 mtfaces,
642 *r_parent_uvs,
643 &uv);
644 for (int j = 0; j < path->segments; j++) {
645 if (j == 0) {
646 sub_v3_v3v3(tangent, path[j + 1].co, path[j].co);
647 }
648 else {
649 sub_v3_v3v3(tangent, path[j + 1].co, path[j - 1].co);
650 }
651 GPU_vertbuf_attr_set(hair_cache->pos, attr_id->pos, curr_point, path[j].co);
652 GPU_vertbuf_attr_set(hair_cache->pos, attr_id->tan, curr_point, tangent);
653 GPU_vertbuf_attr_set(hair_cache->pos, attr_id->ind, curr_point, &i);
654 if (psmd != nullptr) {
655 for (int k = 0; k < num_uv_layers; k++) {
657 hair_cache->pos,
658 uv_id[k],
659 curr_point,
660 (is_simple && is_child) ? (*r_parent_uvs)[psys->child[i].parent][k] : uv[k]);
661 }
662 for (int k = 0; k < num_col_layers; k++) {
663 /* TODO: Put the conversion outside the loop. */
664 ushort scol[4];
666 (is_simple && is_child) ? &(*r_parent_mcol)[psys->child[i].parent][k] : &mcol[k],
667 scol);
668 GPU_vertbuf_attr_set(hair_cache->pos, col_id[k], curr_point, scol);
669 }
670 }
671 GPU_indexbuf_add_generic_vert(elb, curr_point);
672 curr_point++;
673 }
674 sub_v3_v3v3(tangent, path[path->segments].co, path[path->segments - 1].co);
675
676 int global_index = i + global_offset;
677 GPU_vertbuf_attr_set(hair_cache->pos, attr_id->pos, curr_point, path[path->segments].co);
678 GPU_vertbuf_attr_set(hair_cache->pos, attr_id->tan, curr_point, tangent);
679 GPU_vertbuf_attr_set(hair_cache->pos, attr_id->ind, curr_point, &global_index);
680
681 if (psmd != nullptr) {
682 for (int k = 0; k < num_uv_layers; k++) {
683 GPU_vertbuf_attr_set(hair_cache->pos,
684 uv_id[k],
685 curr_point,
686 (is_simple && is_child) ? (*r_parent_uvs)[psys->child[i].parent][k] :
687 uv[k]);
688 }
689 for (int k = 0; k < num_col_layers; k++) {
690 /* TODO: Put the conversion outside the loop. */
691 ushort scol[4];
692 particle_pack_mcol((is_simple && is_child) ? &(*r_parent_mcol)[psys->child[i].parent][k] :
693 &mcol[k],
694 scol);
695 GPU_vertbuf_attr_set(hair_cache->pos, col_id[k], curr_point, scol);
696 }
697 if (!is_simple) {
698 MEM_freeN(uv);
699 MEM_freeN(mcol);
700 }
701 }
702 /* Finish the segment and add restart primitive. */
703 GPU_indexbuf_add_generic_vert(elb, curr_point);
705 curr_point++;
706 }
707 return curr_point;
708}
709
710static float particle_key_weight(const ParticleData *particle, int strand, float t)
711{
712 const ParticleData *part = particle + strand;
713 const HairKey *hkeys = part->hair;
714 float edit_key_seg_t = 1.0f / (part->totkey - 1);
715 if (t == 1.0) {
716 return hkeys[part->totkey - 1].weight;
717 }
718
719 float interp = t / edit_key_seg_t;
720 int index = int(interp);
721 interp -= floorf(interp); /* Time between 2 edit key */
722 float s1 = hkeys[index].weight;
723 float s2 = hkeys[index + 1].weight;
724 return s1 + interp * (s2 - s1);
725}
726
728 const PTCacheEdit * /*edit*/, /* nullptr for weight data */
729 const ParticleData *particle, /* nullptr for select data */
730 ParticleCacheKey **path_cache,
731 const int start_index,
732 const int num_path_keys,
734 GPUVertBufRaw *attr_step)
735{
736 int curr_point = start_index;
737 for (int i = 0; i < num_path_keys; i++) {
738 ParticleCacheKey *path = path_cache[i];
739 if (path->segments <= 0) {
740 continue;
741 }
742 for (int j = 0; j <= path->segments; j++) {
743 EditStrandData *seg_data = (EditStrandData *)GPU_vertbuf_raw_step(attr_step);
744 copy_v3_v3(seg_data->pos, path[j].co);
745 float strand_t = float(j) / path->segments;
746 if (particle) {
747 float weight = particle_key_weight(particle, i, strand_t);
748 /* NaN or unclamped become 1.0f */
749 seg_data->selection = (weight < 1.0f) ? weight : 1.0f;
750 }
751 else {
752 /* Computed in psys_cache_edit_paths_iter(). */
753 seg_data->selection = path[j].col[0];
754 }
755 GPU_indexbuf_add_generic_vert(elb, curr_point);
756 curr_point++;
757 }
758 /* Finish the segment and add restart primitive. */
760 }
761 return curr_point;
762}
763
765 ParticleSystem *psys,
766 ModifierData *md,
767 ParticleHairCache *hair_cache)
768{
769 if (hair_cache->pos != nullptr && hair_cache->indices != nullptr) {
770 return;
771 }
772
773 int curr_point = 0;
775
776 GPU_VERTBUF_DISCARD_SAFE(hair_cache->pos);
778
779 GPUVertFormat format = {0};
781 uint *uv_id = nullptr;
782 uint *col_id = nullptr;
783 int num_uv_layers = 0;
784 int num_col_layers = 0;
785 int active_uv = 0;
786 int active_col = 0;
787 const MTFace **mtfaces = nullptr;
788 const MCol **mcols = nullptr;
789 float (**parent_uvs)[2] = nullptr;
790 MCol **parent_mcol = nullptr;
791
792 if (psmd != nullptr) {
796 }
798 num_col_layers = CustomData_number_of_layers(&psmd->mesh_final->corner_data,
800 if (psmd->mesh_final->active_color_attribute != nullptr) {
804 }
805 }
806 }
807
808 attr_id.pos = GPU_vertformat_attr_add(&format, "pos", gpu::VertAttrType::SFLOAT_32_32_32);
809 attr_id.tan = GPU_vertformat_attr_add(&format, "nor", gpu::VertAttrType::SFLOAT_32_32_32);
810 attr_id.ind = GPU_vertformat_attr_add(&format, "ind", gpu::VertAttrType::SINT_32);
811
812 if (psmd) {
813 uv_id = MEM_malloc_arrayN<uint>(num_uv_layers, "UV attr format");
814 col_id = MEM_malloc_arrayN<uint>(num_col_layers, "Col attr format");
815
816 for (int i = 0; i < num_uv_layers; i++) {
817
818 char uuid[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
819 const char *name = CustomData_get_layer_name(
822
823 SNPRINTF_UTF8(uuid, "a%s", attr_safe_name);
824 uv_id[i] = GPU_vertformat_attr_add(&format, uuid, blender::gpu::VertAttrType::SFLOAT_32_32);
825
826 if (i == active_uv) {
828 }
829 }
830
831 for (int i = 0; i < num_col_layers; i++) {
832 char uuid[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
833 const char *name = CustomData_get_layer_name(
836
837 SNPRINTF_UTF8(uuid, "a%s", attr_safe_name);
838 col_id[i] = GPU_vertformat_attr_add(
839 &format, uuid, blender::gpu::VertAttrType::UNORM_16_16_16_16);
840
841 if (i == active_col) {
843 }
844 }
845 }
846
848 GPU_vertbuf_data_alloc(*hair_cache->pos, hair_cache->point_len);
849
851 GPU_indexbuf_init_ex(&elb, GPU_PRIM_LINE_STRIP, hair_cache->elems_len, hair_cache->point_len);
852
853 if (num_uv_layers || num_col_layers) {
855 if (num_uv_layers) {
856 mtfaces = static_cast<const MTFace **>(
857 MEM_mallocN(sizeof(*mtfaces) * num_uv_layers, "Faces UV layers"));
858 for (int i = 0; i < num_uv_layers; i++) {
859 mtfaces[i] = (const MTFace *)CustomData_get_layer_n(
861 }
862 }
863 if (num_col_layers) {
864 mcols = static_cast<const MCol **>(
865 MEM_mallocN(sizeof(*mcols) * num_col_layers, "Color layers"));
866 for (int i = 0; i < num_col_layers; i++) {
867 mcols[i] = (const MCol *)CustomData_get_layer_n(
868 &psmd->mesh_final->fdata_legacy, CD_MCOL, i);
869 }
870 }
871 }
872
873 if (edit != nullptr && edit->pathcache != nullptr) {
874 curr_point = particle_batch_cache_fill_segments(psys,
875 psmd,
876 edit->pathcache,
878 0,
879 0,
880 edit->totcached,
881 num_uv_layers,
882 num_col_layers,
883 mtfaces,
884 mcols,
885 uv_id,
886 col_id,
887 &parent_uvs,
888 &parent_mcol,
889 &elb,
890 &attr_id,
891 hair_cache);
892 }
893 else {
894 if ((psys->pathcache != nullptr) &&
895 (!psys->childcache || (psys->part->draw & PART_DRAW_PARENT)))
896 {
897 curr_point = particle_batch_cache_fill_segments(psys,
898 psmd,
899 psys->pathcache,
901 0,
902 0,
903 psys->totpart,
904 num_uv_layers,
905 num_col_layers,
906 mtfaces,
907 mcols,
908 uv_id,
909 col_id,
910 &parent_uvs,
911 &parent_mcol,
912 &elb,
913 &attr_id,
914 hair_cache);
915 }
916 if (psys->childcache != nullptr) {
917 const int child_count = psys->totchild * psys->part->disp / 100;
918 curr_point = particle_batch_cache_fill_segments(psys,
919 psmd,
920 psys->childcache,
922 psys->totpart,
923 curr_point,
924 child_count,
925 num_uv_layers,
926 num_col_layers,
927 mtfaces,
928 mcols,
929 uv_id,
930 col_id,
931 &parent_uvs,
932 &parent_mcol,
933 &elb,
934 &attr_id,
935 hair_cache);
936 }
937 }
938 /* Cleanup. */
939 if (parent_uvs != nullptr) {
940 /* TODO(sergey): For edit mode it should be edit->totcached. */
941 for (int i = 0; i < psys->totpart; i++) {
942 MEM_SAFE_FREE(parent_uvs[i]);
943 }
944 MEM_freeN(parent_uvs);
945 }
946 if (parent_mcol != nullptr) {
947 for (int i = 0; i < psys->totpart; i++) {
948 MEM_SAFE_FREE(parent_mcol[i]);
949 }
950 MEM_freeN(parent_mcol);
951 }
952 if (num_uv_layers) {
953 MEM_freeN(mtfaces);
954 }
955 if (num_col_layers) {
956 MEM_freeN(mcols);
957 }
958 if (psmd != nullptr) {
959 MEM_freeN(uv_id);
960 }
961 hair_cache->indices = GPU_indexbuf_build(&elb);
962}
963
965 ParticleSystem *psys,
966 ParticlePointCache *point_cache)
967{
968 if (point_cache->pos != nullptr) {
969 return;
970 }
971
972 int i, curr_point;
973 ParticleData *pa;
975 ParticleSimulationData sim = {nullptr};
976 const DRWContext *draw_ctx = DRW_context_get();
977
978 sim.depsgraph = draw_ctx->depsgraph;
979 sim.scene = draw_ctx->scene;
980 sim.ob = object;
981 sim.psys = psys;
982 sim.psmd = psys_get_modifier(object, psys);
983 psys_sim_data_init(&sim);
984
985 GPU_VERTBUF_DISCARD_SAFE(point_cache->pos);
986
987 static uint pos_id, rot_id, val_id;
988 static const GPUVertFormat format = [&]() {
990 pos_id = GPU_vertformat_attr_add(&format, "part_pos", gpu::VertAttrType::SFLOAT_32_32_32);
991 val_id = GPU_vertformat_attr_add(&format, "part_val", gpu::VertAttrType::SFLOAT_32);
992 rot_id = GPU_vertformat_attr_add(&format, "part_rot", gpu::VertAttrType::SFLOAT_32_32_32_32);
993 return format;
994 }();
995
997 GPU_vertbuf_data_alloc(*point_cache->pos, psys->totpart);
998
999 for (curr_point = 0, i = 0, pa = psys->particles; i < psys->totpart; i++, pa++) {
1000 state.time = DEG_get_ctime(draw_ctx->depsgraph);
1001 if (!psys_get_particle_state(&sim, i, &state, false)) {
1002 continue;
1003 }
1004
1005 float val;
1006
1007 GPU_vertbuf_attr_set(point_cache->pos, pos_id, curr_point, state.co);
1008 GPU_vertbuf_attr_set(point_cache->pos, rot_id, curr_point, state.rot);
1009
1010 switch (psys->part->draw_col) {
1011 case PART_DRAW_COL_VEL:
1012 val = len_v3(state.vel) / psys->part->color_vec_max;
1013 break;
1014 case PART_DRAW_COL_ACC:
1015 val = len_v3v3(state.vel, pa->prev_state.vel) /
1016 ((state.time - pa->prev_state.time) * psys->part->color_vec_max);
1017 break;
1018 default:
1019 val = -1.0f;
1020 break;
1021 }
1022
1023 GPU_vertbuf_attr_set(point_cache->pos, val_id, curr_point, &val);
1024
1025 curr_point++;
1026 }
1027
1028 if (curr_point != psys->totpart) {
1029 GPU_vertbuf_data_resize(*point_cache->pos, curr_point);
1030 }
1031
1032 psys_sim_data_free(&sim);
1033}
1034
1036 ParticleSystem *psys,
1037 PTCacheEdit *edit)
1038{
1039 if (edit->psys == nullptr) {
1040 return;
1041 }
1042 /* NOTE: Get flag from particle system coming from drawing object.
1043 * this is where depsgraph will be setting flags to.
1044 */
1045 const DRWContext *draw_ctx = DRW_context_get();
1046 Scene *scene_orig = DEG_get_original(draw_ctx->scene);
1047 Object *object_orig = DEG_get_original(object_eval);
1048 if (psys->flag & PSYS_HAIR_UPDATED) {
1049 PE_update_object(draw_ctx->depsgraph, scene_orig, object_orig, 0);
1050 psys->flag &= ~PSYS_HAIR_UPDATED;
1051 }
1052 if (edit->pathcache == nullptr) {
1053 Depsgraph *depsgraph = draw_ctx->depsgraph;
1055 scene_orig,
1056 object_orig,
1057 edit,
1060 }
1061}
1062
1064{
1065 if ((object_eval->mode & OB_MODE_PARTICLE_EDIT) == 0) {
1066 return;
1067 }
1068 const DRWContext *draw_ctx = DRW_context_get();
1069 Scene *scene_orig = DEG_get_original(draw_ctx->scene);
1070 Object *object_orig = DEG_get_original(object_eval);
1071 PTCacheEdit *edit = PE_create_current(draw_ctx->depsgraph, scene_orig, object_orig);
1072 if (edit != nullptr) {
1073 drw_particle_update_ptcache_edit(object_eval, psys, edit);
1074 }
1075}
1076
1078 ParticleSystem *psys,
1079 ModifierData *md,
1080 PTCacheEdit *edit,
1081 const int additional_subdivision)
1082{
1083 const DRWContext *draw_ctx = DRW_context_get();
1084 if (psys_in_edit_mode(draw_ctx->depsgraph, psys)) {
1085 object = DEG_get_original(object);
1086 psys = psys_orig_get(psys);
1087 }
1089
1092 math::clamp(additional_subdivision, 0, 3));
1093 src.object = object;
1094 src.psys = psys;
1095 src.md = md;
1096 src.edit = edit;
1097 return src;
1098}
1099
1101 ParticleSystem *psys,
1102 ModifierData *md)
1103{
1105 if (cache->hair.hairs == nullptr) {
1106 drw_particle_update_ptcache(object, psys);
1107 ParticleDrawSource source = drw_particle_get_hair_source(object, psys, md, nullptr, 0);
1108 ensure_seg_pt_count(source.edit, source.psys, &cache->hair);
1109 particle_batch_cache_ensure_pos_and_seg(source.edit, source.psys, source.md, &cache->hair);
1110 cache->hair.hairs = GPU_batch_create(
1111 GPU_PRIM_LINE_STRIP, cache->hair.pos, cache->hair.indices);
1112 }
1113 return cache->hair.hairs;
1114}
1115
1117{
1119
1120 if (cache->point.points == nullptr) {
1121 particle_batch_cache_ensure_pos(object, psys, &cache->point);
1122 cache->point.points = GPU_batch_create(GPU_PRIM_POINTS, cache->point.pos, nullptr);
1123 }
1124
1125 return cache->point.points;
1126}
1127
1129 ParticleSystem *psys,
1130 ModifierData * /*md*/,
1131 ParticleHairCache *hair_cache,
1132 bool use_weight)
1133{
1134 if (hair_cache->pos != nullptr && hair_cache->indices != nullptr) {
1135 return;
1136 }
1137
1138 ParticleData *particle = (use_weight) ? psys->particles : nullptr;
1139
1140 GPU_VERTBUF_DISCARD_SAFE(hair_cache->pos);
1142
1143 GPUVertBufRaw data_step;
1145 uint pos_id, selection_id;
1146 const GPUVertFormat *edit_point_format = edit_points_vert_format_get(&pos_id, &selection_id);
1147
1148 hair_cache->pos = GPU_vertbuf_create_with_format(*edit_point_format);
1149 GPU_vertbuf_data_alloc(*hair_cache->pos, hair_cache->point_len);
1150 GPU_vertbuf_attr_get_raw_data(hair_cache->pos, pos_id, &data_step);
1151
1152 GPU_indexbuf_init_ex(&elb, GPU_PRIM_LINE_STRIP, hair_cache->elems_len, hair_cache->point_len);
1153
1154 if (edit != nullptr && edit->pathcache != nullptr) {
1156 edit, particle, edit->pathcache, 0, edit->totcached, &elb, &data_step);
1157 }
1158 hair_cache->indices = GPU_indexbuf_build(&elb);
1159}
1160
1162 ParticleSystem *psys,
1163 PTCacheEdit *edit,
1164 bool use_weight)
1165{
1167 if (cache->edit_is_weight != use_weight) {
1170 }
1171 if (cache->edit_hair.hairs != nullptr) {
1172 return cache->edit_hair.hairs;
1173 }
1174 drw_particle_update_ptcache_edit(object, psys, edit);
1175 ensure_seg_pt_count(edit, psys, &cache->edit_hair);
1176 particle_batch_cache_ensure_edit_pos_and_seg(edit, psys, nullptr, &cache->edit_hair, use_weight);
1179 cache->edit_is_weight = use_weight;
1180 return cache->edit_hair.hairs;
1181}
1182
1184{
1185 if (cache->edit_inner_pos != nullptr) {
1186 return;
1187 }
1188 cache->edit_inner_point_len = 0;
1189 for (int point_index = 0; point_index < edit->totpoint; point_index++) {
1190 const PTCacheEditPoint *point = &edit->points[point_index];
1191 if (point->flag & PEP_HIDE) {
1192 continue;
1193 }
1194 BLI_assert(point->totkey >= 1);
1195 cache->edit_inner_point_len += (point->totkey - 1);
1196 }
1197}
1198
1200 ParticleBatchCache *cache)
1201{
1202 if (cache->edit_inner_pos != nullptr) {
1203 return;
1204 }
1205
1206 uint pos_id, selection_id;
1207 const GPUVertFormat *edit_point_format = edit_points_vert_format_get(&pos_id, &selection_id);
1208
1209 cache->edit_inner_pos = GPU_vertbuf_create_with_format(*edit_point_format);
1211
1212 int global_key_index = 0;
1213 for (int point_index = 0; point_index < edit->totpoint; point_index++) {
1214 const PTCacheEditPoint *point = &edit->points[point_index];
1215 if (point->flag & PEP_HIDE) {
1216 continue;
1217 }
1218 for (int key_index = 0; key_index < point->totkey - 1; key_index++) {
1219 PTCacheEditKey *key = &point->keys[key_index];
1220 float selection = (key->flag & PEK_SELECT) ? 1.0f : 0.0f;
1221 GPU_vertbuf_attr_set(cache->edit_inner_pos, pos_id, global_key_index, key->world_co);
1222 GPU_vertbuf_attr_set(cache->edit_inner_pos, selection_id, global_key_index, &selection);
1223 global_key_index++;
1224 }
1225 }
1226}
1227
1229 ParticleSystem *psys,
1230 PTCacheEdit *edit)
1231{
1233 if (cache->edit_inner_points != nullptr) {
1234 return cache->edit_inner_points;
1235 }
1236 drw_particle_update_ptcache_edit(object, psys, edit);
1237 ensure_edit_inner_points_count(edit, cache);
1240 return cache->edit_inner_points;
1241}
1242
1244{
1245 if (cache->edit_tip_pos != nullptr) {
1246 return;
1247 }
1248 cache->edit_tip_point_len = 0;
1249 for (int point_index = 0; point_index < edit->totpoint; point_index++) {
1250 const PTCacheEditPoint *point = &edit->points[point_index];
1251 if (point->flag & PEP_HIDE) {
1252 continue;
1253 }
1254 cache->edit_tip_point_len += 1;
1255 }
1256}
1257
1259{
1260 if (cache->edit_tip_pos != nullptr) {
1261 return;
1262 }
1263
1264 uint pos_id, selection_id;
1265 const GPUVertFormat *edit_point_format = edit_points_vert_format_get(&pos_id, &selection_id);
1266
1267 cache->edit_tip_pos = GPU_vertbuf_create_with_format(*edit_point_format);
1269
1270 int global_point_index = 0;
1271 for (int point_index = 0; point_index < edit->totpoint; point_index++) {
1272 const PTCacheEditPoint *point = &edit->points[point_index];
1273 if (point->flag & PEP_HIDE) {
1274 continue;
1275 }
1276 PTCacheEditKey *key = &point->keys[point->totkey - 1];
1277 float selection = (key->flag & PEK_SELECT) ? 1.0f : 0.0f;
1278
1279 GPU_vertbuf_attr_set(cache->edit_tip_pos, pos_id, global_point_index, key->world_co);
1280 GPU_vertbuf_attr_set(cache->edit_tip_pos, selection_id, global_point_index, &selection);
1281 global_point_index++;
1282 }
1283}
1284
1286 ParticleSystem *psys,
1287 PTCacheEdit *edit)
1288{
1290 if (cache->edit_tip_points != nullptr) {
1291 return cache->edit_tip_points;
1292 }
1293 drw_particle_update_ptcache_edit(object, psys, edit);
1294 ensure_edit_tip_points_count(edit, cache);
1297 return cache->edit_tip_points;
1298}
1299
1300/* Can return DMCACHE_NOTFOUND in case of invalid mapping. */
1301static int particle_mface_index(const ParticleData &particle, int face_count_legacy)
1302{
1304 return particle.num_dmcache;
1305 }
1306 if (particle.num < face_count_legacy) {
1307 return (particle.num == DMCACHE_ISCHILD) ? DMCACHE_NOTFOUND : particle.num;
1308 }
1309 return DMCACHE_NOTFOUND;
1310}
1311static int particle_mface_index(const ChildParticle &particle, int /*face_count_legacy*/)
1312{
1313 return particle.num;
1314}
1315
1317{
1318 /* Convert to linear ushort and swizzle */
1322 mcol.a / 255.0f};
1324 std::swap(col[0], col[2]);
1325 return col;
1326}
1327
1328template<typename ParticleDataT>
1329static float4 interpolate(const ParticleDataT &particle, Span<MFace> mfaces, Span<MCol> mcols)
1330{
1331 int num = particle_mface_index(particle, mfaces.size());
1332 if (num == DMCACHE_NOTFOUND) {
1333 return float4(0, 0, 0, 1);
1334 }
1335 /* CustomDataLayer CD_MCOL has 4 structs per face. */
1336 MCol mcol;
1337 psys_interpolate_mcol(mcols.slice(num * 4, 4).data(), mfaces[num].v4, particle.fuv, &mcol);
1338 return particle_mcol_convert(mcol);
1339}
1340
1341template<typename ParticleDataT>
1342static float2 interpolate(const ParticleDataT &particle, Span<MFace> mfaces, Span<MTFace> mtfaces)
1343{
1344 int num = particle_mface_index(particle, mfaces.size());
1345 if (num == DMCACHE_NOTFOUND) {
1346 return float2(0);
1347 }
1348 float2 uv;
1349 psys_interpolate_uvs(&mtfaces[num], mfaces[num].v4, particle.fuv, uv);
1350 return uv;
1351}
1352
1353static std::optional<StringRef> get_first_uv_name(const bke::AttributeAccessor &attributes)
1354{
1355 std::optional<StringRef> name;
1356 attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
1357 if (iter.data_type == bke::AttrType::Float2) {
1358 name = iter.name;
1359 iter.stop();
1360 }
1361 });
1362 return name;
1363}
1364
1365template<typename T>
1367 const eCustomDataType type,
1368 const StringRef name)
1369{
1370 int layer_id = CustomData_get_named_layer(&mesh.fdata_legacy, type, name);
1371 return {static_cast<const T *>(CustomData_get_layer_n(&mesh.fdata_legacy, type, layer_id)),
1372 /* There is 4 MCol per face. */
1373 mesh.totface_legacy * (std::is_same_v<T, MCol> ? 4 : 1)};
1374}
1375
1376template<typename T>
1378{
1379 return {static_cast<const T *>(CustomData_get_layer(&mesh.fdata_legacy, type)),
1380 mesh.totface_legacy};
1381}
1382
1383template<typename InputT, typename OutputT, eCustomDataType data_type>
1385 const StringRef name)
1386{
1388 Mesh &mesh = *psmd->mesh_final;
1389
1390 /* TODO(fclem): Use normalized integer format. */
1394 vbo->allocate(src.curves_num());
1395 MutableSpan<OutputT> data = vbo->data<OutputT>();
1396
1397 const int emit_from = psmd->psys->part->from;
1398 /* True if no interpolation for child particle. */
1399 const bool is_simple = (src.psys->part->childtype == PART_CHILD_PARTICLES) ||
1400 !ELEM(emit_from, PART_FROM_FACE, PART_FROM_VOLUME);
1401
1403 Span<InputT> attr = span_from_custom_data_layer<InputT>(mesh, data_type, name);
1405 Span<ChildParticle> children(src.psys->child, src.psys->totchild);
1406 Span<ParticleData> particles(src.psys->particles, src.psys->totpart);
1407
1408 /* Index of the particle/hair curve. Note that the order of the loops matter. */
1409 int curve_index = 0;
1410
1411 ParticleSpans part_spans = src.particles_get();
1412 for (const int particle_index : part_spans.parent.index_range()) {
1413 data[curve_index++] = interpolate(particles[particle_index], mfaces, attr);
1414 }
1415
1416 if (is_simple) {
1417 /* Fallback array if parent particles are not displayed. */
1418 Vector<OutputT> parent_data;
1419 if (part_spans.parent.is_empty()) {
1420 parent_data.reserve(src.psys->totpart);
1421 for (int particle_index : IndexRange(src.psys->totpart)) {
1422 parent_data.append(interpolate(particles[particle_index], mfaces, attr));
1423 }
1424 }
1425
1426 Span<OutputT> data_parent(part_spans.parent.is_empty() ? parent_data.data() : data.data(),
1427 src.psys->totpart);
1428
1429 for (const int particle_index : part_spans.children.index_range()) {
1430 /* Simple copy of the parent data. */
1431 data[curve_index++] = data_parent[children[particle_index].parent];
1432 }
1433 }
1434 else {
1435 for (const int particle_index : part_spans.children.index_range()) {
1436 data[curve_index++] = interpolate(children[particle_index], mfaces, attr);
1437 }
1438 }
1439 return vbo;
1440}
1441
1443 const Mesh &mesh,
1444 const StringRef name,
1445 bool &r_is_point_domain)
1446{
1447 using namespace bke;
1448 /* Note: All legacy hair attributes come from the emitter mesh and are on per curve domain. */
1449 r_is_point_domain = false;
1450
1451 const AttributeAccessor attributes = mesh.attributes();
1452
1453 auto meta_data = attributes.lookup_meta_data(name);
1454 if (meta_data && meta_data->domain == bke::AttrDomain::Corner) {
1455 if (meta_data->data_type == AttrType::ColorByte) {
1457 }
1458 if (meta_data->data_type == AttrType::Float2) {
1460 }
1461 }
1462 /* Attribute doesn't exist or is of an incompatible type.
1463 * Replace it with a black curve domain attribute. */
1465}
1466
1468 ParticleDrawSource &src,
1469 const Mesh &mesh,
1470 const StringRef name,
1471 const int index)
1472{
1473 char sampler_name[32];
1475
1477 src, mesh, name, attributes_point_domain[index]);
1478
1479 /* Existing final data may have been for a different attribute (with a different name or domain),
1480 * free the data. */
1481 this->curve_attributes_buf[index].reset();
1482
1483 /* Ensure final data for points. */
1484 if (attributes_point_domain[index]) {
1486 }
1487 else {
1488 this->curve_attributes_buf[index] = std::move(attr_buf);
1489 }
1490}
1491
1493 ParticleDrawSource &src,
1494 const GPUMaterial *gpu_material)
1495{
1497 if (psmd == nullptr || psmd->mesh_final == nullptr || src.curves_num() == 0) {
1498 return;
1499 }
1500 const Mesh &mesh = *psmd->mesh_final;
1501 const bke::AttributeAccessor attributes = mesh.attributes();
1502
1503 if (gpu_material) {
1504 VectorSet<std::string> attrs_needed;
1505 ListBase gpu_attrs = GPU_material_attributes(gpu_material);
1506 LISTBASE_FOREACH (GPUMaterialAttribute *, gpu_attr, &gpu_attrs) {
1507 StringRef name = gpu_attr->name;
1508 if (name.is_empty()) {
1509 if (std::optional<StringRef> uv_name = get_first_uv_name(attributes)) {
1510 drw_attributes_add_request(&attrs_needed, *uv_name);
1511 }
1512 }
1513 if (!attributes.contains(name)) {
1514 continue;
1515 }
1516 drw_attributes_add_request(&attrs_needed, name);
1517 }
1518
1519 if (!drw_attributes_overlap(&attr_used, &attrs_needed)) {
1520 /* Some new attributes have been added, free all and start over. */
1521 for (const int i : IndexRange(GPU_MAX_ATTR)) {
1522 this->curve_attributes_buf[i].reset();
1523 }
1524 drw_attributes_merge(&attr_used, &attrs_needed);
1525 }
1527 }
1528
1529 for (const int i : attr_used.index_range()) {
1530 if (this->curve_attributes_buf[i]) {
1531 continue;
1532 }
1533 ensure_attribute(module, src, mesh, attr_used[i], i);
1534 }
1535}
1536
1538{
1539 if (points_by_curve_buf) {
1540 return;
1541 }
1542
1545 src.evaluated_points_by_curve().data());
1546
1547 /* Use the same type for all curves. */
1549 auto resolution_varray = VArray<int32_t>::from_single(src.resolution(), src.curves_num());
1550 /* Not used. */
1551 auto cyclic_offsets_varray = VArray<int32_t>::from_single(0, 2);
1552 /* TODO(fclem): Optimize shaders to avoid needing to upload this data if data is uniform.
1553 * This concerns all varray. */
1554 this->curves_type_buf = gpu::VertBuf::from_varray(type_varray);
1555 this->curves_resolution_buf = gpu::VertBuf::from_varray(resolution_varray);
1556 this->curves_cyclic_buf = gpu::VertBuf::from_varray(cyclic_offsets_varray);
1557}
1558
1559/* Copied from cycles. */
1560static float hair_shape_radius(float shape, float root, float tip, float time)
1561{
1562 BLI_assert(time >= 0.0f);
1563 BLI_assert(time <= 1.0f);
1564 float radius = 1.0f - time;
1565 if (shape < 0.0f) {
1566 radius = powf(radius, 1.0f + shape);
1567 }
1568 else {
1569 radius = powf(radius, 1.0f / (1.0f - shape));
1570 }
1571 return (radius * (root - tip)) + tip;
1572}
1573
1575{
1577 return;
1578 }
1579
1580 if (src.curves_num() == 0) {
1581 /* Garbage data. */
1585 return;
1586 }
1587
1588 ensure_common(src);
1589
1592
1593 MutableSpan<float3> points_pos = points_pos_buf->data<float3>();
1594 MutableSpan<float> points_rad = points_rad_buf->data<float>();
1595
1596 const ParticleSettings &part = *src.psys->part;
1597 const float hair_rad_shape = part.shape;
1598 const float hair_rad_root = part.rad_root * part.rad_scale * 0.5f;
1599 const float hair_rad_tip = part.rad_tip * part.rad_scale * 0.5f;
1600 const bool hair_close_tip = (part.shape_flag & PART_SHAPE_CLOSE_TIP) != 0;
1601
1602 int i = 0;
1604 int j = 0;
1605 for (const ParticleCacheKey &point : strand) {
1606 points_pos[i] = point.co;
1607 points_rad[i] = (hair_close_tip && (j == strand.index_range().last())) ?
1608 0.0f :
1610 hair_rad_shape, hair_rad_root, hair_rad_tip, point.time);
1611 i++, j++;
1612 }
1613 });
1614
1616
1618
1619 module.evaluate_positions(true,
1620 false,
1621 false,
1622 false,
1623 false,
1624 src.curves_num(),
1625 *this,
1626 std::move(points_pos_buf),
1627 std::move(points_rad_buf),
1628 evaluated_pos_rad_buf,
1629 transform);
1630
1631 /* TODO(fclem): Make time and length optional. */
1634
1635 module.evaluate_curve_length_intercept(false, src.curves_num(), *this);
1636}
1637
1639 ParticleDrawSource &src,
1640 int face_per_segment)
1641{
1642 const bool is_ribbon = face_per_segment < 2;
1643
1644 gpu::VertBufPtr &indirection_buf = is_ribbon ? this->indirection_ribbon_buf :
1646 if (indirection_buf) {
1647 return indirection_buf;
1648 }
1649
1650 if (src.curves_num() == 0) {
1651 /* Garbage data. */
1652 indirection_buf = gpu::VertBuf::device_only<int>(4);
1653 return indirection_buf;
1654 }
1655
1656 ensure_common(src);
1657
1658 indirection_buf = module.evaluate_topology_indirection(
1659 src.curves_num(), src.evaluated_points_num(), *this, is_ribbon, false);
1660
1661 return indirection_buf;
1662}
1663
1665{
1667 CurvesEvalCache &eval_cache = cache->hair.eval_cache;
1668 if (assign_if_different(eval_cache.resolution, src.resolution())) {
1670 }
1671 return eval_cache;
1672}
1673
1674} // namespace blender::draw
CustomData interface, see also DNA_customdata_types.h.
const void * CustomData_get_layer_n(const CustomData *data, eCustomDataType type, int n)
int CustomData_get_named_layer(const CustomData *data, eCustomDataType type, blender::StringRef name)
const void * CustomData_get_layer(const CustomData *data, eCustomDataType type)
const char * CustomData_get_layer_name(const CustomData *data, eCustomDataType type, int n)
int CustomData_get_active_layer(const CustomData *data, eCustomDataType type)
bool CustomData_has_layer(const CustomData *data, eCustomDataType type)
int CustomData_number_of_layers(const CustomData *data, eCustomDataType type)
void BKE_mesh_tessface_ensure(Mesh *mesh)
struct ParticleSystemModifierData * psys_get_modifier(struct Object *ob, struct ParticleSystem *psys)
Definition particle.cc:2155
#define DMCACHE_NOTFOUND
struct ParticleSystem * psys_orig_get(struct ParticleSystem *psys)
Definition particle.cc:660
void psys_cache_edit_paths(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob, struct PTCacheEdit *edit, float cfra, bool use_render_params)
Definition particle.cc:3655
bool psys_in_edit_mode(struct Depsgraph *depsgraph, const struct ParticleSystem *psys)
void psys_interpolate_uvs(const struct MTFace *tface, int quad, const float w[4], float r_uv[2])
void psys_sim_data_free(struct ParticleSimulationData *sim)
Definition particle.cc:633
#define DMCACHE_ISCHILD
@ BKE_PARTICLE_BATCH_DIRTY_ALL
void psys_interpolate_mcol(const struct MCol *mcol, int quad, const float w[4], struct MCol *mc)
void psys_sim_data_init(struct ParticleSimulationData *sim)
Definition particle.cc:592
bool psys_get_particle_state(struct ParticleSimulationData *sim, int p, struct ParticleKey *state, bool always)
Definition particle.cc:4879
#define PEP_HIDE
#define PEK_SELECT
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define LISTBASE_FOREACH(type, var, list)
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
ATTR_WARN_UNUSED_RESULT const size_t num
#define SNPRINTF_UTF8(dst, format,...)
unsigned int uint
unsigned short ushort
#define UNLIKELY(x)
#define ELEM(...)
@ DAG_EVAL_RENDER
float DEG_get_ctime(const Depsgraph *graph)
eEvaluationMode DEG_get_mode(const Depsgraph *graph)
T * DEG_get_original(T *id)
Object groups, one object can be in many groups at once.
@ CD_PROP_BYTE_COLOR
@ CD_PROP_FLOAT2
@ OB_MODE_PARTICLE_EDIT
@ PART_SHAPE_CLOSE_TIP
@ PSYS_HAIR_UPDATED
@ PART_DRAW_PARENT
@ PART_DRAW_COL_ACC
@ PART_DRAW_COL_VEL
struct ParticleSystem ParticleSystem
@ PART_CHILD_PARTICLES
@ PART_FROM_VOLUME
@ PART_FROM_FACE
void PE_update_object(Depsgraph *depsgraph, Scene *scene, Object *ob, int useflag)
PTCacheEdit * PE_create_current(Depsgraph *depsgraph, Scene *scene, Object *ob)
#define GPU_batch_create(primitive_type, vertex_buf, index_buf)
Definition GPU_batch.hh:141
#define GPU_BATCH_DISCARD_SAFE(batch)
Definition GPU_batch.hh:197
#define GPU_INDEXBUF_DISCARD_SAFE(elem)
void GPU_indexbuf_add_primitive_restart(GPUIndexBufBuilder *)
blender::gpu::IndexBuf * GPU_indexbuf_build(GPUIndexBufBuilder *)
void GPU_indexbuf_add_generic_vert(GPUIndexBufBuilder *, uint v)
void GPU_indexbuf_init_ex(GPUIndexBufBuilder *, GPUPrimType, uint index_len, uint vertex_len)
ListBase GPU_material_attributes(const GPUMaterial *material)
@ GPU_PRIM_POINTS
@ GPU_PRIM_LINE_STRIP
static constexpr int GPU_MAX_ATTR
Definition GPU_shader.hh:33
void GPU_vertbuf_attr_get_raw_data(blender::gpu::VertBuf *, uint a_idx, GPUVertBufRaw *access)
GPU_INLINE void * GPU_vertbuf_raw_step(GPUVertBufRaw *a)
void GPU_vertbuf_data_resize(blender::gpu::VertBuf &verts, uint v_len)
blender::gpu::VertBuf * GPU_vertbuf_create_with_format_ex(const GPUVertFormat &format, GPUUsageType usage)
static blender::gpu::VertBuf * GPU_vertbuf_create_with_format(const GPUVertFormat &format)
void GPU_vertbuf_attr_set(blender::gpu::VertBuf *, uint a_idx, uint v_idx, const void *data)
#define GPU_VERTBUF_DISCARD_SAFE(verts)
void GPU_vertbuf_data_alloc(blender::gpu::VertBuf &verts, uint v_len)
@ GPU_USAGE_STATIC
@ GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY
void GPU_vertformat_alias_add(GPUVertFormat *, blender::StringRef alias)
void GPU_vertformat_safe_attr_name(blender::StringRef attr_name, char *r_safe_name, uint max_len)
static constexpr int GPU_MAX_SAFE_ATTR_NAME
uint GPU_vertformat_attr_add(GPUVertFormat *format, blender::StringRef name, blender::gpu::VertAttrType type)
BLI_INLINE void IMB_colormanagement_rec709_to_scene_linear(float scene_linear[3], const float rec709[3])
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
BMesh const char void * data
BPy_StructRNA * depsgraph
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
AttributeSet attributes
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
constexpr const T * data() const
Definition BLI_span.hh:215
constexpr int64_t size() const
Definition BLI_span.hh:252
static VArray from_single(T value, const int64_t size)
void append(const T &value)
void reserve(const int64_t min_capacity)
void foreach_attribute(const FunctionRef< void(const AttributeIter &)> fn) const
bool contains(StringRef attribute_id) const
static VertBufPtr device_only(uint size)
static VertBufPtr from_varray(const VArray< T > &array)
static VertBufPtr from_size(const int size, GPUUsageType usage=GPU_USAGE_STATIC)
static VertBufPtr from_span(const Span< T > data)
nullptr float
#define powf(x, y)
Utilities for rendering attributes.
const DRWContext * DRW_context_get()
#define MAX_THICKRES
uint col
struct @021025263243242147216143265077100330027142264337::@240232116316110053135047106323056371161236243121 attr_id
ccl_device_inline uint particle_index(KernelGlobals kg, const int particle)
format
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
ccl_device_inline float interp(const float a, const float b, const float t)
Definition math_base.h:502
MINLINE unsigned short unit_float_to_ushort_clamp(float val)
float BLI_color_from_srgb_table[256]
static ulong state[N]
#define T
void drw_particle_update_ptcache(Object *object_eval, ParticleSystem *psys)
void drw_attributes_add_request(VectorSet< std::string > *attrs, const StringRef name)
static void count_cache_segment_keys(ParticleCacheKey **pathcache, const int num_path_cache_keys, ParticleHairCache *hair_cache)
static float4 particle_mcol_convert(const MCol &mcol)
void DRW_particle_batch_cache_free(ParticleSystem *psys)
CurvesEvalCache & hair_particle_get_eval_cache(ParticleDrawSource &src)
blender::gpu::Batch * DRW_particles_batch_cache_get_edit_inner_points(Object *object, ParticleSystem *psys, PTCacheEdit *edit)
ParticleDrawSource drw_particle_get_hair_source(Object *object, ParticleSystem *psys, ModifierData *md, PTCacheEdit *edit, const int additional_subdivision)
blender::gpu::Batch * DRW_particles_batch_cache_get_edit_tip_points(Object *object, ParticleSystem *psys, PTCacheEdit *edit)
static void drw_particle_update_ptcache_edit(Object *object_eval, ParticleSystem *psys, PTCacheEdit *edit)
static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit, ParticleSystem *psys, ModifierData *md, ParticleHairCache *hair_cache)
static int particle_batch_cache_fill_segments_edit(const PTCacheEdit *, const ParticleData *particle, ParticleCacheKey **path_cache, const int start_index, const int num_path_keys, GPUIndexBufBuilder *elb, GPUVertBufRaw *attr_step)
static bool particle_batch_cache_valid(ParticleSystem *psys)
static void particle_batch_cache_ensure_edit_tip_pos(PTCacheEdit *edit, ParticleBatchCache *cache)
blender::gpu::Batch * DRW_particles_batch_cache_get_edit_strands(Object *object, ParticleSystem *psys, PTCacheEdit *edit, bool use_weight)
static void particle_batch_cache_ensure_edit_pos_and_seg(PTCacheEdit *edit, ParticleSystem *psys, ModifierData *, ParticleHairCache *hair_cache, bool use_weight)
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])
static void ensure_edit_inner_points_count(const PTCacheEdit *edit, ParticleBatchCache *cache)
blender::gpu::Batch * DRW_particles_batch_cache_get_dots(Object *object, ParticleSystem *psys)
static std::optional< StringRef > get_first_uv_name(const bke::AttributeAccessor &attributes)
static void particle_batch_cache_clear(ParticleSystem *psys)
Span< T > span_from_custom_data_layer(const Mesh &mesh, const eCustomDataType type, const StringRef name)
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])
static void particle_pack_mcol(MCol *mcol, ushort r_scol[3])
static int particle_mface_index(const ParticleData &particle, int face_count_legacy)
static void particle_batch_cache_ensure_edit_inner_pos(PTCacheEdit *edit, ParticleBatchCache *cache)
void drw_curves_get_attribute_sampler_name(const StringRef layer_name, char r_sampler_name[32])
static void particle_batch_cache_clear_point(ParticlePointCache *point_cache)
static void particle_batch_cache_ensure_pos(Object *object, ParticleSystem *psys, ParticlePointCache *point_cache)
bool drw_attributes_overlap(const VectorSet< std::string > *a, const VectorSet< std::string > *b)
static float particle_key_weight(const ParticleData *particle, int strand, float t)
static void particle_calculate_parent_mcol(ParticleSystem *psys, ParticleSystemModifierData *psmd, const int num_col_layers, const int parent_index, const MCol **mcols, MCol *r_mcol)
static gpu::VertBufPtr ensure_curve_attribute(ParticleDrawSource &src, const Mesh &mesh, const StringRef name, bool &r_is_point_domain)
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)
static gpu::VertBufPtr interpolate_face_corner_attribute_to_curve(ParticleDrawSource &src, const StringRef name)
static void particle_batch_cache_init(ParticleSystem *psys)
void DRW_particle_batch_cache_dirty_tag(ParticleSystem *psys, int mode)
static ParticleBatchCache * particle_batch_cache_get(ParticleSystem *psys)
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)
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, GPUIndexBufBuilder *elb, HairAttributeID *attr_id, ParticleHairCache *hair_cache)
void drw_attributes_merge(VectorSet< std::string > *dst, const VectorSet< std::string > *src)
blender::gpu::Batch * DRW_particles_batch_cache_get_hair(Object *object, ParticleSystem *psys, ModifierData *md)
static float hair_shape_radius(float shape, float root, float tip, float time)
static void ensure_seg_pt_count(PTCacheEdit *edit, ParticleSystem *psys, ParticleHairCache *hair_cache)
static void ensure_edit_tip_points_count(const PTCacheEdit *edit, ParticleBatchCache *cache)
static const GPUVertFormat * edit_points_vert_format_get(uint *r_pos_id, uint *r_selection_id)
static void particle_batch_cache_clear_hair(ParticleHairCache *hair_cache)
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])
std::unique_ptr< gpu::VertBuf, gpu::VertBufDeleter > VertBufPtr
T clamp(const T &a, const T &min, const T &max)
T interpolate(const T &a, const T &b, const FactorT &t)
bool assign_if_different(T &old_value, T new_value)
MatBase< float, 4, 4 > float4x4
VecBase< float, 4 > float4
VecBase< float, 2 > float2
VecBase< float, 3 > float3
static bool is_child(const Object *ob, const Object *parent)
static struct PyModuleDef module
Definition python.cpp:796
const char * name
#define floorf
Depsgraph * depsgraph
Scene * scene
unsigned char r
unsigned char a
unsigned char g
unsigned char b
unsigned int v4
CustomData corner_data
CustomData fdata_legacy
int totface_legacy
int faces_num
char * active_color_attribute
struct PTCacheEditKey * keys
struct ParticleCacheKey ** pathcache
PTCacheEditPoint * points
struct ParticleSystem * psys
ParticleKey prev_state
struct Depsgraph * depsgraph
struct ParticleSystemModifierData * psmd
struct Scene * scene
struct ParticleSystem * psys
struct Object * ob
struct ParticleSystem * psys
ChildParticle * child
ParticleData * particles
ParticleSettings * part
struct ParticleCacheKey ** childcache
struct ParticleCacheKey ** pathcache
std::array< bool, GPU_MAX_ATTR > attributes_point_domain
void ensure_common(const bke::CurvesGeometry &curves)
gpu::VertBufPtr & indirection_buf_get(CurvesModule &module, const bke::CurvesGeometry &curves, int face_per_segment)
void ensure_attributes(struct CurvesModule &module, const bke::CurvesGeometry &curves, const GPUMaterial *gpu_material)
void ensure_positions(CurvesModule &module, const bke::CurvesGeometry &curves)
gpu::VertBufPtr curve_attributes_buf[GPU_MAX_ATTR]
VectorSet< std::string > attr_used_over_time
VectorSet< std::string > attr_used
void ensure_attribute(struct CurvesModule &module, const bke::CurvesGeometry &curves, StringRef name, int index)
blender::gpu::Batch * proc_hairs[MAX_THICKRES]
void foreach_strand(FunctionRef< void(Span< ParticleCacheKey >)> callback)
Span< ParticleCacheKey * > parent
Span< ParticleCacheKey * > children
i
Definition text_draw.cc:230