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