Cycles: add "density", "flame" and "color" attributes for smoke domains.
[blender.git] / intern / cycles / blender / blender_mesh.cpp
index 7055cf981c7074b09e9413e00533c08b8efe1a5a..fb667d1ba2f9535d0fb34650b6bcad028eb4c708 100644 (file)
@@ -1,21 +1,20 @@
 /*
- * Copyright 2011, Blender Foundation.
+ * Copyright 2011-2013 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.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- * 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.
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
- * 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.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
  */
 
 #include "mesh.h"
 #include "object.h"
 #include "scene.h"
 
 #include "util_foreach.h"
 
+#include "mikktspace.h"
+
 CCL_NAMESPACE_BEGIN
 
-/* Find/Add */
+/* Tangent Space */
+
+struct MikkUserData {
+       MikkUserData(const BL::Mesh mesh_, const BL::MeshTextureFaceLayer layer_, int num_faces_)
+       : mesh(mesh_), layer(layer_), num_faces(num_faces_)
+       {
+               tangent.resize(num_faces*4);
+       }
+
+       BL::Mesh mesh;
+       BL::MeshTextureFaceLayer layer;
+       int num_faces;
+       vector<float4> tangent;
+};
+
+static int mikk_get_num_faces(const SMikkTSpaceContext *context)
+{
+       MikkUserData *userdata = (MikkUserData*)context->m_pUserData;
+       return userdata->num_faces;
+}
+
+static int mikk_get_num_verts_of_face(const SMikkTSpaceContext *context, const int face_num)
+{
+       MikkUserData *userdata = (MikkUserData*)context->m_pUserData;
+       BL::MeshTessFace f = userdata->mesh.tessfaces[face_num];
+       int4 vi = get_int4(f.vertices_raw());
+
+       return (vi[3] == 0)? 3: 4;
+}
 
