Viewport smoke: add support for axis aligned slicing.
authorKévin Dietrich <kevin.dietrich@mailoo.org>
Sat, 24 Sep 2016 19:41:16 +0000 (21:41 +0200)
committerKévin Dietrich <kevin.dietrich@mailoo.org>
Sat, 24 Sep 2016 20:21:02 +0000 (22:21 +0200)
Current approach uses view aligned slicing to generate polygons for GL
texturing such that the generated polygons are always facing the view
plane. Now it is also possible to use object aligned slicing, which
creates polygons by slicing the object perpendicular to whichever axis
is facing the most the view plane. It is also possible to create a
single slice for inspecting the volume, or for 2D rendering effects.

Settings for this, along with a density multiplier setting, are to be
found in a newly added "Smoke Display Settings" panel in the smoke
domain properties tab.

Reviewers: plasmasolutions, gottfried

Differential Revision: https://developer.blender.org/D1733

release/scripts/startup/bl_ui/properties_physics_smoke.py
source/blender/blenkernel/intern/smoke.c
source/blender/blenloader/intern/versioning_270.c
source/blender/editors/space_view3d/drawvolume.c
source/blender/makesdna/DNA_smoke_types.h
source/blender/makesrna/intern/rna_smoke.c

index 99aa54a958af0ebc93648ec62f54b280cfe07733..829850e4f2f81013cbdd564dbf573f814625f401 100644 (file)
@@ -349,5 +349,40 @@ class PHYSICS_PT_smoke_field_weights(PhysicButtonsPanel, Panel):
         domain = context.smoke.domain_settings
         effector_weights_ui(self, context, domain.effector_weights, 'SMOKE')
 
+
+class PHYSICS_PT_smoke_display_settings(PhysicButtonsPanel, Panel):
+    bl_label = "Smoke Display Settings"
+    bl_options = {'DEFAULT_CLOSED'}
+
+    @classmethod
+    def poll(cls, context):
+        md = context.smoke
+        rd = context.scene.render
+        return md and (md.smoke_type == 'DOMAIN') and (not rd.use_game_engine)
+
+    def draw(self, context):
+        domain = context.smoke.domain_settings
+        layout = self.layout
+
+        layout.prop(domain, "display_thickness")
+
+        layout.separator()
+        layout.label(text="Slicing:")
+        layout.prop(domain, "slice_method")
+
+        slice_method = domain.slice_method
+        axis_slice_method = domain.axis_slice_method
+
+        if slice_method == 'AXIS_ALIGNED':
+            layout.prop(domain, "axis_slice_method")
+
+            if axis_slice_method == 'SINGLE':
+                layout.prop(domain, "slice_axis")
+                layout.prop(domain, "slice_depth")
+
+        if axis_slice_method == 'FULL':
+            layout.prop(domain, "slice_per_voxel")
+
+
 if __name__ == "__main__":  # only for live edit.
     bpy.utils.register_module(__name__)
index f622d541f3aef64d619ecd68398d1094bff4d9ad..d0f546e9bef9b483b27b26a2674a71c27735b7ed 100644 (file)
@@ -536,6 +536,13 @@ void smokeModifier_createType(struct SmokeModifierData *smd)
 #endif
                        smd->domain->data_depth = 0;
                        smd->domain->cache_file_format = PTCACHE_FILE_PTCACHE;
