Blender V4.3
draw_hair.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
11#include "DRW_render.hh"
12
13#include "BLI_string_utils.hh"
14#include "BLI_utildefines.h"
15
18#include "DNA_modifier_types.h"
19#include "DNA_particle_types.h"
20
21#include "BKE_duplilist.hh"
22
23#include "GPU_batch.hh"
24#include "GPU_capabilities.hh"
25#include "GPU_compute.hh"
26#include "GPU_context.hh"
27#include "GPU_material.hh"
28#include "GPU_shader.hh"
29#include "GPU_texture.hh"
30#include "GPU_vertex_buffer.hh"
31
32#include "DRW_gpu_wrapper.hh"
33
34#include "draw_hair_private.hh"
35#include "draw_shader.hh"
36#include "draw_shader_shared.hh"
37
44
46static DRWPass *g_tf_pass; /* XXX can be a problem with multiple #DRWManager in the future */
48
50{
51 if (g_dummy_vbo != nullptr) {
52 return;
53 }
54 /* initialize vertex format */
57
60
61 const float vert[4] = {0.0f, 0.0f, 0.0f, 0.0f};
63 GPU_vertbuf_attr_fill(g_dummy_vbo, dummy_id, vert);
64 /* Create VBO immediately to bind to texture buffer. */
66
67 g_dummy_curves_info = MEM_new<blender::draw::UniformBuffer<CurvesInfos>>("g_dummy_curves_info");
68 memset(
69 g_dummy_curves_info->is_point_attribute, 0, sizeof(g_dummy_curves_info->is_point_attribute));
70 g_dummy_curves_info->push_update();
71}
72
74{
75 g_tf_pass = DRW_pass_create("Update Hair Pass", DRW_STATE_NO_DRAW);
76
78}
79
81 ParticleHairCache *cache,
82 const int subdiv)
83{
84 DRW_shgroup_buffer_texture(shgrp, "hairPointBuffer", cache->proc_point_buf);
85 DRW_shgroup_buffer_texture(shgrp, "hairStrandBuffer", cache->proc_strand_buf);
86 DRW_shgroup_buffer_texture(shgrp, "hairStrandSegBuffer", cache->proc_strand_seg_buf);
87 DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &cache->final[subdiv].strands_res, 1);
88}
89
90static void drw_hair_particle_cache_update_compute(ParticleHairCache *cache, const int subdiv)
91{
92 const int strands_len = cache->strands_len;
93 const int final_points_len = cache->final[subdiv].strands_res * strands_len;
94 if (final_points_len > 0) {
98 DRW_shgroup_vertex_buffer(shgrp, "posTime", cache->final[subdiv].proc_buf);
99
100 const int max_strands_per_call = GPU_max_work_group_count(0);
101 int strands_start = 0;
102 while (strands_start < strands_len) {
103 int batch_strands_len = std::min(strands_len - strands_start, max_strands_per_call);
104 DRWShadingGroup *subgroup = DRW_shgroup_create_sub(shgrp);
105 DRW_shgroup_uniform_int_copy(subgroup, "hairStrandOffset", strands_start);
106 DRW_shgroup_call_compute(subgroup, batch_strands_len, cache->final[subdiv].strands_res, 1);
107 strands_start += batch_strands_len;
108 }
109 }
110}
111
113 ParticleSystem *psys,
114 ModifierData *md,
115 GPUMaterial *gpu_material,
116 int subdiv,
117 int thickness_res)
118{
119 using namespace blender::draw;
120 ParticleHairCache *cache;
121 bool update = particles_ensure_procedural_data(
122 object, psys, md, &cache, gpu_material, subdiv, thickness_res);
123
124 if (update) {
126 }
127 return cache;
128}
129
131 ParticleSystem *psys,
132 ModifierData *md)
133{
134 const DRWContextState *draw_ctx = DRW_context_state_get();
135 Scene *scene = draw_ctx->scene;
136
137 int subdiv = scene->r.hair_subdiv;
138 int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
139
141 object, psys, md, nullptr, subdiv, thickness_res);
142
143 return cache->final[subdiv].proc_buf;
144}
145
147 ParticleSystem * /*psys*/,
148 ModifierData * /*md*/,
149 float (*dupli_mat)[4])
150{
151 Object *dupli_parent = DRW_object_get_dupli_parent(object);
152 DupliObject *dupli_object = DRW_object_get_dupli(object);
153
154 if ((dupli_parent != nullptr) && (dupli_object != nullptr)) {
155 if (dupli_object->type & OB_DUPLICOLLECTION) {
156 unit_m4(dupli_mat);
157 Collection *collection = dupli_parent->instance_collection;
158 if (collection != nullptr) {
159 sub_v3_v3(dupli_mat[3], collection->instance_offset);
160 }
161 mul_m4_m4m4(dupli_mat, dupli_parent->object_to_world().ptr(), dupli_mat);
162 }
163 else {
164 copy_m4_m4(dupli_mat, dupli_object->ob->object_to_world().ptr());
165 invert_m4(dupli_mat);
166 mul_m4_m4m4(dupli_mat, object->object_to_world().ptr(), dupli_mat);
167 }
168 }
169 else {
170 unit_m4(dupli_mat);
171 }
172}
173
175 ParticleSystem *psys,
176 ModifierData *md,
177 DRWShadingGroup *shgrp_parent,
178 GPUMaterial *gpu_material)
179{
180 const DRWContextState *draw_ctx = DRW_context_state_get();
181 Scene *scene = draw_ctx->scene;
182 float dupli_mat[4][4];
183
184 int subdiv = scene->r.hair_subdiv;
185 int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
186
188 object, psys, md, gpu_material, subdiv, thickness_res);
189
190 DRWShadingGroup *shgrp = DRW_shgroup_create_sub(shgrp_parent);
191
192 /* TODO: optimize this. Only bind the ones #GPUMaterial needs. */
193 for (int i = 0; i < hair_cache->num_uv_layers; i++) {
194 for (int n = 0; n < MAX_LAYER_NAME_CT && hair_cache->uv_layer_names[i][n][0] != '\0'; n++) {
195 DRW_shgroup_uniform_texture(shgrp, hair_cache->uv_layer_names[i][n], hair_cache->uv_tex[i]);
196 }
197 }
198 for (int i = 0; i < hair_cache->num_col_layers; i++) {
199 for (int n = 0; n < MAX_LAYER_NAME_CT && hair_cache->col_layer_names[i][n][0] != '\0'; n++) {
201 shgrp, hair_cache->col_layer_names[i][n], hair_cache->col_tex[i]);
202 }
203 }
204
205 /* Fix issue with certain driver not drawing anything if there is nothing bound to
206 * "ac", "au", "u" or "c". */
207 if (hair_cache->num_uv_layers == 0) {
211 }
212 if (hair_cache->num_col_layers == 0) {
215 }
216
217 DRW_hair_duplimat_get(object, psys, md, dupli_mat);
218
219 /* Get hair shape parameters. */
220 ParticleSettings *part = psys->part;
221 float hair_rad_shape = part->shape;
222 float hair_rad_root = part->rad_root * part->rad_scale * 0.5f;
223 float hair_rad_tip = part->rad_tip * part->rad_scale * 0.5f;
224 bool hair_close_tip = (part->shape_flag & PART_SHAPE_CLOSE_TIP) != 0;
225
226 DRW_shgroup_buffer_texture(shgrp, "hairPointBuffer", hair_cache->final[subdiv].proc_buf);
227 if (hair_cache->proc_length_buf) {
228 DRW_shgroup_buffer_texture(shgrp, "l", hair_cache->proc_length_buf);
229 }
230
231 DRW_shgroup_uniform_block(shgrp, "drw_curves", *g_dummy_curves_info);
232 DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &hair_cache->final[subdiv].strands_res, 1);
233 DRW_shgroup_uniform_int_copy(shgrp, "hairThicknessRes", thickness_res);
234 DRW_shgroup_uniform_float_copy(shgrp, "hairRadShape", hair_rad_shape);
235 DRW_shgroup_uniform_mat4_copy(shgrp, "hairDupliMatrix", dupli_mat);
236 DRW_shgroup_uniform_float_copy(shgrp, "hairRadRoot", hair_rad_root);
237 DRW_shgroup_uniform_float_copy(shgrp, "hairRadTip", hair_rad_tip);
238 DRW_shgroup_uniform_bool_copy(shgrp, "hairCloseTip", hair_close_tip);
239 if (gpu_material) {
240 /* NOTE: This needs to happen before the drawcall to allow correct attribute extraction.
241 * (see #101896) */
242 DRW_shgroup_add_material_resources(shgrp, gpu_material);
243 }
244 /* TODO(fclem): Until we have a better way to cull the hair and render with orco, bypass
245 * culling test. */
246 blender::gpu::Batch *geom = hair_cache->final[subdiv].proc_hairs[thickness_res - 1];
247 DRW_shgroup_call_no_cull(shgrp, geom, object);
248
249 return shgrp;
250}
251
253{
254 /* Just render the pass when using compute shaders or transform feedback. */
257}
258
264
265/* New Draw Manager. */
266#include "draw_common.hh"
267
268namespace blender::draw {
269
270static PassSimple *g_pass = nullptr;
271
273{
274 if (!g_pass) {
275 g_pass = MEM_new<PassSimple>("drw_hair g_pass", "Update Hair Pass");
276 }
277 g_pass->init();
279}
280
282 ParticleSystem *psys,
283 ModifierData *md,
284 GPUMaterial *gpu_material,
285 int subdiv,
286 int thickness_res)
287{
288 using namespace blender::draw;
289 ParticleHairCache *cache;
291 object, psys, md, &cache, gpu_material, subdiv, thickness_res);
292
293 if (!update) {
294 return cache;
295 }
296
297 const int strands_len = cache->strands_len;
298 const int final_points_len = cache->final[subdiv].strands_res * strands_len;
299 if (final_points_len > 0) {
300 PassSimple::Sub &ob_ps = g_pass->sub("Object Pass");
301
303
304 ob_ps.bind_texture("hairPointBuffer", cache->proc_point_buf);
305 ob_ps.bind_texture("hairStrandBuffer", cache->proc_strand_buf);
306 ob_ps.bind_texture("hairStrandSegBuffer", cache->proc_strand_seg_buf);
307 ob_ps.push_constant("hairStrandsRes", &cache->final[subdiv].strands_res);
308 ob_ps.bind_ssbo("posTime", cache->final[subdiv].proc_buf);
309
310 const int max_strands_per_call = GPU_max_work_group_count(0);
311 int strands_start = 0;
312 while (strands_start < strands_len) {
313 int batch_strands_len = std::min(strands_len - strands_start, max_strands_per_call);
314 PassSimple::Sub &sub_ps = ob_ps.sub("Sub Pass");
315 sub_ps.push_constant("hairStrandOffset", strands_start);
316 sub_ps.dispatch(int3(batch_strands_len, cache->final[subdiv].strands_res, 1));
317 strands_start += batch_strands_len;
318 }
319 }
320
321 return cache;
322}
323
325 Object *object,
326 ParticleSystem *psys,
327 ModifierData *md)
328{
329 int subdiv = scene->r.hair_subdiv;
330 int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
331
333 object, psys, md, nullptr, subdiv, thickness_res);
334
335 return cache->final[subdiv].proc_buf;
336}
337
338void hair_update(Manager &manager)
339{
340 manager.submit(*g_pass);
342}
343
345{
346 MEM_delete(g_pass);
347 g_pass = nullptr;
348}
349
350template<typename PassT>
351blender::gpu::Batch *hair_sub_pass_setup_implementation(PassT &sub_ps,
352 const Scene *scene,
353 Object *object,
354 ParticleSystem *psys,
355 ModifierData *md,
356 GPUMaterial *gpu_material)
357{
360 int subdiv = scene->r.hair_subdiv;
361 int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
363 object, psys, md, gpu_material, subdiv, thickness_res);
364
365 /* TODO: optimize this. Only bind the ones #GPUMaterial needs. */
366 for (int i : IndexRange(hair_cache->num_uv_layers)) {
367 for (int n = 0; n < MAX_LAYER_NAME_CT && hair_cache->uv_layer_names[i][n][0] != '\0'; n++) {
368 sub_ps.bind_texture(hair_cache->uv_layer_names[i][n], hair_cache->uv_tex[i]);
369 }
370 }
371 for (int i : IndexRange(hair_cache->num_col_layers)) {
372 for (int n = 0; n < MAX_LAYER_NAME_CT && hair_cache->col_layer_names[i][n][0] != '\0'; n++) {
373 sub_ps.bind_texture(hair_cache->col_layer_names[i][n], hair_cache->col_tex[i]);
374 }
375 }
376
377 /* Fix issue with certain driver not drawing anything if there is nothing bound to
378 * "ac", "au", "u" or "c". */
379 if (hair_cache->num_uv_layers == 0) {
380 sub_ps.bind_texture("u", g_dummy_vbo);
381 sub_ps.bind_texture("au", g_dummy_vbo);
382 sub_ps.bind_texture("a", g_dummy_vbo);
383 }
384 if (hair_cache->num_col_layers == 0) {
385 sub_ps.bind_texture("c", g_dummy_vbo);
386 sub_ps.bind_texture("ac", g_dummy_vbo);
387 }
388
389 float4x4 dupli_mat;
390 DRW_hair_duplimat_get(object, psys, md, dupli_mat.ptr());
391
392 /* Get hair shape parameters. */
393 ParticleSettings *part = psys->part;
394 float hair_rad_shape = part->shape;
395 float hair_rad_root = part->rad_root * part->rad_scale * 0.5f;
396 float hair_rad_tip = part->rad_tip * part->rad_scale * 0.5f;
397 bool hair_close_tip = (part->shape_flag & PART_SHAPE_CLOSE_TIP) != 0;
398
399 sub_ps.bind_texture("hairPointBuffer", hair_cache->final[subdiv].proc_buf);
400 if (hair_cache->proc_length_buf) {
401 sub_ps.bind_texture("l", hair_cache->proc_length_buf);
402 }
403
404 sub_ps.bind_ubo("drw_curves", *g_dummy_curves_info);
405 sub_ps.push_constant("hairStrandsRes", &hair_cache->final[subdiv].strands_res, 1);
406 sub_ps.push_constant("hairThicknessRes", thickness_res);
407 sub_ps.push_constant("hairRadShape", hair_rad_shape);
408 sub_ps.push_constant("hairDupliMatrix", dupli_mat);
409 sub_ps.push_constant("hairRadRoot", hair_rad_root);
410 sub_ps.push_constant("hairRadTip", hair_rad_tip);
411 sub_ps.push_constant("hairCloseTip", hair_close_tip);
412
413 return hair_cache->final[subdiv].proc_hairs[thickness_res - 1];
414}
415
416blender::gpu::Batch *hair_sub_pass_setup(PassMain::Sub &sub_ps,
417 const Scene *scene,
418 Object *object,
419 ParticleSystem *psys,
420 ModifierData *md,
421 GPUMaterial *gpu_material)
422{
423 return hair_sub_pass_setup_implementation(sub_ps, scene, object, psys, md, gpu_material);
424}
425
426blender::gpu::Batch *hair_sub_pass_setup(PassSimple::Sub &sub_ps,
427 const Scene *scene,
428 Object *object,
429 ParticleSystem *psys,
430 ModifierData *md,
431 GPUMaterial *gpu_material)
432{
433 return hair_sub_pass_setup_implementation(sub_ps, scene, object, psys, md, gpu_material);
434}
435
436} // namespace blender::draw
void mul_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4])
void unit_m4(float m[4][4])
Definition rct.c:1127
void copy_m4_m4(float m1[4][4], const float m2[4][4])
bool invert_m4(float mat[4][4])
MINLINE void sub_v3_v3(float r[3], const float a[3])
unsigned int uint
Object groups, one object can be in many groups at once.
@ OB_DUPLICOLLECTION
@ PART_SHAPE_CLOSE_TIP
@ SCE_HAIR_SHAPE_STRAND
#define DRW_shgroup_vertex_buffer(shgroup, name, vert)
#define DRW_shgroup_call_no_cull(shgroup, geom, ob)
#define DRW_shgroup_uniform_block(shgroup, name, ubo)
int GPU_max_work_group_count(int index)
void GPU_memory_barrier(eGPUBarrier barrier)
Definition gpu_state.cc:374
@ GPU_BARRIER_SHADER_STORAGE
Definition GPU_state.hh:48
void GPU_vertbuf_use(blender::gpu::VertBuf *)
void GPU_vertbuf_attr_fill(blender::gpu::VertBuf *, uint a_idx, const void *data)
blender::gpu::VertBuf * GPU_vertbuf_create_with_format_ex(const GPUVertFormat &format, GPUUsageType usage)
#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
@ GPU_FETCH_FLOAT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
struct GPUShader GPUShader
void submit(PassSimple &pass, View &view)
void bind_texture(const char *name, GPUTexture *texture, GPUSamplerState state=sampler_auto)
PassBase< DrawCommandBufType > & sub(const char *name)
Definition draw_pass.hh:616
void dispatch(int group_len)
Definition draw_pass.hh:874
void state_set(DRWState state, int clip_plane_count=0)
Definition draw_pass.hh:954
void push_constant(const char *name, const float &data)
void bind_ssbo(const char *name, GPUStorageBuf *buffer)
void shader_set(GPUShader *shader)
Definition draw_pass.hh:971
void DRW_hair_duplimat_get(Object *object, ParticleSystem *psys, ModifierData *md, float(*dupli_mat)[4])
Definition draw_hair.cc:146
blender::gpu::VertBuf * DRW_hair_pos_buffer_get(Object *object, ParticleSystem *psys, ModifierData *md)
Definition draw_hair.cc:130
static void drw_hair_ensure_vbo()
Definition draw_hair.cc:49
static blender::draw::UniformBuffer< CurvesInfos > * g_dummy_curves_info
Definition draw_hair.cc:47
DRWShadingGroup * DRW_shgroup_hair_create_sub(Object *object, ParticleSystem *psys, ModifierData *md, DRWShadingGroup *shgrp_parent, GPUMaterial *gpu_material)
Definition draw_hair.cc:174
static void drw_hair_particle_cache_update_compute(ParticleHairCache *cache, const int subdiv)
Definition draw_hair.cc:90
static blender::gpu::VertBuf * g_dummy_vbo
Definition draw_hair.cc:45
static ParticleHairCache * drw_hair_particle_cache_get(Object *object, ParticleSystem *psys, ModifierData *md, GPUMaterial *gpu_material, int subdiv, int thickness_res)
Definition draw_hair.cc:112
void DRW_hair_init()
Definition draw_hair.cc:73
static void drw_hair_particle_cache_shgrp_attach_resources(DRWShadingGroup *shgrp, ParticleHairCache *cache, const int subdiv)
Definition draw_hair.cc:80
void DRW_hair_free()
Definition draw_hair.cc:259
void DRW_hair_update()
Definition draw_hair.cc:252
void DRW_hair_duplimat_get(Object *object, ParticleSystem *, ModifierData *, float(*dupli_mat)[4])
Definition draw_hair.cc:146
static DRWPass * g_tf_pass
Definition draw_hair.cc:46
@ PART_REFINE_CATMULL_ROM
#define MAX_LAYER_NAME_CT
Object * DRW_object_get_dupli_parent(const Object *)
const DRWContextState * DRW_context_state_get()
DupliObject * DRW_object_get_dupli(const Object *)
DRWShadingGroup * DRW_shgroup_create(GPUShader *shader, DRWPass *pass)
void DRW_shgroup_uniform_float_copy(DRWShadingGroup *shgroup, const char *name, const float value)
void DRW_shgroup_buffer_texture(DRWShadingGroup *shgroup, const char *name, blender::gpu::VertBuf *vertex_buffer)
void DRW_shgroup_uniform_texture(DRWShadingGroup *shgroup, const char *name, const GPUTexture *tex)
DRWPass * DRW_pass_create(const char *name, DRWState state)
DRWShadingGroup * DRW_shgroup_create_sub(DRWShadingGroup *shgroup)
void DRW_shgroup_call_compute(DRWShadingGroup *shgroup, int groups_x_len, int groups_y_len, int groups_z_len)
void DRW_shgroup_uniform_int_copy(DRWShadingGroup *shgroup, const char *name, const int value)
void DRW_shgroup_uniform_int(DRWShadingGroup *shgroup, const char *name, const int *value, int arraysize)
void DRW_shgroup_add_material_resources(DRWShadingGroup *grp, GPUMaterial *material)
void DRW_shgroup_uniform_bool_copy(DRWShadingGroup *shgroup, const char *name, const bool value)
void DRW_shgroup_uniform_mat4_copy(DRWShadingGroup *shgroup, const char *name, const float(*value)[4])
void DRW_draw_pass(DRWPass *pass)
GPUShader * DRW_shader_hair_refine_get(ParticleRefineShader refinement)
@ DRW_STATE_NO_DRAW
Definition draw_state.hh:27
format
static PassSimple * g_pass
gpu::Batch * hair_sub_pass_setup(PassMain::Sub &sub_ps, const Scene *scene, Object *object, ParticleSystem *psys, ModifierData *md, GPUMaterial *gpu_material=nullptr)
Definition draw_hair.cc:416
blender::gpu::Batch * hair_sub_pass_setup_implementation(PassT &sub_ps, const Scene *scene, Object *object, ParticleSystem *psys, ModifierData *md, GPUMaterial *gpu_material)
Definition draw_hair.cc:351
void hair_update(Manager &manager)
Definition draw_hair.cc:338
static gpu::VertBuf * g_dummy_vbo
bool particles_ensure_procedural_data(Object *object, ParticleSystem *psys, ModifierData *md, ParticleHairCache **r_hair_cache, GPUMaterial *gpu_material, int subdiv, int thickness_res)
gpu::VertBuf * hair_pos_buffer_get(Scene *scene, Object *object, ParticleSystem *psys, ModifierData *md)
Definition draw_hair.cc:324
void hair_free()
Definition draw_hair.cc:344
static ParticleHairCache * hair_particle_cache_get(Object *object, ParticleSystem *psys, ModifierData *md, GPUMaterial *gpu_material, int subdiv, int thickness_res)
Definition draw_hair.cc:281
void hair_init()
Definition draw_hair.cc:272
VecBase< int32_t, 3 > int3
static void update(bNodeTree *ntree)
struct Collection * instance_collection
GPUTexture * uv_tex[MAX_MTFACE]
blender::gpu::VertBuf * proc_length_buf
blender::gpu::VertBuf * proc_strand_buf
blender::gpu::VertBuf * proc_strand_seg_buf
char uv_layer_names[MAX_MTFACE][MAX_LAYER_NAME_CT][MAX_LAYER_NAME_LEN]
blender::gpu::VertBuf * proc_point_buf
ParticleHairFinalCache final[MAX_HAIR_SUBDIV]
char(* col_layer_names)[MAX_LAYER_NAME_CT][MAX_LAYER_NAME_LEN]
blender::gpu::VertBuf * proc_buf
blender::gpu::Batch * proc_hairs[MAX_THICKRES]
ParticleRefineCall * next
Definition draw_hair.cc:39
blender::gpu::VertBuf * vbo
Definition draw_hair.cc:40
DRWShadingGroup * shgrp
Definition draw_hair.cc:41
ParticleSettings * part
struct RenderData r
const c_style_mat & ptr() const