From 0eb32ab22809d9c0c41bfff5082f58b1a7aa9965 Mon Sep 17 00:00:00 2001 From: Luca Rood Date: Tue, 9 May 2017 16:23:47 +0200 Subject: Implement hair drawing with Draw Manager in Clay engine Part of T51378 --- source/blender/blenkernel/BKE_particle.h | 7 + source/blender/blenkernel/intern/particle.c | 19 ++ source/blender/blenkernel/intern/particle_system.c | 2 + source/blender/blenloader/intern/readfile.c | 2 + source/blender/draw/CMakeLists.txt | 3 + source/blender/draw/engines/clay/clay_engine.c | 211 ++++++++++++++- .../engines/clay/shaders/particle_strand_frag.glsl | 46 ++++ .../draw/engines/clay/shaders/particle_vert.glsl | 34 +++ source/blender/draw/intern/draw_cache.c | 11 + source/blender/draw/intern/draw_cache.h | 3 + source/blender/draw/intern/draw_cache_impl.h | 7 + .../draw/intern/draw_cache_impl_particles.c | 282 +++++++++++++++++++++ source/blender/draw/intern/draw_manager.c | 6 + source/blender/draw/modes/object_mode.c | 2 +- source/blender/makesdna/DNA_particle_types.h | 2 + source/blender/makesrna/intern/rna_scene.c | 61 +++++ 16 files changed, 695 insertions(+), 3 deletions(-) create mode 100644 source/blender/draw/engines/clay/shaders/particle_strand_frag.glsl create mode 100644 source/blender/draw/engines/clay/shaders/particle_vert.glsl create mode 100644 source/blender/draw/intern/draw_cache_impl_particles.c (limited to 'source') diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h index 2b6a84a2f87..90f7703b967 100644 --- a/source/blender/blenkernel/BKE_particle.h +++ b/source/blender/blenkernel/BKE_particle.h @@ -479,3 +479,10 @@ void BKE_particle_system_eval(struct EvaluationContext *eval_ctx, struct ParticleSystem *psys); #endif + +/* Draw Cache */ +enum { + BKE_PARTICLE_BATCH_DIRTY_ALL = 0, +}; +void BKE_particle_batch_cache_dirty(struct ParticleSystem *psys, int mode); +void BKE_particle_batch_cache_free(struct ParticleSystem *psys); diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index b6f5f11846b..71257fdc4b4 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -4305,3 +4305,22 @@ void psys_apply_hair_lattice(Scene *scene, Object *ob, ParticleSystem *psys) psys->flag |= PSYS_EDITED; } } + + + +/* Draw Engine */ +void (*BKE_particle_batch_cache_dirty_cb)(ParticleSystem *psys, int mode) = NULL; +void (*BKE_particle_batch_cache_free_cb)(ParticleSystem *psys) = NULL; + +void BKE_particle_batch_cache_dirty(ParticleSystem *psys, int mode) +{ + if (psys->batch_cache) { + BKE_particle_batch_cache_dirty_cb(psys, mode); + } +} +void BKE_particle_batch_cache_free(ParticleSystem *psys) +{ + if (psys->batch_cache) { + BKE_particle_batch_cache_free_cb(psys); + } +} diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index c26ac056499..2da0b6aeb4f 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -4321,6 +4321,8 @@ void particle_system_update(Scene *scene, Object *ob, ParticleSystem *psys, cons /* save matrix for duplicators, at rendertime the actual dupliobject's matrix is used so don't update! */ if (psys->renderdata==0) invert_m4_m4(psys->imat, ob->obmat); + + BKE_particle_batch_cache_dirty(psys, BKE_PARTICLE_BATCH_DIRTY_ALL); } /* ID looper */ diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index bd2180c28a2..cac1ec084b5 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -4372,6 +4372,8 @@ static void direct_link_particlesystems(FileData *fd, ListBase *particles) psys->tree = NULL; psys->bvhtree = NULL; + + psys->batch_cache = NULL; } return; } diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index fa625f608a1..45c52187f72 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -59,6 +59,7 @@ set(SRC intern/draw_cache_impl_displist.c intern/draw_cache_impl_lattice.c intern/draw_cache_impl_mesh.c + intern/draw_cache_impl_particles.c intern/draw_common.c intern/draw_manager.c intern/draw_manager_text.c @@ -108,6 +109,8 @@ data_to_c_simple(engines/clay/shaders/clay_frag.glsl SRC) data_to_c_simple(engines/clay/shaders/clay_vert.glsl SRC) data_to_c_simple(engines/clay/shaders/ssao_alchemy.glsl SRC) data_to_c_simple(engines/clay/shaders/ssao_groundtruth.glsl SRC) +data_to_c_simple(engines/clay/shaders/particle_vert.glsl SRC) +data_to_c_simple(engines/clay/shaders/particle_strand_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/default_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/default_world_frag.glsl SRC) diff --git a/source/blender/draw/engines/clay/clay_engine.c b/source/blender/draw/engines/clay/clay_engine.c index a405f26ea8e..95ddb3cc6fa 100644 --- a/source/blender/draw/engines/clay/clay_engine.c +++ b/source/blender/draw/engines/clay/clay_engine.c @@ -21,9 +21,12 @@ #include "DRW_render.h" +#include "DNA_particle_types.h" + #include "BKE_icons.h" #include "BKE_idprop.h" #include "BKE_main.h" +#include "BKE_particle.h" #include "BLI_dynstr.h" #include "BLI_rand.h" @@ -43,6 +46,8 @@ extern char datatoc_clay_frag_glsl[]; extern char datatoc_clay_vert_glsl[]; extern char datatoc_ssao_alchemy_glsl[]; +extern char datatoc_particle_vert_glsl[]; +extern char datatoc_particle_strand_frag_glsl[]; /* *********** LISTS *********** */ @@ -59,24 +64,45 @@ typedef struct CLAY_UBO_Material { } CLAY_UBO_Material; /* 48 bytes */ BLI_STATIC_ASSERT_ALIGN(CLAY_UBO_Material, 16); +typedef struct CLAY_HAIR_UBO_Material { + float hair_world; + float hair_diffuse; + float hair_specular; + float hair_hardness; + float hair_randomicity; + float pad1[3]; + float hair_diffuse_color[3]; + float pad2; + float hair_specular_color[3]; + float pad3; +} CLAY_HAIR_UBO_Material; /* 48 bytes */ + #define MAX_CLAY_MAT 512 /* 512 = 9 bit material id */ typedef struct CLAY_UBO_Storage { CLAY_UBO_Material materials[MAX_CLAY_MAT]; } CLAY_UBO_Storage; +typedef struct CLAY_HAIR_UBO_Storage { + CLAY_HAIR_UBO_Material materials[MAX_CLAY_MAT]; +} CLAY_HAIR_UBO_Storage; + /* GPUViewport.storage * Is freed everytime the viewport engine changes */ typedef struct CLAY_Storage { /* Materials Parameter UBO */ CLAY_UBO_Storage mat_storage; + CLAY_HAIR_UBO_Storage hair_mat_storage; int ubo_current_id; + int hair_ubo_current_id; DRWShadingGroup *shgrps[MAX_CLAY_MAT]; + DRWShadingGroup *hair_shgrps[MAX_CLAY_MAT]; } CLAY_Storage; typedef struct CLAY_StorageList { struct CLAY_Storage *storage; struct GPUUniformBuffer *mat_ubo; + struct GPUUniformBuffer *hair_mat_ubo; struct CLAY_PrivateData *g_data; } CLAY_StorageList; @@ -99,6 +125,7 @@ typedef struct CLAY_PassList { struct DRWPass *depth_pass; struct DRWPass *depth_pass_cull; struct DRWPass *clay_pass; + struct DRWPass *hair_pass; } CLAY_PassList; typedef struct CLAY_Data { @@ -116,6 +143,7 @@ static struct { struct GPUShader *depth_sh; /* Shading Pass */ struct GPUShader *clay_sh; + struct GPUShader *hair_sh; /* Matcap textures */ struct GPUTexture *matcap_array; @@ -129,8 +157,12 @@ static struct { struct GPUTexture *jitter_tx; struct GPUTexture *sampling_tx; + /* hair */ + float hair_light[3]; + /* Just a serie of int from 0 to MAX_CLAY_MAT-1 */ int ubo_mat_idxs[MAX_CLAY_MAT]; + int hair_ubo_mat_idxs[MAX_CLAY_MAT]; } e_data = {NULL}; /* Engine data */ typedef struct CLAY_PrivateData { @@ -140,6 +172,7 @@ typedef struct CLAY_PrivateData { DRWShadingGroup *depth_shgrp_cull; DRWShadingGroup *depth_shgrp_cull_select; DRWShadingGroup *depth_shgrp_cull_active; + DRWShadingGroup *hair; } CLAY_PrivateData; /* Transient data */ /* Functions */ @@ -324,6 +357,10 @@ static void CLAY_engine_init(void *vedata) MEM_freeN(matcap_with_ao); } + if (!e_data.hair_sh) { + e_data.hair_sh = DRW_shader_create(datatoc_particle_vert_glsl, NULL, datatoc_particle_strand_frag_glsl, "#define MAX_MATERIAL 512\n"); + } + if (!stl->storage) { stl->storage = MEM_callocN(sizeof(CLAY_Storage), "CLAY_Storage"); } @@ -332,6 +369,10 @@ static void CLAY_engine_init(void *vedata) stl->mat_ubo = DRW_uniformbuffer_create(sizeof(CLAY_UBO_Storage), NULL); } + if (!stl->hair_mat_ubo) { + stl->hair_mat_ubo = DRW_uniformbuffer_create(sizeof(CLAY_HAIR_UBO_Storage), NULL); + } + if (e_data.ubo_mat_idxs[1] == 0) { /* Just int to have pointers to them */ for (int i = 0; i < MAX_CLAY_MAT; ++i) { @@ -413,6 +454,13 @@ static void CLAY_engine_init(void *vedata) e_data.cached_sample_num = ssao_samples; } } + + /* hair setup */ + { + e_data.hair_light[0] = 1.0f; + e_data.hair_light[1] = -0.5f; + e_data.hair_light[2] = -0.7f; + } } static DRWShadingGroup *CLAY_shgroup_create(CLAY_Data *vedata, DRWPass *pass, int *material_id) @@ -437,6 +485,16 @@ static DRWShadingGroup *CLAY_shgroup_create(CLAY_Data *vedata, DRWPass *pass, in return grp; } +static DRWShadingGroup *CLAY_hair_shgroup_create(DRWPass *pass, int *material_id) +{ + DRWShadingGroup *grp = DRW_shgroup_create(e_data.hair_sh, pass); + + DRW_shgroup_uniform_vec3(grp, "light", e_data.hair_light, 1); + DRW_shgroup_uniform_int(grp, "mat_id", material_id, 1); + + return grp; +} + static int search_mat_to_ubo( CLAY_Storage *storage, float matcap_rot, float matcap_hue, float matcap_sat, float matcap_val, float ssao_distance, float ssao_factor_cavity, @@ -464,6 +522,36 @@ static int search_mat_to_ubo( return -1; } +static int search_hair_mat_to_ubo(CLAY_Storage *storage, float hair_world, float hair_diffuse, float hair_specular, + float hair_hardness, float hair_randomicity, const float *hair_diff_color, + const float *hair_spec_color) +{ + /* For now just use a linear search and test all parameters */ + /* TODO make a hash table */ + for (int i = 0; i < storage->hair_ubo_current_id; ++i) { + CLAY_HAIR_UBO_Material *ubo = &storage->hair_mat_storage.materials[i]; + + if ((ubo->hair_world == hair_world) && + (ubo->hair_diffuse == hair_diffuse) && + (ubo->hair_specular == hair_specular) && + (ubo->hair_hardness == hair_hardness) && + (ubo->hair_randomicity == hair_randomicity) && + (ubo->hair_diffuse_color[0] == hair_diff_color[0]) && + (ubo->hair_diffuse_color[1] == hair_diff_color[1]) && + (ubo->hair_diffuse_color[2] == hair_diff_color[2]) && + (ubo->hair_diffuse_color[3] == hair_diff_color[3]) && + (ubo->hair_specular_color[0] == hair_spec_color[0]) && + (ubo->hair_specular_color[1] == hair_spec_color[1]) && + (ubo->hair_specular_color[2] == hair_spec_color[2]) && + (ubo->hair_specular_color[3] == hair_spec_color[3])) + { + return i; + } + } + + return -1; +} + static int push_mat_to_ubo(CLAY_Storage *storage, float matcap_rot, float matcap_hue, float matcap_sat, float matcap_val, float ssao_distance, float ssao_factor_cavity, float ssao_factor_edge, float ssao_attenuation, int matcap_icon) @@ -491,6 +579,32 @@ static int push_mat_to_ubo(CLAY_Storage *storage, float matcap_rot, float matcap return id; } +static int push_hair_mat_to_ubo(CLAY_Storage *storage, float hair_world, float hair_diffuse, float hair_specular, + float hair_hardness, float hair_randomicity, const float *hair_diff_color, + const float *hair_spec_color) +{ + int id = storage->hair_ubo_current_id; + CLAY_HAIR_UBO_Material *ubo = &storage->hair_mat_storage.materials[id]; + + ubo->hair_world = hair_world; + ubo->hair_diffuse = hair_diffuse; + ubo->hair_specular = hair_specular; + ubo->hair_hardness = hair_hardness; + ubo->hair_randomicity = hair_randomicity; + ubo->hair_diffuse_color[0] = hair_diff_color[0]; + ubo->hair_diffuse_color[1] = hair_diff_color[1]; + ubo->hair_diffuse_color[2] = hair_diff_color[2]; + ubo->hair_diffuse_color[3] = hair_diff_color[3]; + ubo->hair_specular_color[0] = hair_spec_color[0]; + ubo->hair_specular_color[1] = hair_spec_color[1]; + ubo->hair_specular_color[2] = hair_spec_color[2]; + ubo->hair_specular_color[3] = hair_spec_color[3]; + + storage->hair_ubo_current_id++; + + return id; +} + static int mat_in_ubo(CLAY_Storage *storage, float matcap_rot, float matcap_hue, float matcap_sat, float matcap_val, float ssao_distance, float ssao_factor_cavity, float ssao_factor_edge, float ssao_attenuation, int matcap_icon) @@ -510,6 +624,23 @@ static int mat_in_ubo(CLAY_Storage *storage, float matcap_rot, float matcap_hue, return id; } +static int hair_mat_in_ubo(CLAY_Storage *storage, float hair_world, float hair_diffuse, float hair_specular, + float hair_hardness, float hair_randomicity, const float *hair_diff_color, + const float *hair_spec_color) +{ + /* Search material in UBO */ + int id = search_hair_mat_to_ubo(storage, hair_world, hair_diffuse, hair_specular, + hair_hardness, hair_randomicity, hair_diff_color, hair_spec_color); + + /* if not found create it */ + if (id == -1) { + id = push_hair_mat_to_ubo(storage, hair_world, hair_diffuse, hair_specular, + hair_hardness, hair_randomicity, hair_diff_color, hair_spec_color); + } + + return id; +} + static DRWShadingGroup *CLAY_object_shgrp_get(CLAY_Data *vedata, Object *ob, CLAY_StorageList *stl, CLAY_PassList *psl) { DRWShadingGroup **shgrps = stl->storage->shgrps; @@ -541,6 +672,34 @@ static DRWShadingGroup *CLAY_object_shgrp_get(CLAY_Data *vedata, Object *ob, CLA return shgrps[id]; } +static DRWShadingGroup *CLAY_hair_shgrp_get(Object *ob, CLAY_StorageList *stl, CLAY_PassList *psl) +{ + DRWShadingGroup **hair_shgrps = stl->storage->hair_shgrps; + IDProperty *props = BKE_layer_collection_engine_evaluated_get(ob, COLLECTION_MODE_NONE, RE_engine_id_BLENDER_CLAY); + + /* Default Settings */ + float hair_world = BKE_collection_engine_property_value_get_float(props, "world_intensity"); + float hair_diffuse = BKE_collection_engine_property_value_get_float(props, "diffuse_intensity"); + float hair_specular = BKE_collection_engine_property_value_get_float(props, "specular_intensity"); + float hair_hardness = BKE_collection_engine_property_value_get_float(props, "specular_hardness"); + float hair_randomicity = BKE_collection_engine_property_value_get_float(props, "color_randomicity"); + const float *hair_diff_color = BKE_collection_engine_property_value_get_float_array(props, "hair_diffuse_color"); + const float *hair_spec_color = BKE_collection_engine_property_value_get_float_array(props, "hair_specular_color"); + + int hair_id = hair_mat_in_ubo(stl->storage, hair_world, hair_diffuse, hair_specular, + hair_hardness, hair_randomicity, hair_diff_color, hair_spec_color); + + if (hair_shgrps[hair_id] == NULL) { + hair_shgrps[hair_id] = CLAY_hair_shgroup_create(psl->hair_pass, &e_data.hair_ubo_mat_idxs[hair_id]); + /* if it's the first shgrp, pass bind the material UBO */ + if (stl->storage->hair_ubo_current_id == 1) { + DRW_shgroup_uniform_block(hair_shgrps[0], "material_block", stl->hair_mat_ubo); + } + } + + return hair_shgrps[hair_id]; +} + static void CLAY_cache_init(void *vedata) { CLAY_PassList *psl = ((CLAY_Data *)vedata)->psl; @@ -568,6 +727,13 @@ static void CLAY_cache_init(void *vedata) stl->storage->ubo_current_id = 0; memset(stl->storage->shgrps, 0, sizeof(DRWShadingGroup *) * MAX_CLAY_MAT); } + + /* Hair Pass */ + { + psl->hair_pass = DRW_pass_create("Hair Pass", DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS); + stl->storage->hair_ubo_current_id = 0; + memset(stl->storage->hair_shgrps, 0, sizeof(DRWShadingGroup *) * MAX_CLAY_MAT); + } } static void CLAY_cache_populate(void *vedata, Object *ob) @@ -575,7 +741,7 @@ static void CLAY_cache_populate(void *vedata, Object *ob) CLAY_PassList *psl = ((CLAY_Data *)vedata)->psl; CLAY_StorageList *stl = ((CLAY_Data *)vedata)->stl; - DRWShadingGroup *clay_shgrp; + DRWShadingGroup *clay_shgrp, *hair_shgrp; if (!DRW_object_is_renderable(ob)) return; @@ -592,6 +758,35 @@ static void CLAY_cache_populate(void *vedata, Object *ob) clay_shgrp = CLAY_object_shgrp_get(vedata, ob, stl, psl); DRW_shgroup_call_add(clay_shgrp, geom, ob->obmat); } + + if (ob->type == OB_MESH) { + for (ParticleSystem *psys = ob->particlesystem.first; psys; psys = psys->next) { + if (psys_check_enabled(ob, psys, false)) { + ParticleSettings *part = psys->part; + int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as; + + if (draw_as == PART_DRAW_PATH && !psys->pathcache && !psys->childcache) { + draw_as = PART_DRAW_DOT; + } + + switch (draw_as) { + case PART_DRAW_PATH: + geom = DRW_cache_particles_get_hair(psys); + break; + default: + geom = NULL; + break; + } + + if (geom) { + static float mat[4][4]; + unit_m4(mat); + hair_shgrp = CLAY_hair_shgrp_get(ob, stl, psl); + DRW_shgroup_call_add(hair_shgrp, geom, mat); + } + } + } + } } static void CLAY_cache_finish(void *vedata) @@ -599,6 +794,7 @@ static void CLAY_cache_finish(void *vedata) CLAY_StorageList *stl = ((CLAY_Data *)vedata)->stl; DRW_uniformbuffer_update(stl->mat_ubo, &stl->storage->mat_storage); + DRW_uniformbuffer_update(stl->hair_mat_ubo, &stl->storage->hair_mat_storage); } static void CLAY_draw_scene(void *vedata) @@ -620,6 +816,7 @@ static void CLAY_draw_scene(void *vedata) /* Pass 3 : Shading */ DRW_draw_pass(psl->clay_pass); + DRW_draw_pass(psl->hair_pass); } static void CLAY_layer_collection_settings_create(RenderEngine *UNUSED(engine), IDProperty *props) @@ -628,6 +825,9 @@ static void CLAY_layer_collection_settings_create(RenderEngine *UNUSED(engine), props->type == IDP_GROUP && props->subtype == IDP_GROUP_SUB_ENGINE_RENDER); + static float default_hair_diffuse_color[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + static float default_hair_specular_color[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + BKE_collection_engine_property_add_int(props, "matcap_icon", ICON_MATCAP_01); BKE_collection_engine_property_add_int(props, "type", CLAY_MATCAP_NONE); BKE_collection_engine_property_add_float(props, "matcap_rotation", 0.0f); @@ -637,7 +837,13 @@ static void CLAY_layer_collection_settings_create(RenderEngine *UNUSED(engine), BKE_collection_engine_property_add_float(props, "ssao_distance", 0.2f); BKE_collection_engine_property_add_float(props, "ssao_attenuation", 1.0f); BKE_collection_engine_property_add_float(props, "ssao_factor_cavity", 1.0f); - BKE_collection_engine_property_add_float(props, "ssao_factor_edge", 1.0f); + BKE_collection_engine_property_add_float(props, "world_intensity", 0.1f); + BKE_collection_engine_property_add_float(props, "diffuse_intensity", 0.2f); + BKE_collection_engine_property_add_float(props, "specular_intensity", 0.3f); + BKE_collection_engine_property_add_float(props, "specular_hardness", 4.0f); + BKE_collection_engine_property_add_float(props, "color_randomicity", 0.0f); + BKE_collection_engine_property_add_float_array(props, "hair_diffuse_color", default_hair_diffuse_color, 4); + BKE_collection_engine_property_add_float_array(props, "hair_specular_color", default_hair_specular_color, 4); } static void CLAY_scene_layer_settings_create(RenderEngine *UNUSED(engine), IDProperty *props) @@ -652,6 +858,7 @@ static void CLAY_scene_layer_settings_create(RenderEngine *UNUSED(engine), IDPro static void CLAY_engine_free(void) { DRW_SHADER_FREE_SAFE(e_data.clay_sh); + DRW_SHADER_FREE_SAFE(e_data.hair_sh); DRW_TEXTURE_FREE_SAFE(e_data.matcap_array); DRW_TEXTURE_FREE_SAFE(e_data.jitter_tx); DRW_TEXTURE_FREE_SAFE(e_data.sampling_tx); diff --git a/source/blender/draw/engines/clay/shaders/particle_strand_frag.glsl b/source/blender/draw/engines/clay/shaders/particle_strand_frag.glsl new file mode 100644 index 00000000000..f1ce3a55699 --- /dev/null +++ b/source/blender/draw/engines/clay/shaders/particle_strand_frag.glsl @@ -0,0 +1,46 @@ + +uniform vec3 light; + +/* Material Parameters packed in an UBO */ +struct Material { + vec4 one; + vec4 two; + vec4 hair_diffuse_color; + vec4 hair_specular_color; +}; + +layout(std140) uniform material_block { + Material shader_param[MAX_MATERIAL]; +}; + +uniform int mat_id; + +#define world shader_param[mat_id].one.x +#define diffuse shader_param[mat_id].one.y +#define specular shader_param[mat_id].one.z +#define hardness shader_param[mat_id].one.w +#define randomicity shader_param[mat_id].two.x +#define diffColor shader_param[mat_id].hair_diffuse_color +#define specColor shader_param[mat_id].hair_specular_color + +in vec3 normal; +in vec3 viewPosition; +flat in float colRand; +out vec4 fragColor; + +void main() +{ + vec3 normal = normalize(normal); + vec3 specVec = normalize(normalize(light) + normalize(viewPosition)); + float specCos = dot(specVec, normal); + float diffCos = dot(normalize(light), normal); + float maxChan = max(max(diffColor.r, diffColor.g), diffColor.b); + float diff; + float spec; + diff = world; /* world */ + diff += sqrt(1.0 - diffCos*diffCos) * diffuse; /* diffuse */ + spec = pow(1.0 - abs(specCos), hardness) * specular; /* specular */ + fragColor = (diffColor - (colRand * maxChan * randomicity)) * diff; /* add diffuse */ + fragColor += specColor * spec; /* add specular */ + fragColor = clamp(fragColor, 0.0, 1.0); +} diff --git a/source/blender/draw/engines/clay/shaders/particle_vert.glsl b/source/blender/draw/engines/clay/shaders/particle_vert.glsl new file mode 100644 index 00000000000..c10cade330c --- /dev/null +++ b/source/blender/draw/engines/clay/shaders/particle_vert.glsl @@ -0,0 +1,34 @@ + +uniform mat4 ModelViewProjectionMatrix; +uniform mat3 NormalMatrix; +uniform mat4 ModelViewMatrix; + +in vec3 pos; +in vec3 nor; +in int ind; +out vec3 normal; +out vec3 viewPosition; +flat out float colRand; + +/* TODO: This function yields great distribution, but might be a bit inefficient because of the 4 trig ops. + * Something more efficient would be nice */ +float rand(int seed) +{ + vec4 nums = vec4(0.0); + nums.x = mod(tan(mod(float(seed + 1) * 238965.0, 342.0)), 1.0) + 0.01; + nums.y = mod(tan(mod(float(seed + 1) * 34435643.0, 756.0)), 1.0) + 0.01; + nums.z = mod(tan(mod(float(seed + 1) * 4356757.0, 456.0)), 1.0) + 0.01; + nums.w = mod(tan(mod(float(seed + 1) * 778679.0, 987.0)), 1.0) + 0.01; + + float num = mod((nums.x / nums.y) + 1 - (nums.z / nums.w), 1.0); + num += 0.5; + return mod(num, 1.0); +} + +void main() +{ + gl_Position = ModelViewProjectionMatrix * vec4(pos, 1.0); + normal = normalize(NormalMatrix * nor); + viewPosition = (ModelViewMatrix * vec4(pos, 1.0)).xyz; + colRand = rand(ind); +} diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index 5160dc97608..f5c02f244da 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -28,6 +28,7 @@ #include "DNA_mesh_types.h" #include "DNA_curve_types.h" #include "DNA_object_types.h" +#include "DNA_particle_types.h" #include "BLI_utildefines.h" #include "BLI_math.h" @@ -1977,3 +1978,13 @@ Batch *DRW_cache_lattice_vert_overlay_get(Object *ob) } /** \} */ + +/* -------------------------------------------------------------------- */ + +/** \name Particles + * \{ */ + +Batch *DRW_cache_particles_get_hair(ParticleSystem *psys) +{ + return DRW_particles_batch_cache_get_hair(psys); +} diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h index 00031560113..0da09bcc332 100644 --- a/source/blender/draw/intern/draw_cache.h +++ b/source/blender/draw/intern/draw_cache.h @@ -128,4 +128,7 @@ struct Batch *DRW_cache_lattice_verts_get(struct Object *ob); struct Batch *DRW_cache_lattice_wire_get(struct Object *ob); struct Batch *DRW_cache_lattice_vert_overlay_get(struct Object *ob); +/* Particles */ +struct Batch *DRW_cache_particles_get_hair(struct ParticleSystem *psys); + #endif /* __DRAW_CACHE_H__ */ diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h index ddcc55fd85e..2363d097261 100644 --- a/source/blender/draw/intern/draw_cache_impl.h +++ b/source/blender/draw/intern/draw_cache_impl.h @@ -29,6 +29,7 @@ struct Batch; struct ListBase; struct CurveCache; +struct ParticleSystem; struct Curve; struct Lattice; @@ -44,6 +45,9 @@ void DRW_mesh_batch_cache_free(struct Mesh *me); void DRW_lattice_batch_cache_dirty(struct Lattice *lt, int mode); void DRW_lattice_batch_cache_free(struct Lattice *lt); +void DRW_particle_batch_cache_dirty(struct ParticleSystem *psys, int mode); +void DRW_particle_batch_cache_free(struct ParticleSystem *psys); + /* Curve */ struct Batch *DRW_curve_batch_cache_get_wire_edge(struct Curve *cu, struct CurveCache *ob_curve_cache); struct Batch *DRW_curve_batch_cache_get_normal_edge( @@ -85,4 +89,7 @@ struct Batch *DRW_mesh_batch_cache_get_overlay_loose_edges(struct Mesh *me); struct Batch *DRW_mesh_batch_cache_get_overlay_loose_verts(struct Mesh *me); struct Batch *DRW_mesh_batch_cache_get_overlay_facedots(struct Mesh *me); +/* Particles */ +struct Batch *DRW_particles_batch_cache_get_hair(struct ParticleSystem *psys); + #endif /* __DRAW_CACHE_IMPL_H__ */ diff --git a/source/blender/draw/intern/draw_cache_impl_particles.c b/source/blender/draw/intern/draw_cache_impl_particles.c new file mode 100644 index 00000000000..779f2f78c1f --- /dev/null +++ b/source/blender/draw/intern/draw_cache_impl_particles.c @@ -0,0 +1,282 @@ +/* + * ***** 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_cache_impl_particles.c + * \ingroup draw + * + * \brief Particle API for render engines + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_math_vector.h" + +#include "DNA_particle_types.h" + +#include "BKE_particle.h" + +#include "GPU_batch.h" + +#include "draw_cache_impl.h" /* own include */ + +static void particle_batch_cache_clear(ParticleSystem *psys); + +/* ---------------------------------------------------------------------- */ +/* Particle Batch Cache */ + +typedef struct ParticleBatchCache { + VertexBuffer *pos; + ElementList *segments; + + Batch *hairs; + + int segment_count; + int point_count; + + /* settings to determine if cache is invalid */ + bool is_dirty; +} ParticleBatchCache; + +/* Batch cache management. */ + +static bool particle_batch_cache_valid(ParticleSystem *psys) +{ + ParticleBatchCache *cache = psys->batch_cache; + + if (cache == NULL) { + return false; + } + + if (cache->is_dirty == false) { + return true; + } + else { + return false; + } + + return true; +} + +static void particle_batch_cache_init(ParticleSystem *psys) +{ + ParticleBatchCache *cache = psys->batch_cache; + + if (!cache) { + cache = psys->batch_cache = MEM_callocN(sizeof(*cache), __func__); + } + else { + memset(cache, 0, sizeof(*cache)); + } + + cache->is_dirty = false; +} + +static ParticleBatchCache *particle_batch_cache_get(ParticleSystem *psys) +{ + if (!particle_batch_cache_valid(psys)) { + particle_batch_cache_clear(psys); + particle_batch_cache_init(psys); + } + return psys->batch_cache; +} + +void DRW_particle_batch_cache_dirty(ParticleSystem *psys, int mode) +{ + ParticleBatchCache *cache = psys->batch_cache; + if (cache == NULL) { + return; + } + switch (mode) { + case BKE_PARTICLE_BATCH_DIRTY_ALL: + cache->is_dirty = true; + break; + default: + BLI_assert(0); + } +} + +static void particle_batch_cache_clear(ParticleSystem *psys) +{ + ParticleBatchCache *cache = psys->batch_cache; + if (!cache) { + return; + } + + BATCH_DISCARD_SAFE(cache->hairs); + + VERTEXBUFFER_DISCARD_SAFE(cache->pos); + ELEMENTLIST_DISCARD_SAFE(cache->segments); +} + +void DRW_particle_batch_cache_free(ParticleSystem *psys) +{ + particle_batch_cache_clear(psys); + MEM_SAFE_FREE(psys->batch_cache); +} + +static void ensure_seg_pt_count(ParticleSystem *psys, ParticleBatchCache *cache) +{ + if (cache->pos == NULL || cache->segments == NULL) { + cache->segment_count = 0; + cache->point_count = 0; + + if (psys->pathcache && (!psys->childcache || (psys->part->draw & PART_DRAW_PARENT))) { + for (int i = 0; i < psys->totpart; i++) { + ParticleCacheKey *path = psys->pathcache[i]; + + if (path->segments) { + cache->segment_count += path->segments; + cache->point_count += path->segments + 1; + } + } + } + + if (psys->childcache) { + int child_count = psys->totchild * psys->part->disp / 100; + + for (int i = 0; i < child_count; i++) { + ParticleCacheKey *path = psys->childcache[i]; + + if (path->segments) { + cache->segment_count += path->segments; + cache->point_count += path->segments + 1; + } + } + } + } +} + +/* Batch cache usage. */ +static void particle_batch_cache_ensure_pos_and_seg(ParticleSystem *psys, ParticleBatchCache *cache) +{ + if (cache->pos == NULL || cache->segments == NULL) { + static VertexFormat format = { 0 }; + static unsigned pos_id, nor_id, ind_id; + int curr_point = 0; + + VERTEXBUFFER_DISCARD_SAFE(cache->pos); + ELEMENTLIST_DISCARD_SAFE(cache->segments); + + if (format.attrib_ct == 0) { + /* initialize vertex format */ + pos_id = VertexFormat_add_attrib(&format, "pos", COMP_F32, 3, KEEP_FLOAT); + nor_id = VertexFormat_add_attrib(&format, "nor", COMP_F32, 3, KEEP_FLOAT); + ind_id = VertexFormat_add_attrib(&format, "ind", COMP_I32, 1, KEEP_INT); + } + + cache->pos = VertexBuffer_create_with_format(&format); + VertexBuffer_allocate_data(cache->pos, cache->point_count); + + ElementListBuilder elb; + ElementListBuilder_init(&elb, PRIM_LINES, cache->segment_count, cache->point_count); + + if (psys->pathcache && (!psys->childcache || (psys->part->draw & PART_DRAW_PARENT))) { + for (int i = 0; i < psys->totpart; i++) { + ParticleCacheKey *path = psys->pathcache[i]; + + if (path->segments) { + float tangent[3]; + + for (int j = 0; j < path->segments; j++) { + if (j == 0) { + sub_v3_v3v3(tangent, path[j + 1].co, path[j].co); + } + else { + sub_v3_v3v3(tangent, path[j + 1].co, path[j - 1].co); + } + + VertexBuffer_set_attrib(cache->pos, pos_id, curr_point, path[j].co); + VertexBuffer_set_attrib(cache->pos, nor_id, curr_point, tangent); + VertexBuffer_set_attrib(cache->pos, ind_id, curr_point, &i); + + add_line_vertices(&elb, curr_point, curr_point + 1); + + curr_point++; + } + + sub_v3_v3v3(tangent, path[path->segments].co, path[path->segments - 1].co); + + VertexBuffer_set_attrib(cache->pos, pos_id, curr_point, path[path->segments].co); + VertexBuffer_set_attrib(cache->pos, nor_id, curr_point, tangent); + VertexBuffer_set_attrib(cache->pos, ind_id, curr_point, &i); + + curr_point++; + } + } + } + + if (psys->childcache) { + int child_count = psys->totchild * psys->part->disp / 100; + + for (int i = 0, x = psys->totpart; i < child_count; i++, x++) { + ParticleCacheKey *path = psys->childcache[i]; + float tangent[3]; + + if (path->segments) { + for (int j = 0; j < path->segments; j++) { + if (j == 0) { + sub_v3_v3v3(tangent, path[j + 1].co, path[j].co); + } + else { + sub_v3_v3v3(tangent, path[j + 1].co, path[j - 1].co); + } + + VertexBuffer_set_attrib(cache->pos, pos_id, curr_point, path[j].co); + VertexBuffer_set_attrib(cache->pos, nor_id, curr_point, tangent); + VertexBuffer_set_attrib(cache->pos, ind_id, curr_point, &x); + + add_line_vertices(&elb, curr_point, curr_point + 1); + + curr_point++; + } + + sub_v3_v3v3(tangent, path[path->segments].co, path[path->segments - 1].co); + + VertexBuffer_set_attrib(cache->pos, pos_id, curr_point, path[path->segments].co); + VertexBuffer_set_attrib(cache->pos, nor_id, curr_point, tangent); + VertexBuffer_set_attrib(cache->pos, ind_id, curr_point, &x); + + curr_point++; + } + } + } + + cache->segments = ElementList_build(&elb); + } +} + +Batch *DRW_particles_batch_cache_get_hair(ParticleSystem *psys) +{ + ParticleBatchCache *cache = particle_batch_cache_get(psys); + + if (cache->hairs == NULL) { + ensure_seg_pt_count(psys, cache); + particle_batch_cache_ensure_pos_and_seg(psys, cache); + cache->hairs = Batch_create(PRIM_LINES, cache->pos, cache->segments); + } + + return cache->hairs; +} diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index b4efadc077d..92f72bc6664 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -3100,6 +3100,9 @@ void DRW_engines_register(void) /* BKE: lattice.c */ extern void *BKE_lattice_batch_cache_dirty_cb; extern void *BKE_lattice_batch_cache_free_cb; + /* BKE: particle.c */ + extern void *BKE_particle_batch_cache_dirty_cb; + extern void *BKE_particle_batch_cache_free_cb; BKE_curve_batch_cache_dirty_cb = DRW_curve_batch_cache_dirty; BKE_curve_batch_cache_free_cb = DRW_curve_batch_cache_free; @@ -3109,6 +3112,9 @@ void DRW_engines_register(void) BKE_lattice_batch_cache_dirty_cb = DRW_lattice_batch_cache_dirty; BKE_lattice_batch_cache_free_cb = DRW_lattice_batch_cache_free; + + BKE_particle_batch_cache_dirty_cb = DRW_particle_batch_cache_dirty; + BKE_particle_batch_cache_free_cb = DRW_particle_batch_cache_free; } } diff --git a/source/blender/draw/modes/object_mode.c b/source/blender/draw/modes/object_mode.c index ff8118c6378..7fe6ce20608 100644 --- a/source/blender/draw/modes/object_mode.c +++ b/source/blender/draw/modes/object_mode.c @@ -31,6 +31,7 @@ #include "DNA_camera_types.h" #include "DNA_curve_types.h" #include "DNA_object_force.h" +#include "DNA_particle_types.h" #include "DNA_view3d_types.h" #include "DNA_world_types.h" @@ -178,7 +179,6 @@ typedef struct OBJECT_PrivateData{ DRWShadingGroup *wire_select; DRWShadingGroup *wire_select_group; DRWShadingGroup *wire_transform; - } OBJECT_PrivateData; /* Transient data */ static struct { diff --git a/source/blender/makesdna/DNA_particle_types.h b/source/blender/makesdna/DNA_particle_types.h index 1deb9bf3787..5d9d7bd7be0 100644 --- a/source/blender/makesdna/DNA_particle_types.h +++ b/source/blender/makesdna/DNA_particle_types.h @@ -324,6 +324,8 @@ typedef struct ParticleSystem { float dt_frac; /* current time step, as a fraction of a frame */ float _pad; /* spare capacity */ + + void *batch_cache; } ParticleSystem; typedef enum eParticleDrawFlag { diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 614189e7bef..94ef4aed62a 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -2519,6 +2519,9 @@ static void rna_LayerEngineSettings_##_ENGINE_##_##_NAME_##_set(PointerRNA *ptr, #define RNA_LAYER_ENGINE_CLAY_GET_SET_FLOAT(_NAME_) \ RNA_LAYER_ENGINE_GET_SET(float, Clay, COLLECTION_MODE_NONE, _NAME_) +#define RNA_LAYER_ENGINE_CLAY_GET_SET_FLOAT_ARRAY(_NAME_, _LEN_) \ + RNA_LAYER_ENGINE_GET_SET_ARRAY(float, Clay, COLLECTION_MODE_NONE, _NAME_, _LEN_) + #define RNA_LAYER_ENGINE_CLAY_GET_SET_INT(_NAME_) \ RNA_LAYER_ENGINE_GET_SET(int, Clay, COLLECTION_MODE_NONE, _NAME_) @@ -2575,6 +2578,13 @@ RNA_LAYER_ENGINE_CLAY_GET_SET_FLOAT(ssao_factor_cavity) RNA_LAYER_ENGINE_CLAY_GET_SET_FLOAT(ssao_factor_edge) RNA_LAYER_ENGINE_CLAY_GET_SET_FLOAT(ssao_distance) RNA_LAYER_ENGINE_CLAY_GET_SET_FLOAT(ssao_attenuation) +RNA_LAYER_ENGINE_CLAY_GET_SET_FLOAT(world_intensity) +RNA_LAYER_ENGINE_CLAY_GET_SET_FLOAT(diffuse_intensity) +RNA_LAYER_ENGINE_CLAY_GET_SET_FLOAT(specular_intensity) +RNA_LAYER_ENGINE_CLAY_GET_SET_FLOAT(specular_hardness) +RNA_LAYER_ENGINE_CLAY_GET_SET_FLOAT(color_randomicity) +RNA_LAYER_ENGINE_CLAY_GET_SET_FLOAT_ARRAY(hair_diffuse_color, 4) +RNA_LAYER_ENGINE_CLAY_GET_SET_FLOAT_ARRAY(hair_specular_color, 4) #endif /* WITH_CLAY_ENGINE */ /* eevee engine */ @@ -6363,6 +6373,57 @@ static void rna_def_layer_collection_engine_settings_clay(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_LayerCollectionEngineSettings_update"); + prop = RNA_def_property(srna, "world_intensity", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_funcs(prop, "rna_LayerEngineSettings_Clay_world_intensity_get", "rna_LayerEngineSettings_Clay_world_intensity_set", NULL); + RNA_def_property_ui_text(prop, "Hair World Light", "World lighting intensity for hair"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_LayerCollectionEngineSettings_update"); + + prop = RNA_def_property(srna, "diffuse_intensity", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_funcs(prop, "rna_LayerEngineSettings_Clay_diffuse_intensity_get", "rna_LayerEngineSettings_Clay_diffuse_intensity_set", NULL); + RNA_def_property_ui_text(prop, "Hair Diffuse", "Diffuse lighting intensity for hair"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_LayerCollectionEngineSettings_update"); + + prop = RNA_def_property(srna, "specular_intensity", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_funcs(prop, "rna_LayerEngineSettings_Clay_specular_intensity_get", "rna_LayerEngineSettings_Clay_specular_intensity_set", NULL); + RNA_def_property_ui_text(prop, "Hair Specular", "Specular lighting intensity for hair"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_LayerCollectionEngineSettings_update"); + + prop = RNA_def_property(srna, "specular_hardness", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_funcs(prop, "rna_LayerEngineSettings_Clay_specular_hardness_get", "rna_LayerEngineSettings_Clay_specular_hardness_set", NULL); + RNA_def_property_ui_text(prop, "Hair Specular Hardness", "Specular hardness for hair"); + RNA_def_property_range(prop, 0.0f, 1000.0f); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_LayerCollectionEngineSettings_update"); + + prop = RNA_def_property(srna, "color_randomicity", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_funcs(prop, "rna_LayerEngineSettings_Clay_color_randomicity_get", "rna_LayerEngineSettings_Clay_color_randomicity_set", NULL); + RNA_def_property_ui_text(prop, "Hair Color Randomicity", "Color randomicity for hair"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_LayerCollectionEngineSettings_update"); + + prop = RNA_def_property(srna, "hair_diffuse_color", PROP_FLOAT, PROP_COLOR); + RNA_def_property_array(prop, 4); + RNA_def_property_float_funcs(prop, "rna_LayerEngineSettings_Clay_hair_diffuse_color_get", + "rna_LayerEngineSettings_Clay_hair_diffuse_color_set", NULL); + RNA_def_property_ui_text(prop, "Hair Diffuse Color", "Diffuse component of hair shading color"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_LayerCollectionEngineSettings_update"); + + prop = RNA_def_property(srna, "hair_specular_color", PROP_FLOAT, PROP_COLOR); + RNA_def_property_array(prop, 4); + RNA_def_property_float_funcs(prop, "rna_LayerEngineSettings_Clay_hair_specular_color_get", + "rna_LayerEngineSettings_Clay_hair_specular_color_set", NULL); + RNA_def_property_ui_text(prop, "Hair Specular Color", "Specular component of hair shading color"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_LayerCollectionEngineSettings_update"); + RNA_define_verify_sdna(1); /* not in sdna */ } #endif /* WITH_CLAY_ENGINE */ -- cgit v1.2.3