Cycles: add "density", "flame" and "color" attributes for smoke domains.
authorBrecht Van Lommel <brechtvanlommel@gmail.com>
Sat, 29 Mar 2014 12:03:48 +0000 (13:03 +0100)
committerBrecht Van Lommel <brechtvanlommel@gmail.com>
Sat, 29 Mar 2014 12:03:48 +0000 (13:03 +0100)
These can currently be accessed by adding an Attribute node and specifying one
of those three names. A Smoke/Fire node should be added at some point to make
this more convenient.

These values might change still before the release, in particular for flame the
meaning seems unclear, it's just values in the 0..1 range. This is useful for
color ramps, but it might be good if this was also available as temperature in
kelvin so it can be plugged into the blackbody node. But I couldn't figure out
from the smoke code if or how this corresponds to a physical unit.

Here's a (quite poor) example file for a fire + smoke setup:
http://www.pasteall.org/blend/27990

intern/cycles/blender/blender_mesh.cpp
intern/cycles/blender/blender_session.cpp
intern/cycles/blender/blender_util.h
intern/cycles/kernel/kernel_types.h
intern/cycles/render/attribute.cpp

index 8c9734283eaa530b7e5450010d07aab5460fdfa8..fb667d1ba2f9535d0fb34650b6bcad028eb4c708 100644 (file)
@@ -206,6 +206,40 @@ static void mikk_compute_tangents(BL::Mesh b_mesh, BL::MeshTextureFaceLayer b_la
        }
 }
 
+/* Create Volume Attribute */
+
+static void create_mesh_volume_attribute(BL::Object b_ob, Mesh *mesh, ImageManager *image_manager, AttributeStandard std)
+{
+       BL::SmokeDomainSettings b_domain = object_smoke_domain_find(b_ob);
+
+       if(!b_domain)
+               return;
+       
+       Attribute *attr = mesh->attributes.add(std);
+       VoxelAttribute *volume_data = attr->data_voxel();
+       bool is_float, is_linear;
+       bool animated = false;
+
+       volume_data->manager = image_manager;
+       volume_data->slot = image_manager->add_image(Attribute::standard_name(std),
+               b_ob.ptr.data, animated, is_float, is_linear, INTERPOLATION_LINEAR);
+}
+
+static void create_mesh_volume_attributes(Scene *scene, BL::Object b_ob, Mesh *mesh)
+{
+       /* for smoke volume rendering */
+       if(mesh->need_attribute(scene, ATTR_STD_VOLUME_DENSITY))
+               create_mesh_volume_attribute(b_ob, mesh, scene->image_manager, ATTR_STD_VOLUME_DENSITY);
+       if(mesh->need_attribute(scene, ATTR_STD_VOLUME_COLOR))
+               create_mesh_volume_attribute(b_ob, mesh, scene->image_manager, ATTR_STD_VOLUME_COLOR);
+       if(mesh->need_attribute(scene, ATTR_STD_VOLUME_FLAME))
+               create_mesh_volume_attribute(b_ob, mesh, scene->image_manager, ATTR_STD_VOLUME_FLAME);
+       if(mesh->need_attribute(scene, ATTR_STD_VOLUME_HEAT))
+               create_mesh_volume_attribute(b_ob, mesh, scene->image_manager, ATTR_STD_VOLUME_HEAT);
+       if(mesh->need_attribute(scene, ATTR_STD_VOLUME_VELOCITY))
+               create_mesh_volume_attribute(b_ob, mesh, scene->image_manager, ATTR_STD_VOLUME_VELOCITY);
+}
+
 /* Create Mesh */
 
 static void create_mesh(Scene *scene, Mesh *mesh, BL::Mesh b_mesh, const vector<uint>& used_shaders)
@@ -501,6 +535,8 @@ Mesh *BlenderSync::sync_mesh(BL::Object b_ob, bool object_updated, bool hide_tri
                                        create_subd_mesh(scene, mesh, b_mesh, &cmesh, used_shaders);
                                else
                                        create_mesh(scene, mesh, b_mesh, used_shaders);
+
+                               create_mesh_volume_attributes(scene, b_ob, mesh);
                        }
 
                        if(render_layer.use_hair)
