Blender  V2.93
draw_cache_impl_hair.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2017 by Blender Foundation.
17  * All rights reserved.
18  */
19 
26 #include <string.h>
27 
28 #include "MEM_guardedalloc.h"
29 
30 #include "BLI_math_base.h"
31 #include "BLI_math_vector.h"
32 #include "BLI_utildefines.h"
33 
34 #include "DNA_hair_types.h"
35 #include "DNA_object_types.h"
36 
37 #include "BKE_hair.h"
38 
39 #include "GPU_batch.h"
40 #include "GPU_texture.h"
41 
42 #include "draw_cache_impl.h" /* own include */
43 #include "draw_hair_private.h" /* own include */
44 
45 static void hair_batch_cache_clear(Hair *hair);
46 
47 /* ---------------------------------------------------------------------- */
48 /* Hair GPUBatch Cache */
49 
50 typedef struct HairBatchCache {
52 
53  /* settings to determine if cache is invalid */
54  bool is_dirty;
56 
57 /* GPUBatch cache management. */
58 
59 static bool hair_batch_cache_valid(Hair *hair)
60 {
61  HairBatchCache *cache = hair->batch_cache;
62  return (cache && cache->is_dirty == false);
63 }
64 
65 static void hair_batch_cache_init(Hair *hair)
66 {
67  HairBatchCache *cache = hair->batch_cache;
68 
69  if (!cache) {
70  cache = hair->batch_cache = MEM_callocN(sizeof(*cache), __func__);
71  }
72  else {
73  memset(cache, 0, sizeof(*cache));
74  }
75 
76  cache->is_dirty = false;
77 }
78 
80 {
81  if (!hair_batch_cache_valid(hair)) {
84  }
85 }
86 
88 {
90  return hair->batch_cache;
91 }
92 
93 void DRW_hair_batch_cache_dirty_tag(Hair *hair, int mode)
94 {
95  HairBatchCache *cache = hair->batch_cache;
96  if (cache == NULL) {
97  return;
98  }
99  switch (mode) {
101  cache->is_dirty = true;
102  break;
103  default:
104  BLI_assert(0);
105  }
106 }
107 
108 static void hair_batch_cache_clear(Hair *hair)
109 {
110  HairBatchCache *cache = hair->batch_cache;
111  if (!cache) {
112  return;
113  }
114 
116 }
117 
119 {
121  MEM_SAFE_FREE(hair->batch_cache);
122 }
123 
124 static void ensure_seg_pt_count(Hair *hair, ParticleHairCache *hair_cache)
125 {
126  if ((hair_cache->pos != NULL && hair_cache->indices != NULL) ||
127  (hair_cache->proc_point_buf != NULL)) {
128  return;
129  }
130 
131  hair_cache->strands_len = 0;
132  hair_cache->elems_len = 0;
133  hair_cache->point_len = 0;
134 
135  HairCurve *curve = hair->curves;
136  int num_curves = hair->totcurve;
137  for (int i = 0; i < num_curves; i++, curve++) {
138  hair_cache->strands_len++;
139  hair_cache->elems_len += curve->numpoints + 1;
140  hair_cache->point_len += curve->numpoints;
141  }
142 }
143 
145 {
146  /* TODO: use hair radius layer if available. */
147  HairCurve *curve = hair->curves;
148  int num_curves = hair->totcurve;
149  for (int i = 0; i < num_curves; i++, curve++) {
150  float(*curve_co)[3] = hair->co + curve->firstpoint;
151  float total_len = 0.0f;
152  float *co_prev = NULL, *seg_data_first;
153  for (int j = 0; j < curve->numpoints; j++) {
154  float *seg_data = (float *)GPU_vertbuf_raw_step(attr_step);
155  copy_v3_v3(seg_data, curve_co[j]);
156  if (co_prev) {
157  total_len += len_v3v3(co_prev, curve_co[j]);
158  }
159  else {
160  seg_data_first = seg_data;
161  }
162  seg_data[3] = total_len;
163  co_prev = curve_co[j];
164  }
165  if (total_len > 0.0f) {
166  /* Divide by total length to have a [0-1] number. */
167  for (int j = 0; j < curve->numpoints; j++, seg_data_first += 4) {
168  seg_data_first[3] /= total_len;
169  }
170  }
171  }
172 }
173 
175 {
176  if (cache->proc_point_buf != NULL) {
177  return;
178  }
179 
180  /* initialize vertex format */
181  GPUVertFormat format = {0};
183 
186 
187  GPUVertBufRaw pos_step;
188  GPU_vertbuf_attr_get_raw_data(cache->proc_point_buf, pos_id, &pos_step);
189 
191 
192  /* Create vbo immediately to bind to texture buffer. */
194 
195  cache->point_tex = GPU_texture_create_from_vertbuf("hair_point", cache->proc_point_buf);
196 }
197 
199  GPUVertBufRaw *data_step,
200  GPUVertBufRaw *seg_step)
201 {
202  HairCurve *curve = hair->curves;
203  int num_curves = hair->totcurve;
204  for (int i = 0; i < num_curves; i++, curve++) {
205  *(uint *)GPU_vertbuf_raw_step(data_step) = curve->firstpoint;
206  *(ushort *)GPU_vertbuf_raw_step(seg_step) = curve->numpoints - 1;
207  }
208 }
209 
211 {
212  GPUVertBufRaw data_step, seg_step;
213 
214  GPUVertFormat format_data = {0};
215  uint data_id = GPU_vertformat_attr_add(&format_data, "data", GPU_COMP_U32, 1, GPU_FETCH_INT);
216 
217  GPUVertFormat format_seg = {0};
218  uint seg_id = GPU_vertformat_attr_add(&format_seg, "data", GPU_COMP_U16, 1, GPU_FETCH_INT);
219 
220  /* Strand Data */
221  cache->proc_strand_buf = GPU_vertbuf_create_with_format(&format_data);
223  GPU_vertbuf_attr_get_raw_data(cache->proc_strand_buf, data_id, &data_step);
224 
227  GPU_vertbuf_attr_get_raw_data(cache->proc_strand_seg_buf, seg_id, &seg_step);
228 
229  hair_batch_cache_fill_strands_data(hair, &data_step, &seg_step);
230 
231  /* Create vbo immediately to bind to texture buffer. */
233  cache->strand_tex = GPU_texture_create_from_vertbuf("hair_strand", cache->proc_strand_buf);
234 
236  cache->strand_seg_tex = GPU_texture_create_from_vertbuf("hair_strand_seg",
237  cache->proc_strand_seg_buf);
238 }
239 
241 {
242  /* Same format as point_tex. */
243  GPUVertFormat format = {0};
245 
247 
248  /* Create a destination buffer for the transform feedback. Sized appropriately */
249  /* Those are points! not line segments. */
250  GPU_vertbuf_data_alloc(cache->final[subdiv].proc_buf,
251  cache->final[subdiv].strands_res * cache->strands_len);
252 
253  /* Create vbo immediately to bind to texture buffer. */
254  GPU_vertbuf_use(cache->final[subdiv].proc_buf);
255 
256  cache->final[subdiv].proc_tex = GPU_texture_create_from_vertbuf("hair_proc",
257  cache->final[subdiv].proc_buf);
258 }
259 
261  const int res,
262  GPUIndexBufBuilder *elb)
263 {
264  HairCurve *curve = hair->curves;
265  int num_curves = hair->totcurve;
266  uint curr_point = 0;
267  for (int i = 0; i < num_curves; i++, curve++) {
268  for (int k = 0; k < res; k++) {
269  GPU_indexbuf_add_generic_vert(elb, curr_point++);
270  }
272  }
273 }
274 
276  ParticleHairCache *cache,
277  int thickness_res,
278  int subdiv)
279 {
280  BLI_assert(thickness_res <= MAX_THICKRES); /* Cylinder strip not currently supported. */
281 
282  if (cache->final[subdiv].proc_hairs[thickness_res - 1] != NULL) {
283  return;
284  }
285 
286  int verts_per_hair = cache->final[subdiv].strands_res * thickness_res;
287  /* +1 for primitive restart */
288  int element_count = (verts_per_hair + 1) * cache->strands_len;
289  GPUPrimType prim_type = (thickness_res == 1) ? GPU_PRIM_LINE_STRIP : GPU_PRIM_TRI_STRIP;
290 
291  static GPUVertFormat format = {0};
293 
294  /* initialize vertex format */
296 
298  GPU_vertbuf_data_alloc(vbo, 1);
299 
300  GPUIndexBufBuilder elb;
301  GPU_indexbuf_init_ex(&elb, prim_type, element_count, element_count);
302 
303  hair_batch_cache_fill_segments_indices(hair, verts_per_hair, &elb);
304 
305  cache->final[subdiv].proc_hairs[thickness_res - 1] = GPU_batch_create_ex(
307 }
308 
309 /* Ensure all textures and buffers needed for GPU accelerated drawing. */
311  ParticleHairCache **r_hair_cache,
312  int subdiv,
313  int thickness_res)
314 {
315  bool need_ft_update = false;
316  Hair *hair = object->data;
317 
318  HairBatchCache *cache = hair_batch_cache_get(hair);
319  *r_hair_cache = &cache->hair;
320 
321  const int steps = 2; /* TODO: don't hard-code? */
322  (*r_hair_cache)->final[subdiv].strands_res = 1 << (steps + subdiv);
323 
324  /* Refreshed on combing and simulation. */
325  if ((*r_hair_cache)->proc_point_buf == NULL) {
326  ensure_seg_pt_count(hair, &cache->hair);
328  need_ft_update = true;
329  }
330 
331  /* Refreshed if active layer or custom data changes. */
332  if ((*r_hair_cache)->strand_tex == NULL) {
334  }
335 
336  /* Refreshed only on subdiv count change. */
337  if ((*r_hair_cache)->final[subdiv].proc_buf == NULL) {
339  need_ft_update = true;
340  }
341  if ((*r_hair_cache)->final[subdiv].proc_hairs[thickness_res - 1] == NULL) {
342  hair_batch_cache_ensure_procedural_indices(hair, &cache->hair, thickness_res, subdiv);
343  }
344 
345  return need_ft_update;
346 }
347 
349 {
350  return max_ii(1, hair->totcol);
351 }
typedef float(TangentPoint)[2]
General operations for hairs.
@ BKE_HAIR_BATCH_DIRTY_ALL
Definition: BKE_hair.h:52
#define BLI_assert(a)
Definition: BLI_assert.h:58
MINLINE int max_ii(int a, int b)
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v3_v3(float r[3], const float a[3])
unsigned int uint
Definition: BLI_sys_types.h:83
unsigned short ushort
Definition: BLI_sys_types.h:84
Object is a sort of wrapper for general info.
GPUBatch * GPU_batch_create_ex(GPUPrimType prim, GPUVertBuf *vert, GPUIndexBuf *elem, eGPUBatchFlag owns_flag)
Definition: gpu_batch.cc:60
@ GPU_BATCH_OWNS_INDEX
Definition: GPU_batch.h:54
@ GPU_BATCH_OWNS_VBO
Definition: GPU_batch.h:45
void GPU_indexbuf_add_primitive_restart(GPUIndexBufBuilder *)
GPUIndexBuf * 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)
GPUPrimType
Definition: GPU_primitive.h:34
@ GPU_PRIM_LINE_STRIP
Definition: GPU_primitive.h:38
@ GPU_PRIM_TRI_STRIP
Definition: GPU_primitive.h:40
GPUTexture * GPU_texture_create_from_vertbuf(const char *name, struct GPUVertBuf *vert)
Definition: gpu_texture.cc:315
#define GPU_vertbuf_create_with_format(format)
struct GPUVertBuf GPUVertBuf
void GPU_vertbuf_data_alloc(GPUVertBuf *, uint v_len)
void GPU_vertbuf_use(GPUVertBuf *)
GPU_INLINE void * GPU_vertbuf_raw_step(GPUVertBufRaw *a)
void GPU_vertbuf_attr_get_raw_data(GPUVertBuf *, uint a_idx, GPUVertBufRaw *access)
@ GPU_FETCH_FLOAT
@ GPU_FETCH_INT_TO_FLOAT_UNIT
@ GPU_FETCH_INT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
void GPU_vertformat_clear(GPUVertFormat *)
@ GPU_COMP_U16
@ GPU_COMP_F32
@ GPU_COMP_U32
@ GPU_COMP_U8
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
Curve curve
static void hair_batch_cache_fill_segments_proc_pos(Hair *hair, GPUVertBufRaw *attr_step)
void DRW_hair_batch_cache_dirty_tag(Hair *hair, int mode)
int DRW_hair_material_count_get(Hair *hair)
static void hair_batch_cache_init(Hair *hair)
static HairBatchCache * hair_batch_cache_get(Hair *hair)
void DRW_hair_batch_cache_validate(Hair *hair)
static bool hair_batch_cache_valid(Hair *hair)
bool hair_ensure_procedural_data(Object *object, ParticleHairCache **r_hair_cache, int subdiv, int thickness_res)
static void hair_batch_cache_fill_segments_indices(Hair *hair, const int res, GPUIndexBufBuilder *elb)
static void hair_batch_cache_ensure_procedural_pos(Hair *hair, ParticleHairCache *cache)
void DRW_hair_batch_cache_free(Hair *hair)
static void ensure_seg_pt_count(Hair *hair, ParticleHairCache *hair_cache)
static void hair_batch_cache_fill_strands_data(Hair *hair, GPUVertBufRaw *data_step, GPUVertBufRaw *seg_step)
struct HairBatchCache HairBatchCache
static void hair_batch_cache_ensure_procedural_final_points(ParticleHairCache *cache, int subdiv)
static void hair_batch_cache_ensure_procedural_indices(Hair *hair, ParticleHairCache *cache, int thickness_res, int subdiv)
static void hair_batch_cache_clear(Hair *hair)
static void hair_batch_cache_ensure_procedural_strand_data(Hair *hair, ParticleHairCache *cache)
void particle_batch_cache_clear_hair(ParticleHairCache *hair_cache)
#define MAX_THICKRES
format
Definition: logImageCore.h:47
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:45
static const int steps
Definition: sky_nishita.cpp:28
ParticleHairCache hair
float(* co)[3]
int totcurve
struct HairCurve * curves
short totcol
void * batch_cache
GPUVertBuf * proc_point_buf
GPUVertBuf * proc_strand_buf
GPUIndexBuf * indices
GPUTexture * strand_seg_tex
GPUVertBuf * proc_strand_seg_buf
GPUTexture * point_tex
GPUTexture * strand_tex
ParticleHairFinalCache final[MAX_HAIR_SUBDIV]
GPUBatch * proc_hairs[MAX_THICKRES]