+
+                       smd->domain->display_thickness = 1.0f;
+                       smd->domain->slice_method = MOD_SMOKE_SLICE_VIEW_ALIGNED;
+                       smd->domain->axis_slice_method = AXIS_SLICE_FULL;
+                       smd->domain->slice_per_voxel = 5.0f;
+                       smd->domain->slice_depth = 0.5f;
+                       smd->domain->slice_axis = 0;
                }
                else if (smd->type & MOD_SMOKE_TYPE_FLOW)
                {
@@ -629,6 +636,12 @@ void smokeModifier_copy(struct SmokeModifierData *smd, struct SmokeModifierData
                tsmd->domain->openvdb_comp = smd->domain->openvdb_comp;
                tsmd->domain->data_depth = smd->domain->data_depth;
                tsmd->domain->cache_file_format = smd->domain->cache_file_format;
+
+               tsmd->domain->slice_method = smd->domain->slice_method;
+               tsmd->domain->axis_slice_method = smd->domain->axis_slice_method;
+               tsmd->domain->slice_per_voxel = smd->domain->slice_per_voxel;
+               tsmd->domain->slice_depth = smd->domain->slice_depth;
+               tsmd->domain->slice_axis = smd->domain->slice_axis;
        }
        else if (tsmd->flow) {
                tsmd->flow->psys = smd->flow->psys;
index aa5ef1c98cf2bffb78b774d727d7844d41d804d0..0d4ff00b7750bb41e08c5227c68e0640d01540d5 100644 (file)
@@ -52,6 +52,7 @@
 #include "DNA_linestyle_types.h"
 #include "DNA_actuator_types.h"
 #include "DNA_view3d_types.h"
+#include "DNA_smoke_types.h"
 
 #include "DNA_genfile.h"
 
@@ -1405,5 +1406,23 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
                                scene->r.ffcodecdata.constant_rate_factor = FFM_CRF_NONE;
                        }
                }
+
+               if (!DNA_struct_elem_find(fd->filesdna, "SmokeModifierData", "float", "slice_per_voxel")) {
+                       Object *ob;
+                       ModifierData *md;
+
+                       for (ob = main->object.first; ob; ob = ob->id.next) {
+                               for (md = ob->modifiers.first; md; md = md->next) {
+                                       if (md->type == eModifierType_Smoke) {
+                                               SmokeModifierData *smd = (SmokeModifierData *)md;
+                                               if (smd->domain) {
+                                                       smd->domain->slice_per_voxel = 5.0f;
+                                                       smd->domain->slice_depth = 0.5f;
+                                                       smd->domain->display_thickness = 1.0f;
+                                               }
+                                       }
+                               }
+                       }
+               }
        }
 }
index 06677ef44767afa54351278b565cc4dda500ba93..d743e7c09e794cce8a52917b7f8dc4a09ea976b2 100644 (file)
@@ -113,6 +113,63 @@ typedef struct VolumeSlicer {
        float (*verts)[3];
 } VolumeSlicer;
 
+/* *************************** Axis Aligned Slicing ************************** */
+
+static void create_single_slice(VolumeSlicer *slicer, const float depth,
+                                const int axis, const int idx)
+{
+       const float vertices[3][4][3] = {
+           {
+               { depth, slicer->min[1], slicer->min[2] },
+               { depth, slicer->max[1], slicer->min[2] },
+               { depth, slicer->max[1], slicer->max[2] },
+               { depth, slicer->min[1], slicer->max[2] }
+           },
+           {
+               { slicer->min[0], depth, slicer->min[2] },
+               { slicer->min[0], depth, slicer->max[2] },
+               { slicer->max[0], depth, slicer->max[2] },
+               { slicer->max[0], depth, slicer->min[2] }
+           },
+           {
+               { slicer->min[0], slicer->min[1], depth },
+               { slicer->min[0], slicer->max[1], depth },
+               { slicer->max[0], slicer->max[1], depth },
+               { slicer->max[0], slicer->min[1], depth }
+           }
+       };
+
+       copy_v3_v3(slicer->verts[idx + 0], vertices[axis][0]);
+       copy_v3_v3(slicer->verts[idx + 1], vertices[axis][1]);
+       copy_v3_v3(slicer->verts[idx + 2], vertices[axis][2]);
+       copy_v3_v3(slicer->verts[idx + 3], vertices[axis][0]);
+       copy_v3_v3(slicer->verts[idx + 4], vertices[axis][2]);
+       copy_v3_v3(slicer->verts[idx + 5], vertices[axis][3]);
+}
+
+static void create_axis_aligned_slices(VolumeSlicer *slicer, const int num_slices,
+                                       const float view_dir[3], const int axis)
+{
+       float depth, slice_size = slicer->size[axis] / num_slices;
+
+       /* always process slices in back to front order! */
+       if (view_dir[axis] > 0.0f) {
+               depth = slicer->min[axis];
+       }
+       else {
+               depth = slicer->max[axis];
+               slice_size = -slice_size;
+       }
+
+       int count = 0;
+       for (int slice = 0; slice < num_slices; slice++) {
+               create_single_slice(slicer, depth, axis, count);
+
+               count += 6;
+               depth += slice_size;
+       }
+}
+
 /* *************************** View Aligned Slicing ************************** */
 
 /* Code adapted from:
@@ -433,8 +490,25 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob,
 
        /* setup slicing information */
 
