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:
authorClément Foucault <foucault.clem@gmail.com>2018-05-29 13:11:03 +0300
committerClément Foucault <foucault.clem@gmail.com>2018-05-30 13:25:20 +0300
commit328f8dc21c0e8a7e3da14f6578fb84a811f80b89 (patch)
tree6d7cbe0cb0bc0fee859bff5c73f98dbf9109b6f8 /source/blender/draw
parent2faef3473c061e34a03668905c8d81672a72dd38 (diff)
DRW: Add new GPU hair system.
This new system use transform feedback to compute subdivided hair points position. For now no smoothing is done between input points. This new system decouple the strands data (uv, mcol) with the points position, requiring less update work if only simulation is running. In the future, we can have compute shader do the work of the feedback transform pass since it's really what it's meant to. Also we could generate the child particles during this pass, releasing some CPU time. draw_hair.c has been created to handle all of the Shading group creations as well as subdivision shaders. We store one final batch per settings combination because multiple viewport or render could use the same particle system with a different subdivision count or hair shape type.
Diffstat (limited to 'source/blender/draw')
-rw-r--r--source/blender/draw/CMakeLists.txt4
-rw-r--r--source/blender/draw/intern/draw_cache_impl_particles.c710
-rw-r--r--source/blender/draw/intern/draw_common.h20
-rw-r--r--source/blender/draw/intern/draw_hair.c167
-rw-r--r--source/blender/draw/intern/draw_hair_private.h92
-rw-r--r--source/blender/draw/intern/draw_manager.c1
-rw-r--r--source/blender/draw/intern/draw_manager_data.c1
-rw-r--r--source/blender/draw/modes/shaders/common_hair_lib.glsl190
-rw-r--r--source/blender/draw/modes/shaders/common_hair_refine_vert.glsl15
9 files changed, 1177 insertions, 23 deletions
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index 965faa7ba53..96dd9356109 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -63,6 +63,7 @@ set(SRC
intern/draw_cache_impl_particles.c
intern/draw_common.c
intern/draw_debug.c
+ intern/draw_hair.c
intern/draw_instance_data.c
intern/draw_manager.c
intern/draw_manager_data.c
@@ -123,6 +124,7 @@ set(SRC
intern/draw_cache_impl.h
intern/draw_common.h
intern/draw_debug.h
+ intern/draw_hair_private.h
intern/draw_instance_data.h
intern/draw_manager.h
intern/draw_manager_text.h
@@ -234,6 +236,8 @@ data_to_c_simple(engines/workbench/shaders/workbench_shadow_debug_frag.glsl SRC)
data_to_c_simple(engines/workbench/shaders/workbench_world_light_lib.glsl SRC)
data_to_c_simple(modes/shaders/common_globals_lib.glsl SRC)
+data_to_c_simple(modes/shaders/common_hair_lib.glsl SRC)
+data_to_c_simple(modes/shaders/common_hair_refine_vert.glsl SRC)
data_to_c_simple(modes/shaders/common_view_lib.glsl SRC)
data_to_c_simple(modes/shaders/common_fxaa_lib.glsl SRC)
data_to_c_simple(modes/shaders/common_fullscreen_vert.glsl SRC)
diff --git a/source/blender/draw/intern/draw_cache_impl_particles.c b/source/blender/draw/intern/draw_cache_impl_particles.c
index c89ea67a39c..26b2efffc86 100644
--- a/source/blender/draw/intern/draw_cache_impl_particles.c
+++ b/source/blender/draw/intern/draw_cache_impl_particles.c
@@ -42,6 +42,7 @@
#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_particle.h"
@@ -54,6 +55,7 @@
#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);
@@ -67,14 +69,6 @@ typedef struct ParticlePointCache {
int point_count;
} ParticlePointCache;
-typedef struct ParticleHairCache {
- Gwn_VertBuf *pos;
- Gwn_IndexBuf *indices;
- Gwn_Batch *hairs;
- int elems_count;
- int point_count;
-} ParticleHairCache;
-
typedef struct ParticleBatchCache {
/* Object mode strands for hair and points for particle,
* strands for paths when in edit mode.
@@ -169,6 +163,30 @@ static void particle_batch_cache_clear_point(ParticlePointCache *point_cache)
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 - 1; ++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);
@@ -185,6 +203,7 @@ static void particle_batch_cache_clear(ParticleSystem *psys)
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);
@@ -204,6 +223,7 @@ static void count_cache_segment_keys(ParticleCacheKey **pathcache,
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;
}
@@ -214,10 +234,12 @@ static void ensure_seg_pt_count(PTCacheEdit *edit,
ParticleSystem *psys,
ParticleHairCache *hair_cache)
{
- if (hair_cache->pos != NULL && hair_cache->indices != NULL) {
+ 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;
@@ -237,6 +259,14 @@ static void ensure_seg_pt_count(PTCacheEdit *edit,
}
}
+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,
@@ -270,6 +300,38 @@ static void particle_calculate_parent_uvs(ParticleSystem *psys,
}
}
+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,
@@ -290,8 +352,37 @@ static void particle_interpolate_children_uvs(ParticleSystem *psys,
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]);
+ 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]);
}
}
}
@@ -342,6 +433,52 @@ static void particle_calculate_uvs(ParticleSystem *psys,
}
}
+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,
@@ -356,9 +493,13 @@ static int particle_batch_cache_fill_segments(
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)
@@ -370,6 +511,10 @@ static int particle_batch_cache_fill_segments(
*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];
@@ -378,6 +523,14 @@ static int particle_batch_cache_fill_segments(
}
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,
@@ -403,6 +556,15 @@ static int particle_batch_cache_fill_segments(
? (*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++;
@@ -418,12 +580,22 @@ static int particle_batch_cache_fill_segments(
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]);
+ (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. */
@@ -434,6 +606,411 @@ static int particle_batch_cache_fill_segments(
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;
+ }
+
+ unsigned int *seg_data = (unsigned int *)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) {
+ 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};
+ unsigned int data_id = GWN_vertformat_attr_add(&format_data, "data", GWN_COMP_U32, 1, GWN_FETCH_INT);
+
+ Gwn_VertFormat format_uv = {0};
+ unsigned int uv_id = GWN_vertformat_attr_add(&format_uv, "uv", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
+
+ Gwn_VertFormat format_col = {0};
+ unsigned int 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);
+ unsigned int 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);
+ unsigned int 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,
@@ -450,15 +1027,26 @@ static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit,
GWN_INDEXBUF_DISCARD_SAFE(hair_cache->indices);
static Gwn_VertFormat format = { 0 };
- static HairAttributeID attr_id;
+ 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);
}
}
@@ -471,6 +1059,7 @@ static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit,
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);
@@ -478,6 +1067,22 @@ static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit,
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");
+ }
}
}
@@ -490,11 +1095,19 @@ static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit,
hair_cache->elems_count, hair_cache->point_count,
true);
- if (num_uv_layers) {
+ if (num_uv_layers || num_col_layers) {
BKE_mesh_tessface_ensure(psmd->mesh_final);
- 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_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);
+ }
}
}
@@ -502,7 +1115,7 @@ static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit,
curr_point = particle_batch_cache_fill_segments(
psys, psmd, edit->pathcache, PARTICLE_SOURCE_PARENT,
0, 0, edit->totcached,
- num_uv_layers, mtfaces, uv_id, &parent_uvs,
+ num_uv_layers, num_col_layers, mtfaces, mcols, uv_id, col_id, &parent_uvs, &parent_mcol,
&elb, &attr_id, hair_cache);
}
else {
@@ -512,7 +1125,7 @@ static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit,
curr_point = particle_batch_cache_fill_segments(
psys, psmd, psys->pathcache, PARTICLE_SOURCE_PARENT,
0, 0, psys->totpart,
- num_uv_layers, mtfaces, uv_id, &parent_uvs,
+ 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) {
@@ -520,7 +1133,7 @@ static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit,
curr_point = particle_batch_cache_fill_segments(
psys, psmd, psys->childcache, PARTICLE_SOURCE_CHILDREN,
psys->totpart, curr_point, child_count,
- num_uv_layers, mtfaces, uv_id, &parent_uvs,
+ num_uv_layers, num_col_layers, mtfaces, mcols, uv_id, col_id, &parent_uvs, &parent_mcol,
&elb, &attr_id, hair_cache);
}
}
@@ -532,9 +1145,18 @@ static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit,
}
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);
}
@@ -575,7 +1197,7 @@ static void particle_batch_cache_ensure_pos(Object *object,
if (format.attrib_ct == 0) {
/* initialize vertex format */
- pos_id = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT);
+ 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);
}
@@ -858,3 +1480,45 @@ Gwn_Batch *DRW_particles_batch_cache_get_edit_tip_points(
NULL);
return cache->edit_tip_points;
}
+
+/* Ensure all textures and buffers needed for GPU accelerated drawing. */
+bool particles_ensure_procedural_data(
+ Object *UNUSED(object),
+ ParticleSystem *psys,
+ ModifierData *md,
+ ParticleHairCache **r_hair_cache,
+ int subdiv,
+ int thickness_res)
+{
+ bool need_ft_update = false;
+
+ ParticleSettings *part = psys->part;
+ ParticleBatchCache *cache = particle_batch_cache_get(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(NULL, psys, &cache->hair);
+ particle_batch_cache_ensure_procedural_pos(NULL, 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(NULL, psys, 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(NULL, psys, &cache->hair, thickness_res, subdiv);
+ }
+
+
+ return need_ft_update;
+} \ No newline at end of file
diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h
index 11cff02d6c9..49a1ae42198 100644
--- a/source/blender/draw/intern/draw_common.h
+++ b/source/blender/draw/intern/draw_common.h
@@ -29,8 +29,12 @@
struct DRWPass;
struct DRWShadingGroup;
struct Gwn_Batch;
+struct GPUMaterial;
struct Object;
struct ViewLayer;
+struct ModifierData;
+struct ParticleSystem;
+struct PTCacheEdit;
/* Used as ubo but colors can be directly referenced as well */
/* Keep in sync with: common_globals_lib.glsl (globalsBlock) */
@@ -145,6 +149,22 @@ void DRW_shgroup_armature_object(struct Object *ob, struct ViewLayer *view_layer
void DRW_shgroup_armature_pose(struct Object *ob, struct DRWArmaturePasses passes);
void DRW_shgroup_armature_edit(struct Object *ob, struct DRWArmaturePasses passes);
+/* draw_hair.c */
+
+/* This creates a shading group with display hairs.
+ * The draw call is already added by this function, just add additional uniforms. */
+struct DRWShadingGroup *DRW_shgroup_hair_create(
+ struct Object *object, struct ParticleSystem *psys, struct ModifierData *md,
+ struct DRWPass *hair_pass, struct DRWPass *tf_pass,
+ struct GPUShader *shader);
+
+struct DRWShadingGroup *DRW_shgroup_material_hair_create(
+ struct Object *object, struct ParticleSystem *psys, struct ModifierData *md,
+ struct DRWPass *hair_pass, struct DRWPass *tf_pass,
+ struct GPUMaterial *material);
+
+void DRW_hair_free(void);
+
/* pose_mode.c */
bool DRW_pose_mode_armature(
struct Object *ob, struct Object *active_ob);
diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c
new file mode 100644
index 00000000000..c292415d38c
--- /dev/null
+++ b/source/blender/draw/intern/draw_hair.c
@@ -0,0 +1,167 @@
+/*
+ * ***** 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_hair.c
+ * \ingroup draw
+ *
+ * \brief Contains procedural GPU hair drawing methods.
+ */
+
+#include "DRW_render.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_string_utils.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_particle.h"
+#include "BKE_pointcache.h"
+
+#include "ED_particle.h"
+
+#include "GPU_batch.h"
+#include "GPU_shader.h"
+
+#include "draw_hair_private.h"
+
+typedef enum ParticleRefineShader {
+ PART_REFINE_CATMULL_ROM = 0,
+ PART_REFINE_MAX_SHADER,
+} ParticleRefineShader;
+
+static GPUShader *g_refine_shaders[PART_REFINE_MAX_SHADER] = {NULL};
+
+extern char datatoc_common_hair_lib_glsl[];
+extern char datatoc_common_hair_refine_vert_glsl[];
+
+static GPUShader *hair_refine_shader_get(ParticleRefineShader sh)
+{
+ if (g_refine_shaders[sh]) {
+ return g_refine_shaders[sh];
+ }
+
+ char *vert_with_lib = BLI_string_joinN(datatoc_common_hair_lib_glsl, datatoc_common_hair_refine_vert_glsl);
+
+ const char *var_names[1] = {"outData"};
+
+ g_refine_shaders[sh] = DRW_shader_create_with_transform_feedback(vert_with_lib, NULL, "#define HAIR_PHASE_SUBDIV\n",
+ GPU_SHADER_TFB_POINTS, var_names, 1);
+
+ MEM_freeN(vert_with_lib);
+
+ return g_refine_shaders[sh];
+}
+
+static DRWShadingGroup *drw_shgroup_create_hair_procedural_ex(
+ Object *object, ParticleSystem *psys, ModifierData *md,
+ DRWPass *hair_pass, DRWPass *tf_pass,
+ struct GPUMaterial *gpu_mat, GPUShader *gpu_shader)
+{
+ /* TODO(fclem): Pass the scene as parameter */
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ Scene *scene = draw_ctx->scene;
+
+ int subdiv = scene->r.hair_subdiv;
+ int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
+
+ ParticleHairCache *hair_cache;
+ ParticleSettings *part = psys->part;
+ bool need_ft_update = particles_ensure_procedural_data(object, psys, md, &hair_cache, subdiv, thickness_res);
+
+ DRWShadingGroup *shgrp;
+ if (gpu_mat) {
+ shgrp = DRW_shgroup_material_create(gpu_mat, hair_pass);
+ }
+ else if (gpu_shader) {
+ shgrp = DRW_shgroup_create(gpu_shader, hair_pass);
+ }
+ else {
+ BLI_assert(0);
+ }
+
+ /* TODO optimize this. Only bind the ones GPUMaterial needs. */
+ for (int i = 0; i < hair_cache->num_uv_layers; ++i) {
+ for (int n = 0; hair_cache->uv_layer_names[i][n][0] != '\0'; ++n) {
+ DRW_shgroup_uniform_texture(shgrp, hair_cache->uv_layer_names[i][n], hair_cache->uv_tex[i]);
+ }
+ }
+ for (int i = 0; i < hair_cache->num_col_layers; ++i) {
+ for (int n = 0; hair_cache->col_layer_names[i][n][0] != '\0'; ++n) {
+ DRW_shgroup_uniform_texture(shgrp, hair_cache->col_layer_names[i][n], hair_cache->col_tex[i]);
+ }
+ }
+
+ DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", hair_cache->final[subdiv].proc_tex);
+ DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &hair_cache->final[subdiv].strands_res, 1);
+ DRW_shgroup_uniform_int_copy(shgrp, "hairThicknessRes", thickness_res);
+ DRW_shgroup_uniform_float(shgrp, "hairRadShape", &part->shape, 1);
+ DRW_shgroup_uniform_float_copy(shgrp, "hairRadRoot", part->rad_root * part->rad_scale * 0.5f);
+ DRW_shgroup_uniform_float_copy(shgrp, "hairRadTip", part->rad_tip * part->rad_scale * 0.5f);
+ DRW_shgroup_uniform_bool_copy(shgrp, "hairCloseTip", (part->shape_flag & PART_SHAPE_CLOSE_TIP) != 0);
+ /* TODO(fclem): Until we have a better way to cull the hair and render with orco, bypass culling test. */
+ DRW_shgroup_call_object_add_no_cull(shgrp, hair_cache->final[subdiv].proc_hairs[thickness_res-1], object);
+
+ /* Transform Feedback subdiv. */
+ if (need_ft_update) {
+ int final_points_ct = hair_cache->final[subdiv].strands_res * hair_cache->strands_count;
+ GPUShader *tf_shader = hair_refine_shader_get(PART_REFINE_CATMULL_ROM);
+ DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create(tf_shader, tf_pass,
+ hair_cache->final[subdiv].proc_buf);
+ DRW_shgroup_uniform_texture(tf_shgrp, "hairPointBuffer", hair_cache->point_tex);
+ DRW_shgroup_uniform_texture(tf_shgrp, "hairStrandBuffer", hair_cache->strand_tex);
+ DRW_shgroup_uniform_int(tf_shgrp, "hairStrandsRes", &hair_cache->final[subdiv].strands_res, 1);
+ DRW_shgroup_call_procedural_points_add(tf_shgrp, final_points_ct, NULL);
+ }
+
+ return shgrp;
+}
+
+DRWShadingGroup *DRW_shgroup_hair_create(
+ Object *object, ParticleSystem *psys, ModifierData *md,
+ DRWPass *hair_pass, DRWPass *tf_pass,
+ GPUShader *shader)
+{
+ return drw_shgroup_create_hair_procedural_ex(object, psys, md, hair_pass, tf_pass, NULL, shader);
+}
+
+DRWShadingGroup *DRW_shgroup_material_hair_create(
+ Object *object, ParticleSystem *psys, ModifierData *md,
+ DRWPass *hair_pass, DRWPass *tf_pass,
+ struct GPUMaterial *material)
+{
+ return drw_shgroup_create_hair_procedural_ex(object, psys, md, hair_pass, tf_pass, material, NULL);
+}
+
+void DRW_hair_free(void)
+{
+ for (int i = 0; i < PART_REFINE_MAX_SHADER; ++i) {
+ DRW_SHADER_FREE_SAFE(g_refine_shaders[i]);
+ }
+} \ No newline at end of file
diff --git a/source/blender/draw/intern/draw_hair_private.h b/source/blender/draw/intern/draw_hair_private.h
new file mode 100644
index 00000000000..2e6c9a0d3c1
--- /dev/null
+++ b/source/blender/draw/intern/draw_hair_private.h
@@ -0,0 +1,92 @@
+/*
+ * ***** 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_hair_private.h
+ * \ingroup draw
+ */
+
+#ifndef __DRAW_HAIR_PRIVATE_H__
+#define __DRAW_HAIR_PRIVATE_H__
+
+#define MAX_LAYER_NAME_CT 3 /* u0123456789, u, a0123456789 */
+#define MAX_LAYER_NAME_LEN DECIMAL_DIGITS_BOUND(unsigned int) + 2
+#define MAX_THICKRES 2 /* see eHairType */
+#define MAX_HAIR_SUBDIV 4 /* see hair_subdiv rna */
+
+struct Object;
+struct ParticleSystem;
+struct ModifierData;
+struct ParticleHairCache;
+
+typedef struct ParticleHairFinalCache {
+ /* Output of the subdivision stage: vertex buff sized to subdiv level. */
+ Gwn_VertBuf *proc_buf;
+ GPUTexture *proc_tex;
+
+ /* Just contains a huge index buffer used to draw the final hair. */
+ Gwn_Batch *proc_hairs[MAX_THICKRES];
+
+ int strands_res; /* points per hair, at least 2 */
+} ParticleHairFinalCache;
+
+typedef struct ParticleHairCache {
+ Gwn_VertBuf *pos;
+ Gwn_IndexBuf *indices;
+ Gwn_Batch *hairs;
+
+ /* Hair Procedural display: Interpolation is done on the GPU. */
+ Gwn_VertBuf *proc_point_buf; /* Input control points */
+ GPUTexture *point_tex;
+
+ Gwn_VertBuf *proc_strand_buf; /* Infos of control points strands (segment count and base index) */
+ GPUTexture *strand_tex;
+
+ Gwn_VertBuf *proc_uv_buf[MAX_MTFACE];
+ GPUTexture *uv_tex[MAX_MTFACE];
+ char uv_layer_names[MAX_MTFACE][MAX_LAYER_NAME_CT][MAX_LAYER_NAME_LEN];
+
+ Gwn_VertBuf *proc_col_buf[MAX_MCOL];
+ GPUTexture *col_tex[MAX_MCOL];
+ char col_layer_names[MAX_MCOL][MAX_LAYER_NAME_CT][MAX_LAYER_NAME_LEN];
+
+ int num_uv_layers;
+ int num_col_layers;
+
+ ParticleHairFinalCache final[MAX_HAIR_SUBDIV];
+
+ int strands_count;
+ int elems_count;
+ int point_count;
+} ParticleHairCache;
+
+bool particles_ensure_procedural_data(
+ struct Object *object,
+ struct ParticleSystem *psys,
+ struct ModifierData *md,
+ struct ParticleHairCache **r_hair_cache,
+ int subdiv,
+ int thickness_res);
+
+#endif /* __DRAW_HAIR_PRIVATE_H__ */
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
index a57a91363eb..fd233597daa 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -2098,6 +2098,7 @@ void DRW_engines_free(void)
DRW_TEXTURE_FREE_SAFE(g_select_buffer.texture_depth);
GPU_FRAMEBUFFER_FREE_SAFE(g_select_buffer.framebuffer);
+ DRW_hair_free();
DRW_shape_cache_free();
DRW_stats_free();
DRW_globals_free();
diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c
index b6aac33c0f1..105eab5a958 100644
--- a/source/blender/draw/intern/draw_manager_data.c
+++ b/source/blender/draw/intern/draw_manager_data.c
@@ -871,6 +871,7 @@ DRWShadingGroup *DRW_shgroup_empty_tri_batch_create(struct GPUShader *shader, DR
DRWShadingGroup *DRW_shgroup_transform_feedback_create(struct GPUShader *shader, DRWPass *pass, Gwn_VertBuf *tf_target)
{
+ BLI_assert(tf_target != NULL);
DRWShadingGroup *shgroup = drw_shgroup_create_ex(shader, pass);
shgroup->type = DRW_SHG_FEEDBACK_TRANSFORM;
diff --git a/source/blender/draw/modes/shaders/common_hair_lib.glsl b/source/blender/draw/modes/shaders/common_hair_lib.glsl
new file mode 100644
index 00000000000..3fe44b4e142
--- /dev/null
+++ b/source/blender/draw/modes/shaders/common_hair_lib.glsl
@@ -0,0 +1,190 @@
+/**
+ * Library to create hairs dynamically from control points.
+ * This is less bandwidth intensive than fetching the vertex attributes
+ * but does more ALU work per vertex. This also reduce the number
+ * of data the CPU has to precompute and transfert for each update.
+ **/
+
+/**
+ * hairStrandsRes: Number of points per hair strand.
+ * 2 - no subdivision
+ * 3+ - 1 or more interpolated points per hair.
+ **/
+uniform int hairStrandsRes = 8;
+
+/**
+ * hairThicknessRes : Subdiv around the hair.
+ * 1 - Wire Hair: Only one pixel thick, independant of view distance.
+ * 2 - Polystrip Hair: Correct width, flat if camera is parallel.
+ * 3+ - Cylinder Hair: Massive calculation but potentially perfect. Still need proper support.
+ **/
+uniform int hairThicknessRes = 1;
+
+/* Hair thickness shape. */
+uniform float hairRadRoot = 0.01;
+uniform float hairRadTip = 0.0;
+uniform float hairRadShape = 0.5;
+uniform bool hairCloseTip = true;
+
+/* -- Per control points -- */
+uniform samplerBuffer hairPointBuffer; /* RGBA32F */
+#define point_position xyz
+#define point_time w /* Position along the hair length */
+
+/* -- Per strands data -- */
+uniform usamplerBuffer hairStrandBuffer; /* R32UI */
+
+/* Not used, use one buffer per uv layer */
+//uniform samplerBuffer hairUVBuffer; /* RG32F */
+//uniform samplerBuffer hairColBuffer; /* RGBA16 linear color */
+
+void unpack_strand_data(uint data, out int strand_offset, out int strand_segments)
+{
+#if 0 /* Pack point count */
+ // strand_offset = (data & 0x1FFFFFFFu);
+ // strand_segments = 1u << (data >> 29u); /* We only need 3 bits to store subdivision level. */
+#else
+ strand_offset = int(data & 0x00FFFFFFu);
+ strand_segments = int(data >> 24u);
+#endif
+}
+
+/* -- Subdivision stage -- */
+/**
+ * We use a transform feedback to preprocess the strands and add more subdivision to it.
+ * For the moment theses are simple smooth interpolation but one could hope to see the full
+ * children particle modifiers being evaluated at this stage.
+ *
+ * If no more subdivision is needed, we can skip this step.
+ **/
+
+#ifdef HAIR_PHASE_SUBDIV
+int hair_get_base_id(float local_time, int strand_segments, out float interp_time)
+{
+ float time_per_strand_seg = 1.0 / float(strand_segments);
+
+ float ratio = local_time / time_per_strand_seg;
+ interp_time = fract(ratio);
+
+ return int(ratio);
+}
+
+void hair_get_interp_attribs(out vec4 data0, out vec4 data1, out vec4 data2, out vec4 data3, out float interp_time)
+{
+ float local_time = float(gl_VertexID % hairStrandsRes) / float(hairStrandsRes - 1);
+
+ int hair_id = gl_VertexID / hairStrandsRes;
+ uint strand_data = texelFetch(hairStrandBuffer, hair_id).x;
+
+ int strand_offset, strand_segments;
+ unpack_strand_data(strand_data, strand_offset, strand_segments);
+
+ int id = hair_get_base_id(local_time, strand_segments, interp_time);
+
+ int ofs_id = id + strand_offset;
+
+ data0 = texelFetch(hairPointBuffer, ofs_id - 1);
+ data1 = texelFetch(hairPointBuffer, ofs_id);
+ data2 = texelFetch(hairPointBuffer, ofs_id + 1);
+ data3 = texelFetch(hairPointBuffer, ofs_id + 2);
+
+ if (id <= 0) {
+ /* root points. Need to reconstruct previous data. */
+ data0 = data1 * 2.0 - data2;
+ }
+ if (id + 1 >= strand_segments) {
+ /* tip points. Need to reconstruct next data. */
+ data3 = data2 * 2.0 - data1;
+ }
+}
+#endif
+
+/* -- Drawing stage -- */
+/**
+ * For final drawing, the vertex index and the number of vertex per segment
+ **/
+
+#ifndef HAIR_PHASE_SUBDIV
+int hair_get_strand_id(void)
+{
+ return gl_VertexID / (hairStrandsRes * hairThicknessRes);
+}
+
+int hair_get_base_id(void)
+{
+ return gl_VertexID / hairThicknessRes;
+}
+
+/* Copied from cycles. */
+float hair_shaperadius(float shape, float root, float tip, float time)
+{
+ float radius = 1.0 - time;
+
+ if (shape < 0.0) {
+ radius = pow(radius, 1.0 + shape);
+ }
+ else {
+ radius = pow(radius, 1.0 / (1.0 - shape));
+ }
+
+ if (hairCloseTip && (time > 0.99)) {
+ return 0.0;
+ }
+
+ return (radius * (root - tip)) + tip;
+}
+
+void hair_get_pos_tan_nor_time(
+ bool is_persp, vec3 camera_pos, vec3 camera_z,
+ out vec3 wpos, out vec3 wtan, out vec3 wnor, out float time, out float thickness, out float thick_time)
+{
+ int id = hair_get_base_id();
+ vec4 data = texelFetch(hairPointBuffer, id);
+ wpos = data.point_position;
+ time = data.point_time;
+ if (time == 0.0) {
+ /* Hair root */
+ wtan = texelFetch(hairPointBuffer, id + 1).point_position - wpos;
+ }
+ else {
+ wtan = wpos - texelFetch(hairPointBuffer, id - 1).point_position;
+ }
+
+ vec3 camera_vec = (is_persp) ? wpos - camera_pos : -camera_z;
+ wnor = normalize(cross(camera_vec, wtan));
+
+ thickness = hair_shaperadius(hairRadShape, hairRadRoot, hairRadTip, time);
+
+ if (hairThicknessRes > 1) {
+ thick_time = float(gl_VertexID % hairThicknessRes) / float(hairThicknessRes - 1);
+ thick_time = thickness * (thick_time * 2.0 - 1.0);
+
+ wpos += wnor * thick_time;
+ }
+}
+
+vec2 hair_get_customdata_vec2(const samplerBuffer cd_buf)
+{
+ int id = hair_get_strand_id();
+ return texelFetch(cd_buf, id).rg;
+}
+
+vec3 hair_get_customdata_vec3(const samplerBuffer cd_buf)
+{
+ int id = hair_get_strand_id();
+ return texelFetch(cd_buf, id).rgb;
+}
+
+vec4 hair_get_customdata_vec4(const samplerBuffer cd_buf)
+{
+ int id = hair_get_strand_id();
+ return texelFetch(cd_buf, id).rgba;
+}
+
+vec3 hair_get_strand_pos(void)
+{
+ int id = hair_get_strand_id() * hairStrandsRes;
+ return texelFetch(hairPointBuffer, id).point_position;
+}
+
+#endif
diff --git a/source/blender/draw/modes/shaders/common_hair_refine_vert.glsl b/source/blender/draw/modes/shaders/common_hair_refine_vert.glsl
new file mode 100644
index 00000000000..48944722913
--- /dev/null
+++ b/source/blender/draw/modes/shaders/common_hair_refine_vert.glsl
@@ -0,0 +1,15 @@
+
+/* To be compiled with common_hair_lib.glsl */
+
+out vec4 outData;
+
+void main(void)
+{
+ float interp_time;
+ vec4 data0, data1, data2, data3;
+ hair_get_interp_attribs(data0, data1, data2, data3, interp_time);
+
+ /* TODO some interpolation. */
+
+ outData = mix(data1, data2, interp_time);
+} \ No newline at end of file