Mesh *BlenderSync::sync_mesh(BL::Depsgraph &b_depsgraph, BL::Object &b_ob, BL::Object &b_ob_instance, bool object_updated, bool show_self, bool show_particles) { /* test if we can instance or if the object is modified */ BL::ID b_ob_data = b_ob.data(); BL::ID key = (BKE_object_is_modified(b_ob)) ? b_ob_instance : b_ob_data; BL::Material material_override = view_layer.material_override; /* find shader indices */ vector<Shader *> used_shaders; BL::Object::material_slots_iterator slot; for (b_ob.material_slots.begin(slot); slot != b_ob.material_slots.end(); ++slot) { if (material_override) { find_shader(material_override, used_shaders, scene->default_surface); } else { BL::ID b_material(slot->material()); find_shader(b_material, used_shaders, scene->default_surface); } } if (used_shaders.size() == 0) { if (material_override) find_shader(material_override, used_shaders, scene->default_surface); else used_shaders.push_back(scene->default_surface); } /* test if we need to sync */ int requested_geometry_flags = Mesh::GEOMETRY_NONE; if (view_layer.use_surfaces) { requested_geometry_flags |= Mesh::GEOMETRY_TRIANGLES; } if (view_layer.use_hair) { requested_geometry_flags |= Mesh::GEOMETRY_CURVES; } 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 * does not get tagged for recalc */ else if (mesh->used_shaders != used_shaders) ; else if (requested_geometry_flags != mesh->geometry_flags) ; else { /* even if not tagged for recalc, we may need to sync anyway * because the shader needs different mesh attributes */ bool attribute_recalc = false; foreach (Shader *shader, mesh->used_shaders) if (shader->need_update_mesh) attribute_recalc = true; if (!attribute_recalc) return mesh; } } /* ensure we only sync instanced meshes once */ if (mesh_synced.find(mesh) != mesh_synced.end()) return mesh; progress.set_sync_status("Synchronizing object", b_ob.name()); mesh_synced.insert(mesh); /* create derived mesh */ array<int> oldtriangles; array<Mesh::SubdFace> oldsubd_faces; array<int> oldsubd_face_corners; oldtriangles.steal_data(mesh->triangles); oldsubd_faces.steal_data(mesh->subd_faces); oldsubd_face_corners.steal_data(mesh->subd_face_corners); /* compares curve_keys rather than strands in order to handle quick hair * adjustments in dynamic BVH - other methods could probably do this better*/ array<float3> oldcurve_keys; array<float> oldcurve_radius; oldcurve_keys.steal_data(mesh->curve_keys); oldcurve_radius.steal_data(mesh->curve_radius); mesh->clear(); mesh->used_shaders = used_shaders; mesh->name = ustring(b_ob_data.name().c_str()); if (requested_geometry_flags != Mesh::GEOMETRY_NONE) { /* Adaptive subdivision setup. Not for baking since that requires * exact mapping to the Blender mesh. */ if (scene->bake_manager->get_baking()) { mesh->subdivision_type = Mesh::SUBDIVISION_NONE; } else { mesh->subdivision_type = object_subdivision_type(b_ob, preview, experimental); } /* For some reason, meshes do not need this... */ bool need_undeformed = mesh->need_attribute(scene, ATTR_STD_GENERATED); BL::Mesh b_mesh = object_to_mesh( b_data, b_ob, b_depsgraph, need_undeformed, mesh->subdivision_type); if (b_mesh) { /* Sync mesh itself. */ if (view_layer.use_surfaces && show_self) { if (mesh->subdivision_type != Mesh::SUBDIVISION_NONE) create_subd_mesh(scene, mesh, b_ob, b_mesh, used_shaders, dicing_rate, max_subdivisions); else create_mesh(scene, mesh, b_mesh, used_shaders, false); create_mesh_volume_attributes(scene, b_ob, mesh, b_scene.frame_current()); } /* Sync hair curves. */ if (view_layer.use_hair && show_particles && mesh->subdivision_type == Mesh::SUBDIVISION_NONE) { sync_curves(mesh, b_mesh, b_ob, false); } free_object_to_mesh(b_data, b_ob, b_mesh); } } mesh->geometry_flags = requested_geometry_flags; /* fluid motion */ sync_mesh_fluid_motion(b_ob, scene, mesh); /* tag update */ bool rebuild = (oldtriangles != mesh->triangles) || (oldsubd_faces != mesh->subd_faces) || (oldsubd_face_corners != mesh->subd_face_corners) || (oldcurve_keys != mesh->curve_keys) || (oldcurve_radius != mesh->curve_radius); mesh->tag_update(scene, rebuild); return mesh; }
Mesh *BlenderSync::sync_mesh(BL::Object& b_ob, bool object_updated, bool hide_tris) { /* When viewport display is not needed during render we can force some * caches to be releases from blender side in order to reduce peak memory * footprint during synchronization process. */ const bool is_interface_locked = b_engine.render() && b_engine.render().use_lock_interface(); const bool can_free_caches = BlenderSession::headless || is_interface_locked; /* test if we can instance or if the object is modified */ BL::ID b_ob_data = b_ob.data(); BL::ID key = (BKE_object_is_modified(b_ob))? b_ob: b_ob_data; BL::Material material_override = render_layer.material_override; /* find shader indices */ vector<uint> used_shaders; BL::Object::material_slots_iterator slot; for(b_ob.material_slots.begin(slot); slot != b_ob.material_slots.end(); ++slot) { if(material_override) { find_shader(material_override, used_shaders, scene->default_surface); } else { BL::ID b_material(slot->material()); find_shader(b_material, used_shaders, scene->default_surface); } } if(used_shaders.size() == 0) { if(material_override) find_shader(material_override, used_shaders, scene->default_surface); else used_shaders.push_back(scene->default_surface); } /* test if we need to sync */ int requested_geometry_flags = Mesh::GEOMETRY_NONE; if(render_layer.use_surfaces) { requested_geometry_flags |= Mesh::GEOMETRY_TRIANGLES; } if(render_layer.use_hair) { requested_geometry_flags |= Mesh::GEOMETRY_CURVES; } 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 * does not get tagged for recalc */ else if(mesh->used_shaders != used_shaders); else if(requested_geometry_flags != mesh->geometry_flags); else { /* even if not tagged for recalc, we may need to sync anyway * because the shader needs different mesh attributes */ bool attribute_recalc = false; foreach(uint shader, mesh->used_shaders) if(scene->shaders[shader]->need_update_attributes) attribute_recalc = true; if(!attribute_recalc) return mesh; } } /* ensure we only sync instanced meshes once */ if(mesh_synced.find(mesh) != mesh_synced.end()) return mesh; mesh_synced.insert(mesh); /* create derived mesh */ 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 * adjustments 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(requested_geometry_flags != Mesh::GEOMETRY_NONE) { /* mesh objects does have special handle in the dependency graph, * they're ensured to have properly updated. * * updating meshes here will end up having derived mesh referencing * freed data from the blender side. */ if(preview && b_ob.type() != BL::Object::type_MESH) b_ob.update_from_editmode(); 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, b_scene.frame_current()); } if(render_layer.use_hair) sync_curves(mesh, b_mesh, b_ob, false); if(can_free_caches) { b_ob.cache_release(); } /* free derived mesh */ b_data.meshes.remove(b_mesh); } } mesh->geometry_flags = requested_geometry_flags; /* displacement method */ if(cmesh.data) { const int method = get_enum(cmesh, "displacement_method"); if(method == 0 || !experimental) mesh->displacement_method = Mesh::DISPLACE_BUMP; else if(method == 1) mesh->displacement_method = Mesh::DISPLACE_TRUE; else mesh->displacement_method = Mesh::DISPLACE_BOTH; } /* tag update */ bool rebuild = false; if(oldtriangle.size() != mesh->triangles.size()) rebuild = true; else if(oldtriangle.size()) { 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::Depsgraph &b_depsgraph, BL::Object &b_ob, Object *object, float motion_time) { /* ensure we only sync instanced meshes once */ Mesh *mesh = object->mesh; if (mesh_motion_synced.find(mesh) != mesh_motion_synced.end()) return; mesh_motion_synced.insert(mesh); /* ensure we only motion sync meshes that also had mesh synced, to avoid * unnecessary work and to ensure that its attributes were clear */ if (mesh_synced.find(mesh) == mesh_synced.end()) return; /* Find time matching motion step required by mesh. */ int motion_step = mesh->motion_step(motion_time); if (motion_step < 0) { return; } /* skip empty meshes */ const size_t numverts = mesh->verts.size(); const size_t numkeys = mesh->curve_keys.size(); if (!numverts && !numkeys) return; /* skip objects without deforming modifiers. this is not totally reliable, * would need a more extensive check to see which objects are animated */ BL::Mesh b_mesh(PointerRNA_NULL); /* fluid motion is exported immediate with mesh, skip here */ BL::DomainFluidSettings b_fluid_domain = object_fluid_domain_find(b_ob); if (b_fluid_domain) return; if (ccl::BKE_object_is_deform_modified(b_ob, b_scene, preview)) { /* get derived mesh */ b_mesh = object_to_mesh(b_data, b_ob, b_depsgraph, false, Mesh::SUBDIVISION_NONE); } if (!b_mesh) { /* if we have no motion blur on this frame, but on other frames, copy */ if (numverts) { /* triangles */ Attribute *attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); if (attr_mP) { Attribute *attr_mN = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_NORMAL); Attribute *attr_N = mesh->attributes.find(ATTR_STD_VERTEX_NORMAL); float3 *P = &mesh->verts[0]; float3 *N = (attr_N) ? attr_N->data_float3() : NULL; memcpy(attr_mP->data_float3() + motion_step * numverts, P, sizeof(float3) * numverts); if (attr_mN) memcpy(attr_mN->data_float3() + motion_step * numverts, N, sizeof(float3) * numverts); } } if (numkeys) { /* curves */ Attribute *attr_mP = mesh->curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); if (attr_mP) { float3 *keys = &mesh->curve_keys[0]; memcpy(attr_mP->data_float3() + motion_step * numkeys, keys, sizeof(float3) * numkeys); } } return; } /* TODO(sergey): Perform preliminary check for number of verticies. */ 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; } /* Load vertex data from mesh. */ float3 *mP = attr_mP->data_float3() + motion_step * numverts; float3 *mN = (attr_mN) ? attr_mN->data_float3() + motion_step * numverts : NULL; /* NOTE: We don't copy more that existing amount of vertices to prevent * possible memory corruption. */ BL::Mesh::vertices_iterator v; int i = 0; 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()); } if (new_attribute) { /* In case of new attribute, we verify if there really was any motion. */ if (b_mesh.vertices.length() != numverts || memcmp(mP, &mesh->verts[0], sizeof(float3) * numverts) == 0) { /* no motion, remove attributes again */ if (b_mesh.vertices.length() != numverts) { VLOG(1) << "Topology differs, disabling motion blur for object " << b_ob.name(); } else { VLOG(1) << "No actual deformation motion for object " << b_ob.name(); } mesh->attributes.remove(ATTR_STD_MOTION_VERTEX_POSITION); if (attr_mN) mesh->attributes.remove(ATTR_STD_MOTION_VERTEX_NORMAL); } else if (motion_step > 0) { VLOG(1) << "Filling deformation motion for object " << b_ob.name(); /* 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 < motion_step; step++) { memcpy(attr_mP->data_float3() + step * numverts, P, sizeof(float3) * numverts); if (attr_mN) memcpy(attr_mN->data_float3() + step * numverts, N, sizeof(float3) * numverts); } } } else { if (b_mesh.vertices.length() != numverts) { VLOG(1) << "Topology differs, discarding motion blur for object " << b_ob.name() << " at time " << motion_step; memcpy(mP, &mesh->verts[0], sizeof(float3) * numverts); if (mN != NULL) { memcpy(mN, attr_N->data_float3(), sizeof(float3) * numverts); } } } } /* hair motion */ if (numkeys) sync_curves(mesh, b_mesh, b_ob, true, motion_step); /* free derived mesh */ free_object_to_mesh(b_data, b_ob, b_mesh); }
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(); BL::ID key = (BKE_object_is_modified(b_ob))? b_ob: b_ob_data; BL::Material material_override = render_layer.material_override; /* find shader indices */ vector<uint> used_shaders; BL::Object::material_slots_iterator slot; for(b_ob.material_slots.begin(slot); slot != b_ob.material_slots.end(); ++slot) { if(material_override) find_shader(material_override, used_shaders, scene->default_surface); else find_shader(slot->material(), used_shaders, scene->default_surface); } if(used_shaders.size() == 0) { if(material_override) find_shader(material_override, used_shaders, scene->default_surface); else used_shaders.push_back(scene->default_surface); } /* test if we need to sync */ 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 * does not get tagged for recalc */ else if(mesh->used_shaders != used_shaders); else { /* even if not tagged for recalc, we may need to sync anyway * because the shader needs different mesh attributes */ bool attribute_recalc = false; foreach(uint shader, mesh->used_shaders) if(scene->shaders[shader]->need_update_attributes) attribute_recalc = true; if(!attribute_recalc) return mesh; } } /* ensure we only sync instanced meshes once */ if(mesh_synced.find(mesh) != mesh_synced.end()) return mesh; mesh_synced.insert(mesh); /* create derived mesh */ 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(render_layer.use_surfaces || render_layer.use_hair) { if(preview) b_ob.update_from_editmode(); 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) { const int method = RNA_enum_get(&cmesh, "displacement_method"); if(method == 0 || !experimental) mesh->displacement_method = Mesh::DISPLACE_BUMP; else if(method == 1) mesh->displacement_method = Mesh::DISPLACE_TRUE; else mesh->displacement_method = Mesh::DISPLACE_BOTH; } /* tag update */ bool rebuild = false; if(oldtriangle.size() != mesh->triangles.size()) rebuild = true; else if(oldtriangle.size()) { 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; }