-       const int max_slices = 256;
-       const int max_points = max_slices * 12;
+       const bool view_aligned = (sds->slice_method == MOD_SMOKE_SLICE_VIEW_ALIGNED);
+       int max_slices, max_points, axis = 0;
+
+       if (view_aligned) {
+               max_slices = max_iii(sds->res[0], sds->res[1], sds->res[2]) * sds->slice_per_voxel;
+               max_points = max_slices * 12;
+       }
+       else {
+               if (sds->axis_slice_method == AXIS_SLICE_FULL) {
+                       axis = axis_dominant_v3_single(viewnormal);
+                       max_slices = sds->res[axis] * sds->slice_per_voxel;
+               }
+               else {
+                       axis = (sds->slice_axis == SLICE_AXIS_AUTO) ? axis_dominant_v3_single(viewnormal) : sds->slice_axis - 1;
+                       max_slices = 1;
+               }
+
+               max_points = max_slices * 6;
+       }
 
        VolumeSlicer slicer;
        copy_v3_v3(slicer.min, min);
@@ -442,7 +516,22 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob,
        copy_v3_v3(slicer.size, size);
        slicer.verts = MEM_mallocN(sizeof(float) * 3 * max_points, "smoke_slice_vertices");
 
-       const int num_points = create_view_aligned_slices(&slicer, max_slices, viewnormal);
+       int num_points;
+
+       if (view_aligned) {
+               num_points = create_view_aligned_slices(&slicer, max_slices, viewnormal);
+       }
+       else {
+               num_points = max_points;
+
+               if (sds->axis_slice_method == AXIS_SLICE_FULL) {
+                       create_axis_aligned_slices(&slicer, max_slices, viewnormal, axis);
+               }
+               else {
+                       const float depth = (sds->slice_depth - 0.5f) * size[axis];
+                       create_single_slice(&slicer, depth, axis, 0);
+               }
+       }
 
        /* setup buffer and draw */
 
index 76de8443faa30503b03c22cc4110fd882e5e6497..5526c18f6568e0e32474c70c33cfbaf8fea1c438 100644 (file)
@@ -52,6 +52,26 @@ enum {
 /* viewsettings */
 #define MOD_SMOKE_VIEW_SHOWBIG (1<<0)
 
+/* slice method */
+enum {
+       MOD_SMOKE_SLICE_VIEW_ALIGNED = 0,
+       MOD_SMOKE_SLICE_AXIS_ALIGNED = 1,
+};
+
+/* axis aligned method */
+enum {
+       AXIS_SLICE_FULL   = 0,
+       AXIS_SLICE_SINGLE = 1,
+};
+
+/* single slice direction */
+enum {
+       SLICE_AXIS_AUTO = 0,
+       SLICE_AXIS_X    = 1,
+       SLICE_AXIS_Y    = 2,
+       SLICE_AXIS_Z    = 3,
+};
+
 /* cache compression */
 #define SM_CACHE_LIGHT         0
 #define SM_CACHE_HEAVY         1
@@ -161,6 +181,13 @@ typedef struct SmokeDomainSettings {
        float burning_rate, flame_smoke, flame_vorticity;
        float flame_ignition, flame_max_temp;
        float flame_smoke_color[3];
+
+       /* Display settings */
+       char slice_method, axis_slice_method;
+       char slice_axis, pad2;
+       float slice_per_voxel;
+       float slice_depth;
+       float display_thickness;
 } SmokeDomainSettings;
 
 
index dad5577dc12805eb42186a8629be26709a4bbcdb..22c1a115a15dd2df33dc3ca842e3a38eae473487 100644 (file)
@@ -443,6 +443,26 @@ static void rna_def_smoke_domain_settings(BlenderRNA *brna)
                {0, NULL, 0, NULL, NULL}
        };
 