index 82434712ca72c5a097f138cd765ff3e0f4d90f69..f5e696bc582e2a3962181b809ff2fbd8f989270b 100644 (file)
@@ -726,85 +726,121 @@ int BlenderSession::builtin_image_frame(const string &builtin_name)
 
 void BlenderSession::builtin_image_info(const string &builtin_name, void *builtin_data, bool &is_float, int &width, int &height, int &depth, int &channels)
 {
+       /* empty image */
+       is_float = false;
+       width = 0;
+       height = 0;
+       depth = 0;
+       channels = 0;
+
+       if(!builtin_data)
+               return;
+
+       /* recover ID pointer */
        PointerRNA ptr;
        RNA_id_pointer_create((ID*)builtin_data, &ptr);
-       BL::Image b_image(ptr);
+       BL::ID b_id(ptr);
+
+       if(b_id.is_a(&RNA_Image)) {
+               /* image data */
+               BL::Image b_image(b_id);
 
-       if(b_image) {
                is_float = b_image.is_float();
                width = b_image.size()[0];
                height = b_image.size()[1];
                depth = 1;
                channels = b_image.channels();
        }
-       else {
-               is_float = false;
-               width = 0;
-               height = 0;
-               depth = 0;
-               channels = 0;
+       else if(b_id.is_a(&RNA_Object)) {
+               /* smoke volume data */
+               BL::Object b_ob(b_id);
+               BL::SmokeDomainSettings b_domain = object_smoke_domain_find(b_ob);
+
+               if(!b_domain)
+                       return;
+
+               if(builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_DENSITY) ||
+                  builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_FLAME))
+                       channels = 1;
+               else if(builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_COLOR))
+                       channels = 4;
+               else
+                       return;
+
+               int3 resolution = get_int3(b_domain.domain_resolution());
+               int amplify = (b_domain.use_high_resolution())? b_domain.amplify() + 1: 1;
+
+               width = resolution.x * amplify;
+               height = resolution.y * amplify;
+               depth = resolution.z * amplify;
+
+               is_float = true;
        }
 }
 
 bool BlenderSession::builtin_image_pixels(const string &builtin_name, void *builtin_data, unsigned char *pixels)
 {
+       if(!builtin_data)
+               return false;
+
        int frame = builtin_image_frame(builtin_name);
 
        PointerRNA ptr;
        RNA_id_pointer_create((ID*)builtin_data, &ptr);
        BL::Image b_image(ptr);
 
-       if(b_image) {
-               int width = b_image.size()[0];
-               int height = b_image.size()[1];
-               int channels = b_image.channels();
+       int width = b_image.size()[0];
+       int height = b_image.size()[1];
+       int channels = b_image.channels();
 
-               unsigned char *image_pixels;
-               image_pixels = image_get_pixels_for_frame(b_image, frame);
+       unsigned char *image_pixels;
+       image_pixels = image_get_pixels_for_frame(b_image, frame);
 
-               if(image_pixels) {
-                       memcpy(pixels, image_pixels, width * height * channels * sizeof(unsigned char));
-                       MEM_freeN(image_pixels);
+       if(image_pixels) {
+               memcpy(pixels, image_pixels, width * height * channels * sizeof(unsigned char));
+               MEM_freeN(image_pixels);
+       }
+       else {
+               if(channels == 1) {
+                       memset(pixels, 0, width * height * sizeof(unsigned char));
                }
                else {
-                       if(channels == 1) {
-                               memset(pixels, 0, width * height * sizeof(unsigned char));
-                       }
-                       else {
-                               unsigned char *cp = pixels;
-                               for(int i = 0; i < width * height; i++, cp += channels) {
-                                       cp[0] = 255;
-                                       cp[1] = 0;
-                                       cp[2] = 255;
-                                       if(channels == 4)
-                                               cp[3] = 255;
-                               }
+                       unsigned char *cp = pixels;
+                       for(int i = 0; i < width * height; i++, cp += channels) {
+                               cp[0] = 255;
+                               cp[1] = 0;
+                               cp[2] = 255;
+                               if(channels == 4)
+                                       cp[3] = 255;
                        }
                }
+       }
 
-               /* premultiply, byte images are always straight for blender */
-               unsigned char *cp = pixels;
-               for(int i = 0; i < width * height; i++, cp += channels) {
-                       cp[0] = (cp[0] * cp[3]) >> 8;
-                       cp[1] = (cp[1] * cp[3]) >> 8;
-                       cp[2] = (cp[2] * cp[3]) >> 8;
-               }
-
-               return true;
+       /* premultiply, byte images are always straight for blender */
+       unsigned char *cp = pixels;
+       for(int i = 0; i < width * height; i++, cp += channels) {
+               cp[0] = (cp[0] * cp[3]) >> 8;
+               cp[1] = (cp[1] * cp[3]) >> 8;
+               cp[2] = (cp[2] * cp[3]) >> 8;
        }
 
-       return false;
+       return true;
 }
 
 bool BlenderSession::builtin_image_float_pixels(const string &builtin_name, void *builtin_data, float *pixels)
 {
-       int frame = builtin_image_frame(builtin_name);
+       if(!builtin_data)
+               return false;
 
        PointerRNA ptr;
        RNA_id_pointer_create((ID*)builtin_data, &ptr);
-       BL::Image b_image(ptr);
+       BL::ID b_id(ptr);
+
+       if(b_id.is_a(&RNA_Image)) {
+               /* image data */
+               BL::Image b_image(b_id);
+               int frame = builtin_image_frame(builtin_name);
 
-       if(b_image) {
                int width = b_image.size()[0];
                int height = b_image.size()[1];
                int channels = b_image.channels();
@@ -834,6 +870,51 @@ bool BlenderSession::builtin_image_float_pixels(const string &builtin_name, void
 
                return true;
        }
+       else if(b_id.is_a(&RNA_Object)) {
+               /* smoke volume data */
+               BL::Object b_ob(b_id);
+               BL::SmokeDomainSettings b_domain = object_smoke_domain_find(b_ob);
+
+               if(!b_domain)
+                       return false;
+
+               int3 resolution = get_int3(b_domain.domain_resolution());
+               int length, amplify = (b_domain.use_high_resolution())? b_domain.amplify() + 1: 1;
+
+               int width = resolution.x * amplify;
+               int height = resolution.y * amplify;
+               int depth = resolution.z * amplify;
+
+               if(builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_DENSITY)) {
+                       SmokeDomainSettings_density_grid_get_length(&b_domain.ptr, &length);
+
+                       if(length == width*height*depth) {
+                               SmokeDomainSettings_density_grid_get(&b_domain.ptr, pixels);
+                               return true;
+                       }
+               }
+               else if(builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_FLAME)) {
+                       /* this is in range 0..1, and interpreted by the OpenGL smoke viewer
+                        * as 1500..3000 K with the first part faded to zero density */
+                       SmokeDomainSettings_flame_grid_get_length(&b_domain.ptr, &length);
+
+                       if(length == width*height*depth) {
+                               SmokeDomainSettings_flame_grid_get(&b_domain.ptr, pixels);
+                               return true;
+                       }
+               }
+               else if(builtin_name == Attribute::standard_name(ATTR_STD_VOLUME_COLOR)) {
+                       /* the RGB is "premultiplied" by density for better interpolation results */
+                       SmokeDomainSettings_color_grid_get_length(&b_domain.ptr, &length);
+
+                       if(length == width*height*depth*4) {
+                               SmokeDomainSettings_color_grid_get(&b_domain.ptr, pixels);
+                               return true;
+                       }
+               }
+
+               fprintf(stderr, "Cycles error: unexpected smoke volume resolution, skipping\n");
+       }
 
        return false;
 }
index bea5ee49bf44a439fa5adc8367913d867c4cb988..45ca0e175500d8bd6d9050ff8d8532dce4499b24 100644 (file)
@@ -168,6 +168,11 @@ static inline float4 get_float4(BL::Array<float, 4> array)
        return make_float4(array[0], array[1], array[2], array[3]);
 }
 
+static inline int3 get_int3(BL::Array<int, 3> array)
+{
+       return make_int3(array[0], array[1], array[2]);
+}
+
 static inline int4 get_int4(BL::Array<int, 4> array)
 {
        return make_int4(array[0], array[1], array[2], array[3]);
@@ -371,6 +376,22 @@ static inline bool object_use_deform_motion(BL::Object b_ob)
        return use_deform_motion;
 }
 
+static inline BL::SmokeDomainSettings object_smoke_domain_find(BL::Object b_ob)
+{
+       BL::Object::modifiers_iterator b_mod;
+
+       for(b_ob.modifiers.begin(b_mod); b_mod != b_ob.modifiers.end(); ++b_mod) {
+               if (b_mod->is_a(&RNA_SmokeModifier)) {
+                       BL::SmokeModifier b_smd(*b_mod);
+
+                       if(b_smd.smoke_type() == BL::SmokeModifier::smoke_type_DOMAIN)
+                               return b_smd.domain_settings();
+               }
+       }
+       
+       return BL::SmokeDomainSettings(PointerRNA_NULL);
+}
+
 /* ID Map
  *
  * Utility class to keep in sync with blender data.
index f7aebd29bbe39f448e34a23934b3b5c8e4bc4fd7..f01b173269cdc34b4013650bb374a4a649dce628 100644 (file)
@@ -480,6 +480,11 @@ typedef enum AttributeStandard {
        ATTR_STD_CURVE_INTERCEPT,
        ATTR_STD_PTEX_FACE_ID,
        ATTR_STD_PTEX_UV,
+       ATTR_STD_VOLUME_DENSITY,
+       ATTR_STD_VOLUME_COLOR,
+       ATTR_STD_VOLUME_FLAME,
+       ATTR_STD_VOLUME_HEAT,
+       ATTR_STD_VOLUME_VELOCITY,
        ATTR_STD_NUM,
 
        ATTR_STD_NOT_FOUND = ~0
index cd81c33a28fb77ce2fa891bde0c38cbe0c1957e4..e5b1ee7b95ed94ddf563f967e4d162f7e37dc062 100644 (file)
@@ -202,6 +202,16 @@ const char *Attribute::standard_name(AttributeStandard std)
                        return "ptex_face_id";
                case ATTR_STD_PTEX_UV:
                        return "ptex_uv";
+               case ATTR_STD_VOLUME_DENSITY:
+                       return "density";
+               case ATTR_STD_VOLUME_COLOR:
+                       return "color";
+               case ATTR_STD_VOLUME_FLAME:
+                       return "flame";
+               case ATTR_STD_VOLUME_HEAT:
+                       return "heat";
+               case ATTR_STD_VOLUME_VELOCITY:
+                       return "velocity";
                case ATTR_STD_NOT_FOUND:
                case ATTR_STD_NONE:
                case ATTR_STD_NUM:
@@ -328,6 +338,17 @@ Attribute *AttributeSet::add(AttributeStandard std, ustring name)
                        case ATTR_STD_GENERATED_TRANSFORM:
                                attr = add(name, TypeDesc::TypeMatrix, ATTR_ELEMENT_MESH);
                                break;
+                       case ATTR_STD_VOLUME_DENSITY:
+                       case ATTR_STD_VOLUME_FLAME:
+                       case ATTR_STD_VOLUME_HEAT:
+                               attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_VOXEL);
+                               break;
+                       case ATTR_STD_VOLUME_COLOR:
+                               attr = add(name, TypeDesc::TypeColor, ATTR_ELEMENT_VOXEL);
+                               break;
+                       case ATTR_STD_VOLUME_VELOCITY:
+                               attr = add(name, TypeDesc::TypeVector, ATTR_ELEMENT_VOXEL);
+                               break;
                        default:
                                assert(0);
                                break;