Viewport smoke: add support for axis aligned slicing.

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
This commit is contained in:
Kévin Dietrich 2016-09-24 21:41:16 +02:00
parent a070a5befa
commit 4446acbf17
6 changed files with 248 additions and 3 deletions

@ -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__)

@ -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;

@ -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;
}
}
}
}
}
}
}

@ -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 */

@ -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;

@ -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)