+       static EnumPropertyItem smoke_view_items[] = {
+           {MOD_SMOKE_SLICE_VIEW_ALIGNED, "VIEW_ALIGNED", 0, "View", "Slice volume parallel to the view plane"},
+           {MOD_SMOKE_SLICE_AXIS_ALIGNED, "AXIS_ALIGNED", 0, "Axis", "Slice volume parallel to the major axis"},
+           {0, NULL, 0, NULL, NULL}
+       };
+
+       static EnumPropertyItem axis_slice_method_items[] = {
+           {AXIS_SLICE_FULL, "FULL", 0, "Full", "Slice the whole domain object"},
+           {AXIS_SLICE_SINGLE, "SINGLE", 0, "Single", "Perform a single slice of the domain object"},
+           {0, NULL, 0, NULL, NULL}
+       };
+
+       static EnumPropertyItem axis_slice_position_items[] = {
+           {SLICE_AXIS_AUTO, "AUTO", 0, "Auto", "Adjust slice direction according to the view direction"},
+           {SLICE_AXIS_X, "X", 0, "X", "Slice along the X axis"},
+           {SLICE_AXIS_Y, "Y", 0, "Y", "Slice along the Y axis"},
+           {SLICE_AXIS_Z, "Z", 0, "Z", "Slice along the Z axis"},
+           {0, NULL, 0, NULL, NULL}
+       };
+
        srna = RNA_def_struct(brna, "SmokeDomainSettings", NULL);
        RNA_def_struct_ui_text(srna, "Domain Settings", "Smoke domain settings");
        RNA_def_struct_sdna(srna, "SmokeDomainSettings");
@@ -719,6 +739,48 @@ static void rna_def_smoke_domain_settings(BlenderRNA *brna)
        RNA_def_property_enum_funcs(prop, NULL, "rna_Smoke_cachetype_set", NULL);
        RNA_def_property_ui_text(prop, "File Format", "Select the file format to be used for caching");
        RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache");
+
+       /* display settings */
+
+       prop = RNA_def_property(srna, "slice_method", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_sdna(prop, NULL, "slice_method");
+       RNA_def_property_enum_items(prop, smoke_view_items);
+       RNA_def_property_ui_text(prop, "View Method", "How to slice the volume for viewport rendering");
+       RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
+
+       prop = RNA_def_property(srna, "axis_slice_method", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_sdna(prop, NULL, "axis_slice_method");
+       RNA_def_property_enum_items(prop, axis_slice_method_items);
+       RNA_def_property_ui_text(prop, "Method", "");
+       RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
+
+       prop = RNA_def_property(srna, "slice_axis", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_sdna(prop, NULL, "slice_axis");
+       RNA_def_property_enum_items(prop, axis_slice_position_items);
+       RNA_def_property_ui_text(prop, "Axis", "");
+       RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
+
+       prop = RNA_def_property(srna, "slice_per_voxel", PROP_FLOAT, PROP_NONE);
+       RNA_def_property_float_sdna(prop, NULL, "slice_per_voxel");
+       RNA_def_property_range(prop, 0.0, 100.0);
+       RNA_def_property_ui_range(prop, 0.0, 5.0, 0.1, 1);
+       RNA_def_property_ui_text(prop, "Slice Per Voxel",
+                                "How many slices per voxel should be generated");
+       RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
+
+       prop = RNA_def_property(srna, "slice_depth", PROP_FLOAT, PROP_NONE);
+       RNA_def_property_float_sdna(prop, NULL, "slice_depth");
+       RNA_def_property_range(prop, 0.0, 1.0);
+       RNA_def_property_ui_range(prop, 0.0, 1.0, 0.1, 3);
+       RNA_def_property_ui_text(prop, "Position", "Position of the slice");
+       RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
+
+       prop = RNA_def_property(srna, "display_thickness", PROP_FLOAT, PROP_NONE);
+       RNA_def_property_float_sdna(prop, NULL, "display_thickness");
+       RNA_def_property_range(prop, 0.001, 1000.0);
+       RNA_def_property_ui_range(prop, 0.1, 100.0, 0.1, 3);
+       RNA_def_property_ui_text(prop, "Thickness", "Thickness of smoke drawing in the viewport");
+       RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, NULL);
 }
 
 static void rna_def_smoke_flow_settings(BlenderRNA *brna)