-static float3 tangent_from_triangle(float3 v0, float3 v1, float3 v2, float3 tx0, float3 tx1, float3 tx2)
+static void mikk_get_position(const SMikkTSpaceContext *context, float P[3], const int face_num, const int vert_num)
 {
-       float3 duv1 = tx2 - tx0;
-       float3 duv2 = tx2 - tx1;
-       float3 dp1 = v2 - v0;
-       float3 dp2 = v2 - v1;
-       float det = duv1[0] * duv2[1] - duv1[1] * duv2[0];
-
-       if(det != 0.0f) {
-               return normalize(dp1 * duv2[1] - dp2 * duv1[1]);
+       MikkUserData *userdata = (MikkUserData*)context->m_pUserData;
+       BL::MeshTessFace f = userdata->mesh.tessfaces[face_num];
+       int4 vi = get_int4(f.vertices_raw());
+       BL::MeshVertex v = userdata->mesh.vertices[vi[vert_num]];
+       float3 vP = get_float3(v.co());
+
+       P[0] = vP.x;
+       P[1] = vP.y;
+       P[2] = vP.z;
+}
+
+static void mikk_get_texture_coordinate(const SMikkTSpaceContext *context, float uv[2], const int face_num, const int vert_num)
+{
+       MikkUserData *userdata = (MikkUserData*)context->m_pUserData;
+       BL::MeshTextureFace tf = userdata->layer.data[face_num];
+       float3 tfuv;
+       
+       switch (vert_num) {
+               case 0:
+                       tfuv = get_float3(tf.uv1());
+                       break;
+               case 1:
+                       tfuv = get_float3(tf.uv2());
+                       break;
+               case 2:
+                       tfuv = get_float3(tf.uv3());
+                       break;
+               default:
+                       tfuv = get_float3(tf.uv4());
+                       break;
+       }
+       
+       uv[0] = tfuv.x;
+       uv[1] = tfuv.y;
+}
+
+static void mikk_get_normal(const SMikkTSpaceContext *context, float N[3], const int face_num, const int vert_num)
+{
+       MikkUserData *userdata = (MikkUserData*)context->m_pUserData;
+       BL::MeshTessFace f = userdata->mesh.tessfaces[face_num];
+       float3 vN;
+
+       if(f.use_smooth()) {
+               int4 vi = get_int4(f.vertices_raw());
+               BL::MeshVertex v = userdata->mesh.vertices[vi[vert_num]];
+               vN = get_float3(v.normal());
        }
        else {
-               /* give back a sane default, using a valid edge as a fallback */
-               float3 edge = v1 - v0;
+               vN = get_float3(f.normal());
+       }
 
-               if(len(edge) == 0.0f)
-                       edge = v2 - v0;
+       N[0] = vN.x;
+       N[1] = vN.y;
+       N[2] = vN.z;
+}
+
+static void mikk_set_tangent_space(const SMikkTSpaceContext *context, const float T[], const float sign, const int face, const int vert)
+{
+       MikkUserData *userdata = (MikkUserData*)context->m_pUserData;
+
+       userdata->tangent[face*4 + vert] = make_float4(T[0], T[1], T[2], sign);
+}
 
-               return normalize(edge);
+static void mikk_compute_tangents(BL::Mesh b_mesh, BL::MeshTextureFaceLayer b_layer, Mesh *mesh, vector<int>& nverts, bool need_sign, bool active_render)
+{
+       /* setup userdata */
+       MikkUserData userdata(b_mesh, b_layer, nverts.size());
+
+       /* setup interface */
+       SMikkTSpaceInterface sm_interface;
+       memset(&sm_interface, 0, sizeof(sm_interface));
+       sm_interface.m_getNumFaces = mikk_get_num_faces;
+       sm_interface.m_getNumVerticesOfFace = mikk_get_num_verts_of_face;
+       sm_interface.m_getPosition = mikk_get_position;
+       sm_interface.m_getTexCoord = mikk_get_texture_coordinate;
+       sm_interface.m_getNormal = mikk_get_normal;
+       sm_interface.m_setTSpaceBasic = mikk_set_tangent_space;
+
+       /* setup context */
+       SMikkTSpaceContext context;
+       memset(&context, 0, sizeof(context));
+       context.m_pUserData = &userdata;
+       context.m_pInterface = &sm_interface;
+
+       /* compute tangents */
+       genTangSpaceDefault(&context);
+
+       /* create tangent attributes */
+       Attribute *attr;
+       ustring name = ustring((string(b_layer.name().c_str()) + ".tangent").c_str());
+
+       if(active_render)
+               attr = mesh->attributes.add(ATTR_STD_UV_TANGENT, name);
+       else
+               attr = mesh->attributes.add(name, TypeDesc::TypeVector, ATTR_ELEMENT_CORNER);
+
+       float3 *tangent = attr->data_float3();
+
+       /* create bitangent sign attribute */
+       float *tangent_sign = NULL;
+
+       if(need_sign) {
+               Attribute *attr_sign;
+               ustring name_sign = ustring((string(b_layer.name().c_str()) + ".tangent_sign").c_str());
+
+               if(active_render)
+                       attr_sign = mesh->attributes.add(ATTR_STD_UV_TANGENT_SIGN, name_sign);
+               else
+                       attr_sign = mesh->attributes.add(name_sign, TypeDesc::TypeFloat, ATTR_ELEMENT_CORNER);
+
+               tangent_sign = attr_sign->data_float();
+       }
+
+       for(int i = 0; i < nverts.size(); i++) {
+               tangent[0] = float4_to_float3(userdata.tangent[i*4 + 0]);
+               tangent[1] = float4_to_float3(userdata.tangent[i*4 + 1]);
+               tangent[2] = float4_to_float3(userdata.tangent[i*4 + 2]);
+               tangent += 3;
+
+               if(tangent_sign) {
+                       tangent_sign[0] = userdata.tangent[i*4 + 0].w;
+                       tangent_sign[1] = userdata.tangent[i*4 + 1].w;
+                       tangent_sign[2] = userdata.tangent[i*4 + 2].w;
+                       tangent_sign += 3;
+               }
+
+               if(nverts[i] == 4) {
+                       tangent[0] = float4_to_float3(userdata.tangent[i*4 + 0]);
+                       tangent[1] = float4_to_float3(userdata.tangent[i*4 + 2]);
+                       tangent[2] = float4_to_float3(userdata.tangent[i*4 + 3]);
+                       tangent += 3;
+
+                       if(tangent_sign) {
+                               tangent_sign[0] = userdata.tangent[i*4 + 0].w;
+                               tangent_sign[1] = userdata.tangent[i*4 + 2].w;
+                               tangent_sign[2] = userdata.tangent[i*4 + 3].w;
+                               tangent_sign += 3;
+                       }
+               }
        }
 }
 
+/* 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)
 {
-       /* create vertices */
+       /* count vertices and faces */
+       int numverts = b_mesh.vertices.length();
+       int numfaces = b_mesh.tessfaces.length();
+       int numtris = 0;
+
        BL::Mesh::vertices_iterator v;
+       BL::Mesh::tessfaces_iterator f;
 
-       for(b_mesh.vertices.begin(v); v != b_mesh.vertices.end(); ++v)
-               mesh->verts.push_back(get_float3(v->co()));
+       for(b_mesh.tessfaces.begin(f); f != b_mesh.tessfaces.end(); ++f) {
+               int4 vi = get_int4(f->vertices_raw());
+               numtris += (vi[3] == 0)? 1: 2;
+       }
+
+       /* reserve memory */
+       mesh->reserve(numverts, numtris, 0, 0);
+
+       /* create vertex coordinates and normals */
+       int i = 0;
+       for(b_mesh.vertices.begin(v); v != b_mesh.vertices.end(); ++v, ++i)
+               mesh->verts[i] = get_float3(v->co());
 
-       /* create vertex normals */
        Attribute *attr_N = mesh->attributes.add(ATTR_STD_VERTEX_NORMAL);
        float3 *N = attr_N->data_float3();
 
@@ -71,22 +272,31 @@ static void create_mesh(Scene *scene, Mesh *mesh, BL::Mesh b_mesh, const vector<
                *N = get_float3(v->normal());
 
        /* create faces */
-       BL::Mesh::tessfaces_iterator f;
-       vector<int> nverts;
+       vector<int> nverts(numfaces);
+       int fi = 0, ti = 0;
 
-       for(b_mesh.tessfaces.begin(f); f != b_mesh.tessfaces.end(); ++f) {
+       for(b_mesh.tessfaces.begin(f); f != b_mesh.tessfaces.end(); ++f, ++fi) {
                int4 vi = get_int4(f->vertices_raw());
                int n = (vi[3] == 0)? 3: 4;
                int mi = clamp(f->material_index(), 0, used_shaders.size()-1);
                int shader = used_shaders[mi];
                bool smooth = f->use_smooth();
 
-               mesh->add_triangle(vi[0], vi[1], vi[2], shader, smooth);
-
-               if(n == 4)
-                       mesh->add_triangle(vi[0], vi[2], vi[3], shader, smooth);
+               if(n == 4) {
+                       if(is_zero(cross(mesh->verts[vi[1]] - mesh->verts[vi[0]], mesh->verts[vi[2]] - mesh->verts[vi[0]])) ||
+                               is_zero(cross(mesh->verts[vi[2]] - mesh->verts[vi[0]], mesh->verts[vi[3]] - mesh->verts[vi[0]]))) {
+                               mesh->set_triangle(ti++, vi[0], vi[1], vi[3], shader, smooth);
+                               mesh->set_triangle(ti++, vi[2], vi[3], vi[1], shader, smooth);
+                       }
+                       else {
+                               mesh->set_triangle(ti++, vi[0], vi[1], vi[2], shader, smooth);
+                               mesh->set_triangle(ti++, vi[0], vi[2], vi[3], shader, smooth);
+                       }
+               }
+               else
+                       mesh->set_triangle(ti++, vi[0], vi[1], vi[2], shader, smooth);
 
-               nverts.push_back(n);
+               nverts[fi] = n;
        }
 
        /* create vertex color attributes */
@@ -98,7 +308,7 @@ static void create_mesh(Scene *scene, Mesh *mesh, BL::Mesh b_mesh, const vector<
                                continue;
 
                        Attribute *attr = mesh->attributes.add(
-                               ustring(l->name().c_str()), TypeDesc::TypeColor, Attribute::CORNER);
+                               ustring(l->name().c_str()), TypeDesc::TypeColor, ATTR_ELEMENT_CORNER);
 
                        BL::MeshColorLayer::data_iterator c;
                        float3 *fdata = attr->data_float3();
@@ -126,121 +336,81 @@ static void create_mesh(Scene *scene, Mesh *mesh, BL::Mesh b_mesh, const vector<
                BL::Mesh::tessface_uv_textures_iterator l;
 
                for(b_mesh.tessface_uv_textures.begin(l); l != b_mesh.tessface_uv_textures.end(); ++l) {
-                       AttributeStandard std = (l->active_render())? ATTR_STD_UV: ATTR_STD_NONE;
+                       bool active_render = l->active_render();
+                       AttributeStandard std = (active_render)? ATTR_STD_UV: ATTR_STD_NONE;
                        ustring name = ustring(l->name().c_str());
 
-                       if(!(mesh->need_attribute(scene, name) || mesh->need_attribute(scene, std)))
-                               continue;
-
-                       Attribute *attr;
+                       /* UV map */
+                       if(mesh->need_attribute(scene, name) || mesh->need_attribute(scene, std)) {
+                               Attribute *attr;
 
-                       if(l->active_render())
-                               attr = mesh->attributes.add(std, name);
-                       else
-                               attr = mesh->attributes.add(name, TypeDesc::TypePoint, Attribute::CORNER);
-
-                       BL::MeshTextureFaceLayer::data_iterator t;
-                       float3 *fdata = attr->data_float3();
-                       size_t i = 0;
+                               if(active_render)
+                                       attr = mesh->attributes.add(std, name);
+                               else
+                                       attr = mesh->attributes.add(name, TypeDesc::TypePoint, ATTR_ELEMENT_CORNER);
 
-                       for(l->data.begin(t); t != l->data.end(); ++t, ++i) {
-                               fdata[0] =  get_float3(t->uv1());
-                               fdata[1] =  get_float3(t->uv2());
-                               fdata[2] =  get_float3(t->uv3());
-                               fdata += 3;
+                               BL::MeshTextureFaceLayer::data_iterator t;
+                               float3 *fdata = attr->data_float3();
+                               size_t i = 0;
 
-                               if(nverts[i] == 4) {
+                               for(l->data.begin(t); t != l->data.end(); ++t, ++i) {
                                        fdata[0] =  get_float3(t->uv1());
-                                       fdata[1] =  get_float3(t->uv3());
-                                       fdata[2] =  get_float3(t->uv4());
+                                       fdata[1] =  get_float3(t->uv2());
+                                       fdata[2] =  get_float3(t->uv3());
                                        fdata += 3;
+
+                                       if(nverts[i] == 4) {
+                                               fdata[0] =  get_float3(t->uv1());
+                                               fdata[1] =  get_float3(t->uv3());
+                                               fdata[2] =  get_float3(t->uv4());
+                                               fdata += 3;
+                                       }
                                }
                        }
-               }
-       }
-
-       /* create texcoord-based tangent attributes */
-       if(mesh->need_attribute(scene, ATTR_STD_TANGENT)) {
-               BL::Mesh::tessface_uv_textures_iterator l;
-
-               for(b_mesh.tessface_uv_textures.begin(l); l != b_mesh.tessface_uv_textures.end(); ++l) {
-                       if(!l->active_render())
-                               continue;
-
-                       Attribute *attr = mesh->attributes.add(ATTR_STD_TANGENT, ustring("Tangent"));
-
-                       /* compute average tangents per vertex */
-                       float3 *tangents = attr->data_float3();
-                       memset(tangents, 0, sizeof(float3)*mesh->verts.size());
-
-                       BL::MeshTextureFaceLayer::data_iterator t;
-
-                       size_t fi = 0; /* face index */
-                       b_mesh.tessfaces.begin(f);
-                       for(l->data.begin(t); t != l->data.end() && f != b_mesh.tessfaces.end(); ++t, ++fi, ++f) {
-                               int4 vi = get_int4(f->vertices_raw());
 
-                               float3 tx0 = get_float3(t->uv1());
-                               float3 tx1 = get_float3(t->uv2());
-                               float3 tx2 = get_float3(t->uv3());
+                       /* UV tangent */
+                       std = (active_render)? ATTR_STD_UV_TANGENT: ATTR_STD_NONE;
+                       name = ustring((string(l->name().c_str()) + ".tangent").c_str());
 
-                               float3 v0 = mesh->verts[vi[0]];
-                               float3 v1 = mesh->verts[vi[1]];
-                               float3 v2 = mesh->verts[vi[2]];
+                       if(mesh->need_attribute(scene, name) || (active_render && mesh->need_attribute(scene, std))) {
+                               std = (active_render)? ATTR_STD_UV_TANGENT_SIGN: ATTR_STD_NONE;
+                               name = ustring((string(l->name().c_str()) + ".tangent_sign").c_str());
+                               bool need_sign = (mesh->need_attribute(scene, name) || mesh->need_attribute(scene, std));
 
-                               /* calculate tangent for the triangle;
-                                * get vertex positions, and find change in position with respect
-                                * to the texture coords in the first texture coord dimension */
-                               float3 tangent0 = tangent_from_triangle(v0, v1, v2, tx0, tx1, tx2);
-
-                               if(nverts[fi] == 4) {
-                                       /* quad tangent */
-                                       float3 tx3 = get_float3(t->uv4());
-                                       float3 v3 = mesh->verts[vi[3]];
-                                       float3 tangent1 = tangent_from_triangle(v0, v2, v3, tx0, tx2, tx3);
-
-                                       tangents[vi[0]] += 0.5f*(tangent0 + tangent1);
-                                       tangents[vi[1]] += tangent0;
-                                       tangents[vi[2]] += 0.5f*(tangent0 + tangent1);
-                                       tangents[vi[3]] += tangent1;
-                               }
-                               else {
-                                       /* triangle tangent */
-                                       tangents[vi[0]] += tangent0;
-                                       tangents[vi[1]] += tangent0;
-                                       tangents[vi[2]] += tangent0;
-                               }
+                               mikk_compute_tangents(b_mesh, *l, mesh, nverts, need_sign, active_render);
                        }
-
-                       /* normalize tangent vectors */
-                       for(int i = 0; i < mesh->verts.size(); i++)
-                               tangents[i] = normalize(tangents[i]);
                }
        }
 
-       /* create generated coordinates. todo: we should actually get the orco
-        * coordinates from modifiers, for now we use texspace loc/size which
-        * is available in the api. */
+       /* create generated coordinates from undeformed coordinates */
        if(mesh->need_attribute(scene, ATTR_STD_GENERATED)) {
                Attribute *attr = mesh->attributes.add(ATTR_STD_GENERATED);
-               float3 loc = get_float3(b_mesh.texspace_location());
-               float3 size = get_float3(b_mesh.texspace_size());
-
-               if(size.x != 0.0f) size.x = 0.5f/size.x;
-               if(size.y != 0.0f) size.y = 0.5f/size.y;
-               if(size.z != 0.0f) size.z = 0.5f/size.z;
 
-               loc = loc*size - make_float3(0.5f, 0.5f, 0.5f);
+               float3 loc, size;
+               mesh_texture_space(b_mesh, loc, size);
 
                float3 *generated = attr->data_float3();
                size_t i = 0;
 
                for(b_mesh.vertices.begin(v); v != b_mesh.vertices.end(); ++v)
-                       generated[i++] = get_float3(v->co())*size - loc;
+                       generated[i++] = get_float3(v->undeformed_co())*size - loc;
+       }
+
+       /* for volume objects, create a matrix to transform from object space to
+        * mesh texture space. this does not work with deformations but that can
+        * probably only be done well with a volume grid mapping of coordinates */
+       if(mesh->need_attribute(scene, ATTR_STD_GENERATED_TRANSFORM)) {
+               Attribute *attr = mesh->attributes.add(ATTR_STD_GENERATED_TRANSFORM);
+               Transform *tfm = attr->data_transform();
+
+               float3 loc, size;
+               mesh_texture_space(b_mesh, loc, size);
+
+               *tfm = transform_translate(-loc)*transform_scale(size);
        }
 }
 
-static void create_subd_mesh(Mesh *mesh, BL::Mesh b_mesh, PointerRNA *cmesh, const vector<uint>& used_shaders)
+static void create_subd_mesh(Scene *scene, Mesh *mesh, BL::Mesh b_mesh, PointerRNA *cmesh, const vector<uint>& used_shaders)
 {
        /* create subd mesh */
        SubdMesh sdmesh;
@@ -261,26 +431,30 @@ static void create_subd_mesh(Mesh *mesh, BL::Mesh b_mesh, PointerRNA *cmesh, con
 
                if(n == 4)
                        sdmesh.add_face(vi[0], vi[1], vi[2], vi[3]);
-#if 0
                else
                        sdmesh.add_face(vi[0], vi[1], vi[2]);
-#endif
        }
 
        /* finalize subd mesh */
-       sdmesh.link_boundary();
+       sdmesh.finish();
+
+       /* parameters */
+       bool need_ptex = mesh->need_attribute(scene, ATTR_STD_PTEX_FACE_ID) ||
+                        mesh->need_attribute(scene, ATTR_STD_PTEX_UV);
 
-       /* subdivide */
-       DiagSplit dsplit;
-       dsplit.camera = NULL;
-       dsplit.dicing_rate = RNA_float_get(cmesh, "dicing_rate");
+       SubdParams sdparams(mesh, used_shaders[0], true, need_ptex);
+       sdparams.dicing_rate = RNA_float_get(cmesh, "dicing_rate");
+       //scene->camera->update();
+       //sdparams.camera = scene->camera;
 
-       sdmesh.tessellate(&dsplit, false, mesh, used_shaders[0], true);
+       /* tesselate */
+       DiagSplit dsplit(sdparams);
+       sdmesh.tessellate(&dsplit);
 }
 
 /* Sync */
 
-Mesh *BlenderSync::sync_mesh(BL::Object b_ob, bool object_updated)
+Mesh *BlenderSync::sync_mesh(BL::Object b_ob, bool object_updated, bool hide_tris)
 {
        /* test if we can instance or if the object is modified */
        BL::ID b_ob_data = b_ob.data();
@@ -309,6 +483,7 @@ Mesh *BlenderSync::sync_mesh(BL::Object b_ob, bool object_updated)
        Mesh *mesh;
 
        if(!mesh_map.sync(&mesh, key)) {
+               
                /* if transform was applied to mesh, need full update */
                if(object_updated && mesh->transform_applied);
                /* test if shaders changed, these can be object level so mesh
@@ -335,28 +510,46 @@ Mesh *BlenderSync::sync_mesh(BL::Object b_ob, bool object_updated)
        mesh_synced.insert(mesh);
 
        /* create derived mesh */
-       BL::Mesh b_mesh = object_to_mesh(b_ob, b_scene, true, !preview);
        PointerRNA cmesh = RNA_pointer_get(&b_ob_data.ptr, "cycles");
 
        vector<Mesh::Triangle> oldtriangle = mesh->triangles;
+       
+       /* compares curve_keys rather than strands in order to handle quick hair
+        * adjustsments in dynamic BVH - other methods could probably do this better*/
+       vector<float4> oldcurve_keys = mesh->curve_keys;
 
        mesh->clear();
        mesh->used_shaders = used_shaders;
        mesh->name = ustring(b_ob_data.name().c_str());
 
-       if(b_mesh) {
-               if(cmesh.data && experimental && RNA_boolean_get(&cmesh, "use_subdivision"))
-                       create_subd_mesh(mesh, b_mesh, &cmesh, used_shaders);
-               else
-                       create_mesh(scene, mesh, b_mesh, used_shaders);
+       if(render_layer.use_surfaces || render_layer.use_hair) {
+               if(preview)
+                       b_ob.update_from_editmode();
 
-               /* free derived mesh */
-               object_remove_mesh(b_data, b_mesh);
+               bool need_undeformed = mesh->need_attribute(scene, ATTR_STD_GENERATED);
+               BL::Mesh b_mesh = object_to_mesh(b_data, b_ob, b_scene, true, !preview, need_undeformed);
+
+               if(b_mesh) {
+                       if(render_layer.use_surfaces && !hide_tris) {
+                               if(cmesh.data && experimental && RNA_boolean_get(&cmesh, "use_subdivision"))
+                                       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)
+                               sync_curves(mesh, b_mesh, b_ob, false);
+
+                       /* free derived mesh */
+                       b_data.meshes.remove(b_mesh);
+               }
        }
 
        /* displacement method */
        if(cmesh.data) {
-               int method = RNA_enum_get(&cmesh, "displacement_method");
+               const int method = RNA_enum_get(&cmesh, "displacement_method");
 
                if(method == 0 || !experimental)
                        mesh->displacement_method = Mesh::DISPLACE_BUMP;
@@ -375,42 +568,132 @@ Mesh *BlenderSync::sync_mesh(BL::Object b_ob, bool object_updated)
                if(memcmp(&oldtriangle[0], &mesh->triangles[0], sizeof(Mesh::Triangle)*oldtriangle.size()) != 0)
                        rebuild = true;
        }
+
+       if(oldcurve_keys.size() != mesh->curve_keys.size())
+               rebuild = true;
+       else if(oldcurve_keys.size()) {
+               if(memcmp(&oldcurve_keys[0], &mesh->curve_keys[0], sizeof(float4)*oldcurve_keys.size()) != 0)
+                       rebuild = true;
+       }
        
        mesh->tag_update(scene, rebuild);
 
        return mesh;
 }
 
-void BlenderSync::sync_mesh_motion(BL::Object b_ob, Mesh *mesh, int motion)
+void BlenderSync::sync_mesh_motion(BL::Object b_ob, Object *object, float motion_time)
 {
-       /* todo: displacement, subdivision */
-       size_t size = mesh->verts.size();
+       /* ensure we only sync instanced meshes once */
+       Mesh *mesh = object->mesh;
 
-       /* skip objects without deforming modifiers. this is not a totally reliable,
-        * would need a more extensive check to see which objects are animated */
-       if(!size || !ccl::BKE_object_is_deform_modified(b_ob, b_scene, preview))
+       if(mesh_motion_synced.find(mesh) != mesh_motion_synced.end())
                return;
 
+       mesh_motion_synced.insert(mesh);
+
+       /* for motion pass always compute, for motion blur it can be disabled */
+       int time_index = 0;
+
+       if(scene->need_motion() == Scene::MOTION_BLUR) {
+               if(!mesh->use_motion_blur)
+                       return;
+               
+               /* see if this mesh needs motion data at this time */
+               vector<float> object_times = object->motion_times();
+               bool found = false;
+
+               foreach(float object_time, object_times) {
+                       if(motion_time == object_time) {
+                               found = true;
+                               break;
+                       }
+                       else
+                               time_index++;
+               }
+
+               if(!found)
+                       return;
+       }
+       else {
+               if(motion_time == -1.0f)
+                       time_index = 0;
+               else if(motion_time == 1.0f)
+                       time_index = 1;
+               else
+                       return;
+       }
+
+       /* skip objects without deforming modifiers. this is not totally reliable,
+        * would need a more extensive check to see which objects are animated */
+       size_t numverts = mesh->verts.size();
+       size_t numkeys = mesh->curve_keys.size();
+
+       if((!numverts && !numkeys) || !ccl::BKE_object_is_deform_modified(b_ob, b_scene, preview))
+               return;
+       
        /* get derived mesh */
-       BL::Mesh b_mesh = object_to_mesh(b_ob, b_scene, true, !preview);
+       BL::Mesh b_mesh = object_to_mesh(b_data, b_ob, b_scene, true, !preview, false);
 
-       if(b_mesh) {
-               BL::Mesh::vertices_iterator v;
-               AttributeStandard std = (motion == -1)? ATTR_STD_MOTION_PRE: ATTR_STD_MOTION_POST;
-               Attribute *attr_M = mesh->attributes.add(std);
-               float3 *M = attr_M->data_float3(), *cur_M;
-               size_t i = 0;
+       if(!b_mesh)
+               return;
+       
+       if(numverts) {
+               /* find attributes */
+               Attribute *attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
+               Attribute *attr_mN = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_NORMAL);
+               Attribute *attr_N = mesh->attributes.find(ATTR_STD_VERTEX_NORMAL);
+               bool new_attribute = false;
+
+               /* add new attributes if they don't exist already */
+               if(!attr_mP) {
+                       attr_mP = mesh->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION);
+                       if(attr_N)
+                               attr_mN = mesh->attributes.add(ATTR_STD_MOTION_VERTEX_NORMAL);
+
+                       new_attribute = true;
+               }
 
-               for(b_mesh.vertices.begin(v), cur_M = M; v != b_mesh.vertices.end() && i < size; ++v, cur_M++, i++)
-                       *cur_M = get_float3(v->co());
+               /* load vertex data from mesh */
+               float3 *mP = attr_mP->data_float3() + time_index*numverts;
+               float3 *mN = (attr_mN)? attr_mN->data_float3() + time_index*numverts: NULL;
 
-               /* if number of vertices changed, or if coordinates stayed the same, drop it */
-               if(i != size || memcmp(M, &mesh->verts[0], sizeof(float3)*size) == 0)
-                       mesh->attributes.remove(std);
+               BL::Mesh::vertices_iterator v;
+               int i = 0;
 
-               /* free derived mesh */
-               object_remove_mesh(b_data, b_mesh);
+               for(b_mesh.vertices.begin(v); v != b_mesh.vertices.end() && i < numverts; ++v, ++i) {
+                       mP[i] = get_float3(v->co());
+                       if(mN)
+                               mN[i] = get_float3(v->normal());
+               }
+
+               /* in case of new attribute, we verify if there really was any motion */
+               if(new_attribute) {
+                       if(i != numverts || memcmp(mP, &mesh->verts[0], sizeof(float3)*numverts) == 0) {
+                               /* no motion, remove attributes again */
+                               mesh->attributes.remove(ATTR_STD_MOTION_VERTEX_POSITION);
+                               if(attr_mN)
+                                       mesh->attributes.remove(ATTR_STD_MOTION_VERTEX_NORMAL);
+                       }
+                       else if(time_index > 0) {
+                               /* motion, fill up previous steps that we might have skipped because
+                                * they had no motion, but we need them anyway now */
+                               float3 *P = &mesh->verts[0];
+                               float3 *N = (attr_N)? attr_N->data_float3(): NULL;
+
+                               for(int step = 0; step < time_index; step++) {
+                                       memcpy(attr_mP->data_float3() + step*numverts, P, sizeof(float3)*numverts);
+                                       memcpy(attr_mN->data_float3() + step*numverts, N, sizeof(float3)*numverts);
+                               }
+                       }
+               }
        }
+
+       /* hair motion */
+       if(numkeys)
+               sync_curves(mesh, b_mesh, b_ob, true, time_index);
+
+       /* free derived mesh */
+       b_data.meshes.remove(b_mesh);
 }
 
 CCL_NAMESPACE_END