From 7a693626d63a52acd12e80209b634711154a2f9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Mon, 16 Jul 2018 15:01:44 +0200 Subject: Smoke: Port display to Workbench + object mode This does not fix the smokesim. It only port the drawing method. The Object mode engine is in charge of rendering the velocity debugging. Things left to do: - Flame rendering. - Color Ramp coloring of volume data. - View facing slicing (for now it's only doing sampling starting from the volume bounds which gives a squarish look) - Add option to enable dithering (currently on by default. --- source/blender/blenloader/intern/readfile.c | 3 + source/blender/draw/CMakeLists.txt | 4 + .../workbench/shaders/workbench_volume_frag.glsl | 181 +++++++++++++++++++++ .../workbench/shaders/workbench_volume_vert.glsl | 31 ++++ .../draw/engines/workbench/workbench_data.c | 3 + .../draw/engines/workbench/workbench_deferred.c | 32 +++- .../draw/engines/workbench/workbench_forward.c | 23 ++- .../draw/engines/workbench/workbench_private.h | 14 ++ .../draw/engines/workbench/workbench_volume.c | 152 +++++++++++++++++ source/blender/draw/intern/draw_cache.c | 53 +++++- source/blender/draw/intern/draw_cache.h | 3 +- source/blender/draw/intern/draw_common.c | 25 +++ source/blender/draw/intern/draw_common.h | 2 + source/blender/draw/modes/object_mode.c | 101 +++++++++++- .../draw/modes/shaders/volume_velocity_vert.glsl | 115 +++++++++++++ source/blender/gpu/GPU_draw.h | 2 + source/blender/gpu/intern/gpu_draw.c | 46 ++++++ source/blender/makesdna/DNA_smoke_types.h | 3 + 18 files changed, 782 insertions(+), 11 deletions(-) create mode 100644 source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl create mode 100644 source/blender/draw/engines/workbench/shaders/workbench_volume_vert.glsl create mode 100644 source/blender/draw/engines/workbench/workbench_volume.c create mode 100644 source/blender/draw/modes/shaders/volume_velocity_vert.glsl (limited to 'source') diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 6b356ac2e96..f7d585e6fd7 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -5100,6 +5100,9 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb) smd->domain->tex = NULL; smd->domain->tex_shadow = NULL; smd->domain->tex_flame = NULL; + smd->domain->tex_velocity_x = NULL; + smd->domain->tex_velocity_y = NULL; + smd->domain->tex_velocity_z = NULL; smd->domain->tex_wt = NULL; smd->domain->coba = newdataadr(fd, smd->domain->coba); diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 2183bc26755..e4c6a372d34 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -118,6 +118,7 @@ set(SRC engines/workbench/workbench_forward.c engines/workbench/workbench_materials.c engines/workbench/workbench_studiolight.c + engines/workbench/workbench_volume.c engines/workbench/solid_mode.c engines/workbench/transparent_mode.c engines/external/external_engine.c @@ -225,6 +226,8 @@ data_to_c_simple(engines/workbench/shaders/workbench_shadow_vert.glsl SRC) data_to_c_simple(engines/workbench/shaders/workbench_shadow_geom.glsl SRC) data_to_c_simple(engines/workbench/shaders/workbench_shadow_caps_geom.glsl SRC) data_to_c_simple(engines/workbench/shaders/workbench_shadow_debug_frag.glsl SRC) +data_to_c_simple(engines/workbench/shaders/workbench_volume_vert.glsl SRC) +data_to_c_simple(engines/workbench/shaders/workbench_volume_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) @@ -297,6 +300,7 @@ data_to_c_simple(modes/shaders/paint_wire_vert.glsl SRC) data_to_c_simple(modes/shaders/paint_vert_frag.glsl SRC) data_to_c_simple(modes/shaders/particle_strand_frag.glsl SRC) data_to_c_simple(modes/shaders/particle_strand_vert.glsl SRC) +data_to_c_simple(modes/shaders/volume_velocity_vert.glsl SRC) list(APPEND INC ) diff --git a/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl new file mode 100644 index 00000000000..ba5b78f4ecf --- /dev/null +++ b/source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl @@ -0,0 +1,181 @@ + +uniform mat4 ProjectionMatrix; +uniform mat4 ModelMatrixInverse; +uniform mat4 ModelViewMatrixInverse; +uniform mat4 ModelMatrix; + +uniform sampler2D depthBuffer; +uniform sampler3D densityTexture; + +uniform int samplesLen = 256; +uniform float stepLength; /* Step length in local space. */ +uniform float densityScale; /* Simple Opacity multiplicator. */ +uniform vec4 viewvecs[3]; + +uniform float slicePosition; +uniform int sliceAxis; /* -1 is no slice, 0 is X, 1 is Y, 2 is Z. */ + +#ifdef VOLUME_SLICE +in vec3 localPos; +#endif + +out vec4 fragColor; + +#define M_PI 3.1415926535897932 /* pi */ + +float phase_function_isotropic() +{ + return 1.0 / (4.0 * M_PI); +} + +float get_view_z_from_depth(float depth) +{ + if (ProjectionMatrix[3][3] == 0.0) { + float d = 2.0 * depth - 1.0; + return -ProjectionMatrix[3][2] / (d + ProjectionMatrix[2][2]); + } + else { + return viewvecs[0].z + depth * viewvecs[1].z; + } +} + +vec3 get_view_space_from_depth(vec2 uvcoords, float depth) +{ + if (ProjectionMatrix[3][3] == 0.0) { + return vec3(viewvecs[0].xy + uvcoords * viewvecs[1].xy, 1.0) * get_view_z_from_depth(depth); + } + else { + return viewvecs[0].xyz + vec3(uvcoords, depth) * viewvecs[1].xyz; + } +} + +float max_v3(vec3 v) { return max(v.x, max(v.y, v.z)); } + +float line_unit_box_intersect_dist(vec3 lineorigin, vec3 linedirection) +{ + /* https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/ */ + vec3 firstplane = (vec3( 1.0) - lineorigin) / linedirection; + vec3 secondplane = (vec3(-1.0) - lineorigin) / linedirection; + vec3 furthestplane = min(firstplane, secondplane); + return max_v3(furthestplane); +} + +void volume_properties(vec3 ls_pos, out vec3 scattering, out float extinction) +{ + scattering = vec3(0.0); + extinction = 1e-8; + + vec4 density = texture(densityTexture, ls_pos * 0.5 + 0.5); + density.rgb /= density.a; + density *= densityScale; + + scattering = density.rgb; + extinction = max(1e-8, density.a); +} + +#define P(x) ((x + 0.5) * (1.0 / 16.0)) +const vec4 dither_mat[4] = vec4[4]( + vec4( P(0.0), P(8.0), P(2.0), P(10.0)), + vec4(P(12.0), P(4.0), P(14.0), P(6.0)), + vec4( P(3.0), P(11.0), P(1.0), P(9.0)), + vec4(P(15.0), P(7.0), P(13.0), P(5.0)) +); + +vec4 volume_integration( + vec3 ray_ori, vec3 ray_dir, float ray_inc, float ray_max, float step_len) +{ + /* Start with full transmittance and no scattered light. */ + vec3 final_scattering = vec3(0.0); + float final_transmittance = 1.0; + + ivec2 tx = ivec2(gl_FragCoord.xy) % 4; + float noise = dither_mat[tx.x][tx.y]; + + float ray_len = noise * ray_inc; + for (int i = 0; i < samplesLen && ray_len < ray_max; ++i, ray_len += ray_inc) { + vec3 ls_pos = ray_ori + ray_dir * ray_len; + + vec3 Lscat; + float s_extinction; + volume_properties(ls_pos, Lscat, s_extinction); + /* Evaluate Scattering */ + float Tr = exp(-s_extinction * step_len); + /* integrate along the current step segment */ + Lscat = (Lscat - Lscat * Tr) / s_extinction; + /* accumulate and also take into account the transmittance from previous steps */ + final_scattering += final_transmittance * Lscat; + final_transmittance *= Tr; + } + + return vec4(final_scattering, 1.0 - final_transmittance); +} + +void main() +{ +#ifdef VOLUME_SLICE + /* Manual depth test. TODO remove. */ + float depth = texelFetch(depthBuffer, ivec2(gl_FragCoord.xy), 0).r; + if (gl_FragCoord.z >= depth) { + discard; + } + + ivec3 volume_size = textureSize(densityTexture, 0); + float step_len; + if (sliceAxis == 0) { + step_len = float(volume_size.x); + } + else if (sliceAxis == 1) { + step_len = float(volume_size.y); + } + else { + step_len = float(volume_size.z); + } + /* FIXME Should be in world space but is in local space. */ + step_len = 1.0 / step_len; + + vec3 Lscat; + float s_extinction; + volume_properties(localPos, Lscat, s_extinction); + /* Evaluate Scattering */ + float Tr = exp(-s_extinction * step_len); + /* integrate along the current step segment */ + Lscat = (Lscat - Lscat * Tr) / s_extinction; + + fragColor = vec4(Lscat, 1.0 - Tr); + +#else + vec2 screen_uv = gl_FragCoord.xy / vec2(textureSize(depthBuffer, 0).xy); + bool is_persp = ProjectionMatrix[3][3] == 0.0; + + vec3 volume_center = ModelMatrix[3].xyz; + + float depth = texelFetch(depthBuffer, ivec2(gl_FragCoord.xy), 0).r; + float depth_end = min(depth, gl_FragCoord.z); + vec3 vs_ray_end = get_view_space_from_depth(screen_uv, depth_end); + vec3 vs_ray_ori = get_view_space_from_depth(screen_uv, 0.0); + vec3 vs_ray_dir = (is_persp) ? (vs_ray_end - vs_ray_ori) : vec3(0.0, 0.0, -1.0); + vs_ray_dir /= abs(vs_ray_dir.z); + + vec3 ls_ray_dir = mat3(ModelViewMatrixInverse) * vs_ray_dir; + vec3 ls_ray_ori = (ModelViewMatrixInverse * vec4(vs_ray_ori, 1.0)).xyz; + vec3 ls_ray_end = (ModelViewMatrixInverse * vec4(vs_ray_end, 1.0)).xyz; + + /* TODO: Align rays to volume center so that it mimics old behaviour of slicing the volume. */ + + float dist = line_unit_box_intersect_dist(ls_ray_ori, ls_ray_dir); + if (dist > 0.0) { + ls_ray_ori = ls_ray_dir * dist + ls_ray_ori; + } + + vec3 ls_vol_isect = ls_ray_end - ls_ray_ori; + if (dot(ls_ray_dir, ls_vol_isect) < 0.0) { + /* Start is further away than the end. + * That means no volume is intersected. */ + discard; + } + + fragColor = volume_integration(ls_ray_ori, ls_ray_dir, stepLength, + length(ls_vol_isect) / length(ls_ray_dir), + length(vs_ray_dir) * stepLength); +#endif +} \ No newline at end of file diff --git a/source/blender/draw/engines/workbench/shaders/workbench_volume_vert.glsl b/source/blender/draw/engines/workbench/shaders/workbench_volume_vert.glsl new file mode 100644 index 00000000000..90a22d9d02f --- /dev/null +++ b/source/blender/draw/engines/workbench/shaders/workbench_volume_vert.glsl @@ -0,0 +1,31 @@ + +uniform mat4 ModelViewProjectionMatrix; +uniform float slicePosition; +uniform int sliceAxis; /* -1 is no slice, 0 is X, 1 is Y, 2 is Z. */ + +in vec3 pos; + +#ifdef VOLUME_SLICE +in vec3 uvs; + +out vec3 localPos; +#endif + +void main() +{ +#ifdef VOLUME_SLICE + if (sliceAxis == 0) { + localPos = vec3(slicePosition * 2.0 - 1.0, pos.xy); + } + else if (sliceAxis == 1) { + localPos = vec3(pos.x, slicePosition * 2.0 - 1.0, pos.y); + } + else { + localPos = vec3(pos.xy, slicePosition * 2.0 - 1.0); + } + + gl_Position = ModelViewProjectionMatrix * vec4(localPos, 1.0); +#else + gl_Position = ModelViewProjectionMatrix * vec4(pos, 1.0); +#endif +} diff --git a/source/blender/draw/engines/workbench/workbench_data.c b/source/blender/draw/engines/workbench/workbench_data.c index 1f5a1e17277..ede1bd7fcb5 100644 --- a/source/blender/draw/engines/workbench/workbench_data.c +++ b/source/blender/draw/engines/workbench/workbench_data.c @@ -119,6 +119,9 @@ void workbench_private_data_init(WORKBENCH_PrivateData *wpd) wpd->viewvecs[1][2] = vec_far[2] - wpd->viewvecs[0][2]; } } + + wpd->volumes_do = false; + BLI_listbase_clear(&wpd->smoke_domains); } void workbench_private_data_get_light_direction(WORKBENCH_PrivateData *wpd, float r_light_direction[3]) diff --git a/source/blender/draw/engines/workbench/workbench_deferred.c b/source/blender/draw/engines/workbench/workbench_deferred.c index 784ed861014..d17c48b0360 100644 --- a/source/blender/draw/engines/workbench/workbench_deferred.c +++ b/source/blender/draw/engines/workbench/workbench_deferred.c @@ -33,6 +33,7 @@ #include "BLI_rand.h" #include "BKE_node.h" +#include "BKE_modifier.h" #include "BKE_particle.h" #include "DNA_image_types.h" @@ -327,12 +328,11 @@ void workbench_deferred_engine_init(WORKBENCH_Data *vedata) char *cavity_frag = workbench_build_cavity_frag(); e_data.cavity_sh = DRW_shader_create_fullscreen(cavity_frag, NULL); MEM_freeN(cavity_frag); - } + workbench_volume_engine_init(); workbench_fxaa_engine_init(); workbench_taa_engine_init(vedata); - WORKBENCH_PrivateData *wpd = stl->g_data; workbench_private_data_init(wpd); @@ -370,6 +370,10 @@ void workbench_deferred_engine_init(WORKBENCH_Data *vedata) GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_TEXTURE(e_data.composite_buffer_tx), }); + GPU_framebuffer_ensure_config(&fbl->volume_fb, { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(e_data.composite_buffer_tx), + }); GPU_framebuffer_ensure_config(&fbl->effect_fb, { GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(e_data.color_buffer_tx), @@ -446,6 +450,7 @@ void workbench_deferred_engine_free(void) DRW_SHADER_FREE_SAFE(e_data.shadow_caps_sh); DRW_SHADER_FREE_SAFE(e_data.shadow_caps_manifold_sh); + workbench_volume_engine_free(); workbench_fxaa_engine_free(); workbench_taa_engine_free(); } @@ -485,7 +490,10 @@ void workbench_deferred_cache_init(WORKBENCH_Data *vedata) Scene *scene = draw_ctx->scene; + workbench_volume_cache_init(vedata); + select_deferred_shaders(wpd); + /* Deferred Mix Pass */ { workbench_private_data_get_light_direction(wpd, e_data.display.light_direction); @@ -636,6 +644,9 @@ void workbench_deferred_solid_cache_populate(WORKBENCH_Data *vedata, Object *ob) WORKBENCH_StorageList *stl = vedata->stl; WORKBENCH_PassList *psl = vedata->psl; WORKBENCH_PrivateData *wpd = stl->g_data; + const DRWContextState *draw_ctx = DRW_context_state_get(); + Scene *scene = draw_ctx->scene; + if (!DRW_object_is_renderable(ob)) return; @@ -643,13 +654,22 @@ void workbench_deferred_solid_cache_populate(WORKBENCH_Data *vedata, Object *ob) workbench_cache_populate_particles(vedata, ob); } + ModifierData *md; + if (((ob->base_flag & BASE_FROMDUPLI) == 0) && + (md = modifiers_findByType(ob, eModifierType_Smoke)) && + (modifier_isEnabled(scene, md, eModifierMode_Realtime)) && + (((SmokeModifierData *)md)->domain != NULL)) + { + workbench_volume_cache_populate(vedata, scene, ob, md); + return; /* Do not draw solid in this case. */ + } + if (!DRW_check_object_visible_within_active_context(ob)) { return; } WORKBENCH_MaterialData *material; if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL)) { - const DRWContextState *draw_ctx = DRW_context_state_get(); const bool is_active = (ob == draw_ctx->obact); const bool is_sculpt_mode = is_active && (draw_ctx->object_mode & OB_MODE_SCULPT) != 0; bool is_drawn = false; @@ -860,6 +880,12 @@ void workbench_deferred_draw_scene(WORKBENCH_Data *vedata) DRW_draw_pass(psl->composite_pass); } + if (wpd->volumes_do) { + GPU_framebuffer_bind(fbl->volume_fb); + DRW_draw_pass(psl->volume_pass); + } + workbench_aa_draw_pass(vedata, e_data.composite_buffer_tx); workbench_private_data_free(wpd); + workbench_volume_smoke_textures_free(wpd); } diff --git a/source/blender/draw/engines/workbench/workbench_forward.c b/source/blender/draw/engines/workbench/workbench_forward.c index e921b2ac2f7..3fb68654188 100644 --- a/source/blender/draw/engines/workbench/workbench_forward.c +++ b/source/blender/draw/engines/workbench/workbench_forward.c @@ -33,6 +33,7 @@ #include "BKE_node.h" #include "BKE_particle.h" +#include "BKE_modifier.h" #include "DNA_image_types.h" #include "DNA_mesh_types.h" @@ -284,6 +285,7 @@ void workbench_forward_engine_init(WORKBENCH_Data *vedata) MEM_freeN(defines_texture); MEM_freeN(defines_hair); } + workbench_volume_engine_init(); workbench_fxaa_engine_init(); workbench_taa_engine_init(vedata); @@ -305,13 +307,11 @@ void workbench_forward_engine_init(WORKBENCH_Data *vedata) GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_TEXTURE(e_data.object_id_tx), }); - GPU_framebuffer_ensure_config(&fbl->transparent_accum_fb, { GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(e_data.transparent_accum_tx), GPU_ATTACHMENT_TEXTURE(e_data.transparent_revealage_tx), }); - GPU_framebuffer_ensure_config(&fbl->composite_fb, { GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(e_data.composite_buffer_tx), @@ -321,6 +321,8 @@ void workbench_forward_engine_init(WORKBENCH_Data *vedata) GPU_ATTACHMENT_TEXTURE(e_data.transparent_accum_tx), }); + workbench_volume_cache_init(vedata); + /* Transparency Accum */ { int state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_OIT; @@ -372,7 +374,9 @@ void workbench_forward_engine_free() DRW_SHADER_FREE_SAFE(e_data.object_outline_hair_sh); DRW_SHADER_FREE_SAFE(e_data.checker_depth_sh); + workbench_volume_engine_free(); workbench_fxaa_engine_free(); + workbench_taa_engine_free(); } void workbench_forward_cache_init(WORKBENCH_Data *UNUSED(vedata)) @@ -444,6 +448,8 @@ void workbench_forward_cache_populate(WORKBENCH_Data *vedata, Object *ob) { WORKBENCH_StorageList *stl = vedata->stl; WORKBENCH_PrivateData *wpd = stl->g_data; + const DRWContextState *draw_ctx = DRW_context_state_get(); + Scene *scene = draw_ctx->scene; if (!DRW_object_is_renderable(ob)) return; @@ -452,13 +458,22 @@ void workbench_forward_cache_populate(WORKBENCH_Data *vedata, Object *ob) workbench_forward_cache_populate_particles(vedata, ob); } + ModifierData *md; + if (((ob->base_flag & BASE_FROMDUPLI) == 0) && + (md = modifiers_findByType(ob, eModifierType_Smoke)) && + (modifier_isEnabled(scene, md, eModifierMode_Realtime)) && + (((SmokeModifierData *)md)->domain != NULL)) + { + workbench_volume_cache_populate(vedata, scene, ob, md); + return; /* Do not draw solid in this case. */ + } + if (!DRW_check_object_visible_within_active_context(ob)) { return; } WORKBENCH_MaterialData *material; if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL)) { - const DRWContextState *draw_ctx = DRW_context_state_get(); const bool is_active = (ob == draw_ctx->obact); const bool is_sculpt_mode = is_active && (draw_ctx->object_mode & OB_MODE_SCULPT) != 0; bool is_drawn = false; @@ -595,6 +610,7 @@ void workbench_forward_draw_scene(WORKBENCH_Data *vedata) /* Composite */ GPU_framebuffer_bind(fbl->composite_fb); DRW_draw_pass(psl->composite_pass); + DRW_draw_pass(psl->volume_pass); /* Color correct and Anti aliasing */ workbench_aa_draw_pass(vedata, e_data.composite_buffer_tx); @@ -604,4 +620,5 @@ void workbench_forward_draw_scene(WORKBENCH_Data *vedata) DRW_draw_pass(psl->checker_depth_pass); workbench_private_data_free(wpd); + workbench_volume_smoke_textures_free(wpd); } diff --git a/source/blender/draw/engines/workbench/workbench_private.h b/source/blender/draw/engines/workbench/workbench_private.h index 4210e03f470..13e9686aa85 100644 --- a/source/blender/draw/engines/workbench/workbench_private.h +++ b/source/blender/draw/engines/workbench/workbench_private.h @@ -70,6 +70,7 @@ typedef struct WORKBENCH_FramebufferList { struct GPUFrameBuffer *effect_fb; struct GPUFrameBuffer *effect_taa_fb; struct GPUFrameBuffer *depth_buffer_fb; + struct GPUFrameBuffer *volume_fb; /* Forward render buffers */ struct GPUFrameBuffer *object_outline_fb; @@ -101,6 +102,7 @@ typedef struct WORKBENCH_PassList { struct DRWPass *composite_pass; struct DRWPass *composite_shadow_pass; struct DRWPass *effect_aa_pass; + struct DRWPass *volume_pass; /* forward rendering */ struct DRWPass *transparent_accum_pass; @@ -167,6 +169,10 @@ typedef struct WORKBENCH_PrivateData { float shadow_near_sides[2][4]; /* This is a parallelogram, so only 2 normal and distance to the edges. */ bool shadow_changed; + /* Volumes */ + bool volumes_do; + ListBase smoke_domains; + /* Ssao */ float winmat[4][4]; float viewvecs[3][4]; @@ -280,6 +286,14 @@ void workbench_private_data_init(WORKBENCH_PrivateData *wpd); void workbench_private_data_free(WORKBENCH_PrivateData *wpd); void workbench_private_data_get_light_direction(WORKBENCH_PrivateData *wpd, float r_light_direction[3]); +/* workbench_volume.c */ +void workbench_volume_engine_init(void); +void workbench_volume_engine_free(void); +void workbench_volume_cache_init(WORKBENCH_Data *vedata); +void workbench_volume_cache_populate(WORKBENCH_Data *vedata, Scene *scene, Object *ob, struct ModifierData *md); +void workbench_volume_smoke_textures_free(WORKBENCH_PrivateData *wpd); + + extern DrawEngineType draw_engine_workbench_solid; extern DrawEngineType draw_engine_workbench_transparent; diff --git a/source/blender/draw/engines/workbench/workbench_volume.c b/source/blender/draw/engines/workbench/workbench_volume.c new file mode 100644 index 00000000000..f28bfd01ae8 --- /dev/null +++ b/source/blender/draw/engines/workbench/workbench_volume.c @@ -0,0 +1,152 @@ +/* + * Copyright 2018, Blender Foundation. + * + * 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. + * + * Contributor(s): Blender Institute + * + */ + +/** \file workbench_volumed.c + * \ingroup draw_engine + */ + +#include "workbench_private.h" + +#include "BKE_modifier.h" + +#include "DNA_modifier_types.h" +#include "DNA_object_force_types.h" +#include "DNA_smoke_types.h" + +#include "GPU_draw.h" + +static struct { + struct GPUShader *volume_sh; + struct GPUShader *volume_slice_sh; +} e_data = {NULL}; + +extern char datatoc_workbench_volume_vert_glsl[]; +extern char datatoc_workbench_volume_frag_glsl[]; + +void workbench_volume_engine_init(void) +{ + if (!e_data.volume_sh) { + e_data.volume_sh = DRW_shader_create( + datatoc_workbench_volume_vert_glsl, NULL, + datatoc_workbench_volume_frag_glsl, NULL); + e_data.volume_slice_sh = DRW_shader_create( + datatoc_workbench_volume_vert_glsl, NULL, + datatoc_workbench_volume_frag_glsl, "#define VOLUME_SLICE"); + } +} + +void workbench_volume_engine_free(void) +{ + DRW_SHADER_FREE_SAFE(e_data.volume_sh); + DRW_SHADER_FREE_SAFE(e_data.volume_slice_sh); +} + +void workbench_volume_cache_init(WORKBENCH_Data *vedata) +{ + vedata->psl->volume_pass = DRW_pass_create("Volumes", DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_PREMUL | DRW_STATE_CULL_FRONT); +} + +void workbench_volume_cache_populate(WORKBENCH_Data *vedata, Scene *scene, Object *ob, ModifierData *md) +{ + SmokeModifierData *smd = (SmokeModifierData *)md; + SmokeDomainSettings *sds = smd->domain; + WORKBENCH_PrivateData *wpd = vedata->stl->g_data; + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + + /* Don't show smoke before simulation starts, this could be made an option in the future. */ + if (!sds->fluid || CFRA < sds->point_cache[0]->startframe) { + return; + } + + wpd->volumes_do = true; + + if (!sds->wt || !(sds->viewsettings & MOD_SMOKE_VIEW_SHOWBIG)) { + GPU_create_smoke(smd, 0); + } + else if (sds->wt && (sds->viewsettings & MOD_SMOKE_VIEW_SHOWBIG)) { + GPU_create_smoke(smd, 1); + } + + if (sds->tex == NULL) { + return; + } + + if (sds->slice_method == MOD_SMOKE_SLICE_AXIS_ALIGNED && + sds->axis_slice_method == AXIS_SLICE_SINGLE) + { + float invviewmat[4][4]; + DRW_viewport_matrix_get(invviewmat, DRW_MAT_VIEWINV); + + const int axis = (sds->slice_axis == SLICE_AXIS_AUTO) + ? axis_dominant_v3_single(invviewmat[2]) + : sds->slice_axis - 1; + + DRWShadingGroup *grp = DRW_shgroup_create(e_data.volume_slice_sh, vedata->psl->volume_pass); + DRW_shgroup_uniform_texture(grp, "densityTexture", sds->tex); + DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth); + DRW_shgroup_uniform_float_copy(grp, "densityScale", 10.0f * sds->display_thickness); + DRW_shgroup_uniform_float_copy(grp, "slicePosition", sds->slice_depth); + DRW_shgroup_uniform_int_copy(grp, "sliceAxis", axis); + DRW_shgroup_state_disable(grp, DRW_STATE_CULL_FRONT); + BLI_addtail(&wpd->smoke_domains, BLI_genericNodeN(smd)); + + /* TODO Flame rendering */ + /* TODO COBA Rendering */ + + DRW_shgroup_call_object_add(grp, DRW_cache_quad_get(), ob); + } + else { + int max_slices = max_iii(sds->res[0], sds->res[1], sds->res[2]) * sds->slice_per_voxel; + + DRWShadingGroup *grp = DRW_shgroup_create(e_data.volume_sh, vedata->psl->volume_pass); + DRW_shgroup_uniform_vec4(grp, "viewvecs[0]", (float *)wpd->viewvecs, 3); + DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth); + DRW_shgroup_uniform_texture(grp, "densityTexture", sds->tex); + DRW_shgroup_uniform_float_copy(grp, "densityScale", 10.0f * sds->display_thickness); + DRW_shgroup_uniform_int_copy(grp, "samplesLen", max_slices); + /* TODO FIXME : This step size is in object space but the ray itself + * is NOT unit length in object space so the required number of subdivisions + * is tricky to get. */ + DRW_shgroup_uniform_float_copy(grp, "stepLength", 8.0f / max_slices); + DRW_shgroup_state_enable(grp, DRW_STATE_CULL_FRONT); + BLI_addtail(&wpd->smoke_domains, BLI_genericNodeN(smd)); + + /* TODO Flame rendering */ + /* TODO COBA Rendering */ + + DRW_shgroup_call_object_add(grp, DRW_cache_cube_get(), ob); + } +} + +void workbench_volume_smoke_textures_free(WORKBENCH_PrivateData *wpd) +{ + /* Free Smoke Textures after rendering */ + /* XXX This is a waste of processing and GPU bandwidth if nothing + * is updated. But the problem is since Textures are stored in the + * modifier we don't want them to take precious VRAM if the + * modifier is not used for display. We should share them for + * all viewport in a redraw at least. */ + for (LinkData *link = wpd->smoke_domains.first; link; link = link->next) { + SmokeModifierData *smd = (SmokeModifierData *)link->data; + GPU_free_smoke(smd); + } + BLI_freelistN(&wpd->smoke_domains); +} diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index 1bf40673239..0d2317199fc 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -61,6 +61,7 @@ static struct DRWShapeCache { Gwn_Batch *drw_square; Gwn_Batch *drw_line; Gwn_Batch *drw_line_endpoints; + Gwn_Batch *drw_empty_cube; Gwn_Batch *drw_empty_sphere; Gwn_Batch *drw_empty_cone; Gwn_Batch *drw_arrows; @@ -341,6 +342,54 @@ Gwn_Batch *DRW_cache_cube_get(void) { 1.0f, 1.0f, 1.0f} }; + const uint indices[36] = { + 0, 1, 2, + 1, 3, 2, + 0, 4, 1, + 4, 5, 1, + 6, 5, 4, + 6, 7, 5, + 2, 7, 6, + 2, 3, 7, + 3, 1, 7, + 1, 5, 7, + 0, 2, 4, + 2, 6, 4, + }; + + /* Position Only 3D format */ + static Gwn_VertFormat format = { 0 }; + static struct { uint pos; } attr_id; + if (format.attr_len == 0) { + attr_id.pos = GWN_vertformat_attr_add(&format, "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT); + } + + Gwn_VertBuf *vbo = GWN_vertbuf_create_with_format(&format); + GWN_vertbuf_data_alloc(vbo, 36); + + for (int i = 0; i < 36; ++i) { + GWN_vertbuf_attr_set(vbo, attr_id.pos, i, verts[indices[i]]); + } + + SHC.drw_cube = GWN_batch_create_ex(GWN_PRIM_TRIS, vbo, NULL, GWN_BATCH_OWNS_VBO); + } + return SHC.drw_cube; +} + +Gwn_Batch *DRW_cache_empty_cube_get(void) +{ + if (!SHC.drw_empty_cube) { + const GLfloat verts[8][3] = { + {-1.0f, -1.0f, -1.0f}, + {-1.0f, -1.0f, 1.0f}, + {-1.0f, 1.0f, -1.0f}, + {-1.0f, 1.0f, 1.0f}, + { 1.0f, -1.0f, -1.0f}, + { 1.0f, -1.0f, 1.0f}, + { 1.0f, 1.0f, -1.0f}, + { 1.0f, 1.0f, 1.0f} + }; + const GLubyte indices[24] = {0, 1, 1, 3, 3, 2, 2, 0, 0, 4, 4, 5, 5, 7, 7, 6, 6, 4, 1, 5, 3, 7, 2, 6}; /* Position Only 3D format */ @@ -357,9 +406,9 @@ Gwn_Batch *DRW_cache_cube_get(void) GWN_vertbuf_attr_set(vbo, attr_id.pos, i, verts[indices[i]]); } - SHC.drw_cube = GWN_batch_create_ex(GWN_PRIM_LINES, vbo, NULL, GWN_BATCH_OWNS_VBO); + SHC.drw_empty_cube = GWN_batch_create_ex(GWN_PRIM_LINES, vbo, NULL, GWN_BATCH_OWNS_VBO); } - return SHC.drw_cube; + return SHC.drw_empty_cube; } Gwn_Batch *DRW_cache_circle_get(void) diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h index e7ab847d42e..9114a4d872d 100644 --- a/source/blender/draw/intern/draw_cache.h +++ b/source/blender/draw/intern/draw_cache.h @@ -41,6 +41,7 @@ struct Gwn_Batch *DRW_cache_cursor_get(bool crosshair_lines); /* Common Shapes */ struct Gwn_Batch *DRW_cache_fullscreen_quad_get(void); struct Gwn_Batch *DRW_cache_quad_get(void); +struct Gwn_Batch *DRW_cache_cube_get(void); struct Gwn_Batch *DRW_cache_sphere_get(void); struct Gwn_Batch *DRW_cache_single_vert_get(void); struct Gwn_Batch *DRW_cache_single_line_get(void); @@ -61,7 +62,7 @@ void DRW_cache_object_face_wireframe_get( /* Empties */ struct Gwn_Batch *DRW_cache_plain_axes_get(void); struct Gwn_Batch *DRW_cache_single_arrow_get(void); -struct Gwn_Batch *DRW_cache_cube_get(void); +struct Gwn_Batch *DRW_cache_empty_cube_get(void); struct Gwn_Batch *DRW_cache_circle_get(void); struct Gwn_Batch *DRW_cache_square_get(void); struct Gwn_Batch *DRW_cache_empty_sphere_get(void); diff --git a/source/blender/draw/intern/draw_common.c b/source/blender/draw/intern/draw_common.c index a1c60be4742..df80300417e 100644 --- a/source/blender/draw/intern/draw_common.c +++ b/source/blender/draw/intern/draw_common.c @@ -162,6 +162,8 @@ extern char datatoc_animviz_mpath_lines_vert_glsl[]; extern char datatoc_animviz_mpath_lines_geom_glsl[]; extern char datatoc_animviz_mpath_points_vert_glsl[]; +extern char datatoc_volume_velocity_vert_glsl[]; + extern char datatoc_armature_axes_vert_glsl[]; extern char datatoc_armature_sphere_solid_vert_glsl[]; extern char datatoc_armature_sphere_solid_frag_glsl[]; @@ -199,6 +201,9 @@ static struct { struct GPUShader *mpath_line_sh; struct GPUShader *mpath_points_sh; + struct GPUShader *volume_velocity_needle_sh; + struct GPUShader *volume_velocity_sh; + struct GPUShader *mball_handles; } g_shaders = {NULL}; @@ -736,6 +741,26 @@ struct GPUShader *mpath_points_shader_get(void) return g_shaders.mpath_points_sh; } +struct GPUShader *volume_velocity_shader_get(bool use_needle) +{ + if (use_needle) { + if (g_shaders.volume_velocity_needle_sh == NULL) { + g_shaders.volume_velocity_needle_sh = DRW_shader_create( + datatoc_volume_velocity_vert_glsl, NULL, + datatoc_gpu_shader_flat_color_frag_glsl, "#define USE_NEEDLE"); + } + return g_shaders.volume_velocity_needle_sh; + } + else { + if (g_shaders.volume_velocity_sh == NULL) { + g_shaders.volume_velocity_sh = DRW_shader_create( + datatoc_volume_velocity_vert_glsl, NULL, + datatoc_gpu_shader_flat_color_frag_glsl, NULL); + } + return g_shaders.volume_velocity_sh; + } +} + /* ******************************************** COLOR UTILS *********************************************** */ /* TODO FINISH */ diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h index 6227130fb05..b4ae0600f02 100644 --- a/source/blender/draw/intern/draw_common.h +++ b/source/blender/draw/intern/draw_common.h @@ -138,6 +138,8 @@ struct DRWShadingGroup *shgroup_instance_bone_stick(struct DRWPass *pass); struct GPUShader *mpath_line_shader_get(void); struct GPUShader *mpath_points_shader_get(void); +struct GPUShader *volume_velocity_shader_get(bool use_needle); + int DRW_object_wire_theme_get( struct Object *ob, struct ViewLayer *view_layer, float **r_color); float *DRW_color_background_blend_get(int theme_id); diff --git a/source/blender/draw/modes/object_mode.c b/source/blender/draw/modes/object_mode.c index 6c99024312c..2e41dbfd529 100644 --- a/source/blender/draw/modes/object_mode.c +++ b/source/blender/draw/modes/object_mode.c @@ -32,10 +32,12 @@ #include "DNA_curve_types.h" #include "DNA_mesh_types.h" #include "DNA_meta_types.h" +#include "DNA_modifier_types.h" #include "DNA_object_force_types.h" #include "DNA_lightprobe_types.h" #include "DNA_particle_types.h" #include "DNA_rigidbody_types.h" +#include "DNA_smoke_types.h" #include "DNA_view3d_types.h" #include "DNA_world_types.h" @@ -49,6 +51,7 @@ #include "BKE_global.h" #include "BKE_mball.h" #include "BKE_mesh.h" +#include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_particle.h" #include "BKE_image.h" @@ -58,6 +61,7 @@ #include "GPU_shader.h" #include "GPU_texture.h" +#include "GPU_draw.h" #include "MEM_guardedalloc.h" @@ -286,6 +290,8 @@ static struct { struct GPUTexture *outlines_id_tx; struct GPUTexture *outlines_color_tx; struct GPUTexture *outlines_blur_tx; + + ListBase smoke_domains; } e_data = {NULL}; /* Engine data */ @@ -1078,7 +1084,7 @@ static void OBJECT_cache_init(void *vedata) geom = DRW_cache_plain_axes_get(); stl->g_data->plain_axes = shgroup_instance(psl->non_meshes, geom); - geom = DRW_cache_cube_get(); + geom = DRW_cache_empty_cube_get(); stl->g_data->cube = shgroup_instance(psl->non_meshes, geom); geom = DRW_cache_circle_get(); @@ -1156,7 +1162,7 @@ static void OBJECT_cache_init(void *vedata) stl->g_data->camera_mist_points = shgroup_distance_lines_instance(psl->non_meshes, geom); /* Texture Space */ - geom = DRW_cache_cube_get(); + geom = DRW_cache_empty_cube_get(); stl->g_data->texspace = shgroup_instance(psl->non_meshes, geom); } @@ -1723,6 +1729,85 @@ static void DRW_shgroup_forcefield(OBJECT_StorageList *stl, Object *ob, ViewLaye } } +static void DRW_shgroup_volume_extra( + OBJECT_PassList *psl, OBJECT_StorageList *stl, + Object *ob, ViewLayer *view_layer, Scene *scene, ModifierData *md) +{ + SmokeModifierData *smd = (SmokeModifierData *)md; + SmokeDomainSettings *sds = smd->domain; + float *color; + float one = 1.0f; + + if (sds == NULL) { + return; + } + + DRW_object_wire_theme_get(ob, view_layer, &color); + + /* Small cube showing voxel size. */ + float voxel_cubemat[4][4] = {{0.0f}}; + voxel_cubemat[0][0] = 1.0f / (float)sds->res[0]; + voxel_cubemat[1][1] = 1.0f / (float)sds->res[1]; + voxel_cubemat[2][2] = 1.0f / (float)sds->res[2]; + voxel_cubemat[3][0] = voxel_cubemat[3][1] = voxel_cubemat[3][2] = -1.0f; + voxel_cubemat[3][3] = 1.0f; + translate_m4(voxel_cubemat, 1.0f, 1.0f, 1.0f); + mul_m4_m4m4(voxel_cubemat, ob->obmat, voxel_cubemat); + + DRW_shgroup_call_dynamic_add(stl->g_data->cube, color, &one, voxel_cubemat); + + /* Don't show smoke before simulation starts, this could be made an option in the future. */ + if (!sds->draw_velocity || !sds->fluid || CFRA < sds->point_cache[0]->startframe) { + return; + } + + const bool use_needle = (sds->vector_draw_type == VECTOR_DRAW_NEEDLE); + int line_count = (use_needle) ? 6 : 1; + int slice_axis = -1; + line_count *= sds->res[0] * sds->res[1] * sds->res[2]; + + if (sds->slice_method == MOD_SMOKE_SLICE_AXIS_ALIGNED && + sds->axis_slice_method == AXIS_SLICE_SINGLE) + { + float invviewmat[4][4]; + DRW_viewport_matrix_get(invviewmat, DRW_MAT_VIEWINV); + + const int axis = (sds->slice_axis == SLICE_AXIS_AUTO) + ? axis_dominant_v3_single(invviewmat[2]) + : sds->slice_axis - 1; + slice_axis = axis; + line_count /= sds->res[axis]; + } + + GPU_create_smoke_velocity(smd); + + DRWShadingGroup *grp = DRW_shgroup_create(volume_velocity_shader_get(use_needle), psl->non_meshes); + DRW_shgroup_uniform_texture(grp, "velocityX", sds->tex_velocity_x); + DRW_shgroup_uniform_texture(grp, "velocityY", sds->tex_velocity_y); + DRW_shgroup_uniform_texture(grp, "velocityZ", sds->tex_velocity_z); + DRW_shgroup_uniform_float_copy(grp, "displaySize", sds->vector_scale); + DRW_shgroup_uniform_float_copy(grp, "slicePosition", sds->slice_depth); + DRW_shgroup_uniform_int_copy(grp, "sliceAxis", slice_axis); + DRW_shgroup_call_procedural_lines_add(grp, line_count, ob->obmat); + + BLI_addtail(&e_data.smoke_domains, BLI_genericNodeN(smd)); +} + +static void volumes_free_smoke_textures(void) +{ + /* Free Smoke Textures after rendering */ + /* XXX This is a waste of processing and GPU bandwidth if nothing + * is updated. But the problem is since Textures are stored in the + * modifier we don't want them to take precious VRAM if the + * modifier is not used for display. We should share them for + * all viewport in a redraw at least. */ + for (LinkData *link = e_data.smoke_domains.first; link; link = link->next) { + SmokeModifierData *smd = (SmokeModifierData *)link->data; + GPU_free_smoke(smd); + } + BLI_freelistN(&e_data.smoke_domains); +} + static void DRW_shgroup_speaker(OBJECT_StorageList *stl, Object *ob, ViewLayer *view_layer) { float *color; @@ -2142,7 +2227,9 @@ static void OBJECT_cache_populate(void *vedata, Object *ob) OBJECT_StorageList *stl = ((OBJECT_Data *)vedata)->stl; const DRWContextState *draw_ctx = DRW_context_state_get(); ViewLayer *view_layer = draw_ctx->view_layer; + Scene *scene = draw_ctx->scene; View3D *v3d = draw_ctx->v3d; + ModifierData *md = NULL; int theme_id = TH_UNDEFINED; /* Handle particles first in case the emitter itself shouldn't be rendered. */ @@ -2311,6 +2398,14 @@ static void OBJECT_cache_populate(void *vedata, Object *ob) DRW_shgroup_forcefield(stl, ob, view_layer); } + if (((ob->base_flag & BASE_FROMDUPLI) == 0) && + (md = modifiers_findByType(ob, eModifierType_Smoke)) && + (modifier_isEnabled(scene, md, eModifierMode_Realtime)) && + (((SmokeModifierData *)md)->domain != NULL)) + { + DRW_shgroup_volume_extra(psl, stl, ob, view_layer, scene, md); + } + /* don't show object extras in set's */ if ((ob->base_flag & (BASE_FROM_SET | BASE_FROMDUPLI)) == 0) { @@ -2437,6 +2532,8 @@ static void OBJECT_draw_scene(void *vedata) BLI_ghash_free(stl->g_data->image_plane_map, NULL, MEM_freeN); stl->g_data->image_plane_map = NULL; } + + volumes_free_smoke_textures(); } static const DrawEngineDataSize OBJECT_data_size = DRW_VIEWPORT_DATA_SIZE(OBJECT_Data); diff --git a/source/blender/draw/modes/shaders/volume_velocity_vert.glsl b/source/blender/draw/modes/shaders/volume_velocity_vert.glsl new file mode 100644 index 00000000000..574c434920e --- /dev/null +++ b/source/blender/draw/modes/shaders/volume_velocity_vert.glsl @@ -0,0 +1,115 @@ + +uniform mat4 ModelViewProjectionMatrix; + +uniform sampler3D velocityX; +uniform sampler3D velocityY; +uniform sampler3D velocityZ; +uniform float displaySize = 1.0; +uniform float slicePosition; +uniform int sliceAxis; /* -1 is no slice, 0 is X, 1 is Y, 2 is Z. */ + +flat out vec4 finalColor; + +const vec3 corners[4] = vec3[4]( + vec3(0.0, 0.2, -0.5), + vec3(-0.2 * 0.866, -0.2 * 0.5, -0.5), + vec3(0.2 * 0.866, -0.2 * 0.5, -0.5), + vec3(0.0, 0.0, 0.5) +); + +const int indices[12] = int[12](0, 1, 1, 2, 2, 0, 0, 3, 1, 3, 2, 3); + +/* Straight Port from BKE_defvert_weight_to_rgb() + * TODO port this to a color ramp. */ +vec3 weight_to_color(float weight) +{ + vec3 r_rgb = vec3(0.0); + float blend = ((weight / 2.0) + 0.5); + + if (weight <= 0.25) { /* blue->cyan */ + r_rgb.g = blend * weight * 4.0; + r_rgb.b = blend; + } + else if (weight <= 0.50) { /* cyan->green */ + r_rgb.g = blend; + r_rgb.b = blend * (1.0 - ((weight - 0.25) * 4.0)); + } + else if (weight <= 0.75) { /* green->yellow */ + r_rgb.r = blend * ((weight - 0.50) * 4.0); + r_rgb.g = blend; + } + else if (weight <= 1.0) { /* yellow->red */ + r_rgb.r = blend; + r_rgb.g = blend * (1.0 - ((weight - 0.75) * 4.0)); + } + else { + /* exceptional value, unclamped or nan, + * avoid uninitialized memory use */ + r_rgb = vec3(1.0, 0.0, 1.0); + } + + return r_rgb; +} + +mat3 rotation_from_vector(vec3 v) +{ + /* Add epsilon to avoid NaN. */ + vec3 N = normalize(v + 1e-8); + vec3 UpVector = abs(N.z) < 0.99999 ? vec3(0.0,0.0,1.0) : vec3(1.0,0.0,0.0); + vec3 T = normalize(cross(UpVector, N)); + vec3 B = cross(N, T); + return mat3(T, B, N); +} + +void main() +{ +#ifdef USE_NEEDLE + int cell = gl_VertexID / 12; +#else + int cell = gl_VertexID / 2; +#endif + + ivec3 volume_size = textureSize(velocityX, 0); + float voxel_size = 1.0 / float(max(max(volume_size.x, volume_size.y), volume_size.z)); + + ivec3 cell_ofs = ivec3(0); + ivec3 cell_div = volume_size; + if (sliceAxis == 0) { + cell_ofs.x = int(slicePosition * float(volume_size.x)); + cell_div.x = 1; + } + else if (sliceAxis == 1) { + cell_ofs.y = int(slicePosition * float(volume_size.y)); + cell_div.y = 1; + } + else if (sliceAxis == 2) { + cell_ofs.z = int(slicePosition * float(volume_size.z)); + cell_div.z = 1; + } + + ivec3 cell_co; + cell_co.x = cell % cell_div.x; + cell_co.y = (cell / cell_div.x) % cell_div.y; + cell_co.z = cell / (cell_div.x * cell_div.y); + cell_co += cell_ofs; + + vec3 pos = (vec3(cell_co) + 0.5) / vec3(volume_size); + pos = pos * 2.0 - 1.0; + + vec3 velocity; + velocity.x = texelFetch(velocityX, cell_co, 0).r; + velocity.y = texelFetch(velocityY, cell_co, 0).r; + velocity.z = texelFetch(velocityZ, cell_co, 0).r; + + finalColor = vec4(weight_to_color(length(velocity)), 1.0); + +#ifdef USE_NEEDLE + mat3 rot_mat = rotation_from_vector(velocity); + vec3 rotated_pos = rot_mat * corners[indices[gl_VertexID % 12]]; + pos += rotated_pos * length(velocity) * displaySize * voxel_size; +#else + pos += (((gl_VertexID % 2) == 1) ? velocity : vec3(0.0)) * displaySize * voxel_size; +#endif + + gl_Position = ModelViewProjectionMatrix * vec4(pos, 1.0); +} diff --git a/source/blender/gpu/GPU_draw.h b/source/blender/gpu/GPU_draw.h index ebce83d2a5f..028756bc739 100644 --- a/source/blender/gpu/GPU_draw.h +++ b/source/blender/gpu/GPU_draw.h @@ -101,7 +101,9 @@ void GPU_free_images_old(struct Main *bmain); /* smoke drawing functions */ void GPU_free_smoke(struct SmokeModifierData *smd); +void GPU_free_smoke_velocity(struct SmokeModifierData *smd); void GPU_create_smoke(struct SmokeModifierData *smd, int highres); +void GPU_create_smoke_velocity(struct SmokeModifierData *smd); /* Delayed free of OpenGL buffers by main thread */ void GPU_free_unused_buffers(struct Main *bmain); diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c index 7383868843d..e2c83d6fadf 100644 --- a/source/blender/gpu/intern/gpu_draw.c +++ b/source/blender/gpu/intern/gpu_draw.c @@ -971,6 +971,52 @@ void GPU_create_smoke(SmokeModifierData *smd, int highres) #endif // WITH_SMOKE } +void GPU_create_smoke_velocity(SmokeModifierData *smd) +{ +#ifdef WITH_SMOKE + if (smd->type & MOD_SMOKE_TYPE_DOMAIN) { + SmokeDomainSettings *sds = smd->domain; + + const float *vel_x = smoke_get_velocity_x(sds->fluid); + const float *vel_y = smoke_get_velocity_y(sds->fluid); + const float *vel_z = smoke_get_velocity_z(sds->fluid); + + if (ELEM(NULL, vel_x, vel_y, vel_z)) { + return; + } + + if (!sds->tex_velocity_x) { + sds->tex_velocity_x = GPU_texture_create_3D(sds->res[0], sds->res[1], sds->res[2], GPU_R16F, vel_x, NULL); + sds->tex_velocity_y = GPU_texture_create_3D(sds->res[0], sds->res[1], sds->res[2], GPU_R16F, vel_y, NULL); + sds->tex_velocity_z = GPU_texture_create_3D(sds->res[0], sds->res[1], sds->res[2], GPU_R16F, vel_z, NULL); + } + } +#else // WITH_SMOKE + smd->domain->tex_velocity_x = NULL; + smd->domain->tex_velocity_y = NULL; + smd->domain->tex_velocity_z = NULL; +#endif // WITH_SMOKE +} + +/* TODO Unify with the other GPU_free_smoke. */ +void GPU_free_smoke_velocity(SmokeModifierData *smd) +{ + if (smd->type & MOD_SMOKE_TYPE_DOMAIN && smd->domain) { + if (smd->domain->tex_velocity_x) + GPU_texture_free(smd->domain->tex_velocity_x); + + if (smd->domain->tex_velocity_y) + GPU_texture_free(smd->domain->tex_velocity_y); + + if (smd->domain->tex_velocity_z) + GPU_texture_free(smd->domain->tex_velocity_z); + + smd->domain->tex_velocity_x = NULL; + smd->domain->tex_velocity_y = NULL; + smd->domain->tex_velocity_z = NULL; + } +} + static LinkNode *image_free_queue = NULL; static void gpu_queue_image_for_free(Image *ima) diff --git a/source/blender/makesdna/DNA_smoke_types.h b/source/blender/makesdna/DNA_smoke_types.h index 76b609b0c6c..443c6923ed0 100644 --- a/source/blender/makesdna/DNA_smoke_types.h +++ b/source/blender/makesdna/DNA_smoke_types.h @@ -137,6 +137,9 @@ typedef struct SmokeDomainSettings { struct GPUTexture *tex_wt; struct GPUTexture *tex_shadow; struct GPUTexture *tex_flame; + struct GPUTexture *tex_velocity_x; + struct GPUTexture *tex_velocity_y; + struct GPUTexture *tex_velocity_z; float *shadow; /* simulation data */ -- cgit v1.2.3