Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorClément Foucault <foucault.clem@gmail.com>2018-07-16 16:01:44 +0300
committerClément Foucault <foucault.clem@gmail.com>2018-07-16 21:08:09 +0300
commit7a693626d63a52acd12e80209b634711154a2f9d (patch)
tree96bc9529b14b369292d40425dc22e0a50800a1f4 /source
parent43593e846bbed183a54ee1dae1b76f3f8c4be9bf (diff)
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.
Diffstat (limited to 'source')
-rw-r--r--source/blender/blenloader/intern/readfile.c3
-rw-r--r--source/blender/draw/CMakeLists.txt4
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_volume_frag.glsl181
-rw-r--r--source/blender/draw/engines/workbench/shaders/workbench_volume_vert.glsl31
-rw-r--r--source/blender/draw/engines/workbench/workbench_data.c3
-rw-r--r--source/blender/draw/engines/workbench/workbench_deferred.c32
-rw-r--r--source/blender/draw/engines/workbench/workbench_forward.c23
-rw-r--r--source/blender/draw/engines/workbench/workbench_private.h14
-rw-r--r--source/blender/draw/engines/workbench/workbench_volume.c152
-rw-r--r--source/blender/draw/intern/draw_cache.c53
-rw-r--r--source/blender/draw/intern/draw_cache.h3
-rw-r--r--source/blender/draw/intern/draw_common.c25
-rw-r--r--source/blender/draw/intern/draw_common.h2
-rw-r--r--source/blender/draw/modes/object_mode.c101
-rw-r--r--source/blender/draw/modes/shaders/volume_velocity_vert.glsl115
-rw-r--r--source/blender/gpu/GPU_draw.h2
-rw-r--r--source/blender/gpu/intern/gpu_draw.c46
-rw-r--r--source/blender/makesdna/DNA_smoke_types.h3
18 files changed, 782 insertions, 11 deletions
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 */