Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--intern/cycles/bvh/CMakeLists.txt4
-rw-r--r--intern/cycles/bvh/bvh.cpp867
-rw-r--r--intern/cycles/bvh/bvh.h106
-rw-r--r--intern/cycles/bvh/bvh2.cpp364
-rw-r--r--intern/cycles/bvh/bvh2.h87
-rw-r--r--intern/cycles/bvh/bvh4.cpp516
-rw-r--r--intern/cycles/bvh/bvh4.h87
-rw-r--r--intern/cycles/bvh/bvh_binning.cpp4
-rw-r--r--intern/cycles/bvh/bvh_binning.h3
-rw-r--r--intern/cycles/bvh/bvh_build.cpp3
-rw-r--r--intern/cycles/bvh/bvh_build.h6
-rw-r--r--intern/cycles/bvh/bvh_node.cpp3
-rw-r--r--intern/cycles/bvh/bvh_node.h1
-rw-r--r--intern/cycles/bvh/bvh_params.h1
-rw-r--r--intern/cycles/bvh/bvh_sort.cpp3
-rw-r--r--intern/cycles/bvh/bvh_sort.h4
-rw-r--r--intern/cycles/bvh/bvh_split.cpp3
-rw-r--r--intern/cycles/bvh/bvh_unaligned.cpp1
-rw-r--r--intern/cycles/bvh/bvh_unaligned.h1
-rw-r--r--source/blender/alembic/intern/abc_exporter.cc4
-rw-r--r--source/blender/alembic/intern/abc_mesh.cc38
-rw-r--r--source/blender/alembic/intern/abc_transform.cc14
-rw-r--r--source/blender/alembic/intern/abc_transform.h1
-rw-r--r--source/blender/alembic/intern/abc_util.cc9
-rw-r--r--source/blender/alembic/intern/abc_util.h7
-rw-r--r--source/blender/blenkernel/BKE_idprop.h13
-rw-r--r--source/blender/blenkernel/BKE_library.h2
-rw-r--r--source/blender/blenkernel/BKE_library_query.h2
-rw-r--r--source/blender/blenkernel/intern/idprop.c117
-rw-r--r--source/blender/blenkernel/intern/library_query.c119
-rw-r--r--source/blender/blenkernel/intern/library_remap.c30
-rw-r--r--source/blender/blenkernel/intern/node.c2
-rw-r--r--source/blender/blenlib/intern/task.c39
-rw-r--r--source/blender/blenloader/intern/readfile.c219
-rw-r--r--source/blender/editors/include/UI_interface.h2
-rw-r--r--source/blender/editors/interface/interface.c2
-rw-r--r--source/blender/editors/interface/interface_intern.h2
-rw-r--r--source/blender/editors/interface/interface_regions.c17
-rw-r--r--source/blender/editors/interface/interface_templates.c5
-rw-r--r--source/blender/editors/mesh/meshtools.c4
-rw-r--r--source/blender/editors/object/object_relations.c29
-rw-r--r--source/blender/makesdna/DNA_ID.h2
-rw-r--r--source/blender/makesrna/RNA_access.h2
-rw-r--r--source/blender/makesrna/RNA_define.h1
-rw-r--r--source/blender/makesrna/RNA_types.h2
-rw-r--r--source/blender/makesrna/intern/rna_ID.c6
-rw-r--r--source/blender/makesrna/intern/rna_access.c76
-rw-r--r--source/blender/makesrna/intern/rna_define.c13
-rw-r--r--source/blender/makesrna/intern/rna_internal.h1
-rw-r--r--source/blender/makesrna/intern/rna_internal_types.h1
-rw-r--r--source/blender/makesrna/intern/rna_ui.c1
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c1
-rw-r--r--source/blender/makesrna/intern/rna_wm.c1
-rw-r--r--source/blender/python/generic/idprop_py_api.c21
-rw-r--r--source/blender/python/intern/bpy_props.c133
-rw-r--r--source/blender/python/intern/bpy_props.h4
-rw-r--r--source/blender/python/intern/bpy_rna.c44
-rw-r--r--source/blender/python/intern/bpy_rna.h1
-rw-r--r--source/blender/windowmanager/intern/wm.c2
-rw-r--r--tests/python/CMakeLists.txt4
-rw-r--r--tests/python/bl_pyapi_idprop_datablock.py338
61 files changed, 2181 insertions, 1214 deletions
diff --git a/intern/cycles/bvh/CMakeLists.txt b/intern/cycles/bvh/CMakeLists.txt
index 4701d75350a..6078db5a8ca 100644
--- a/intern/cycles/bvh/CMakeLists.txt
+++ b/intern/cycles/bvh/CMakeLists.txt
@@ -8,6 +8,8 @@ set(INC_SYS
set(SRC
bvh.cpp
+ bvh2.cpp
+ bvh4.cpp
bvh_binning.cpp
bvh_build.cpp
bvh_node.cpp
@@ -18,6 +20,8 @@ set(SRC
set(SRC_HEADERS
bvh.h
+ bvh2.h
+ bvh4.h
bvh_binning.h
bvh_build.h
bvh_node.h
diff --git a/intern/cycles/bvh/bvh.cpp b/intern/cycles/bvh/bvh.cpp
index 58348d16746..33143e2d8aa 100644
--- a/intern/cycles/bvh/bvh.cpp
+++ b/intern/cycles/bvh/bvh.cpp
@@ -15,45 +15,32 @@
* limitations under the License.
*/
+#include "bvh/bvh.h"
+
#include "render/mesh.h"
#include "render/object.h"
-#include "render/scene.h"
-#include "render/curves.h"
-#include "bvh/bvh.h"
+#include "bvh/bvh2.h"
+#include "bvh/bvh4.h"
#include "bvh/bvh_build.h"
#include "bvh/bvh_node.h"
-#include "bvh/bvh_params.h"
-#include "bvh/bvh_unaligned.h"
-#include "util/util_debug.h"
#include "util/util_foreach.h"
-#include "util/util_logging.h"
-#include "util/util_map.h"
#include "util/util_progress.h"
-#include "util/util_system.h"
-#include "util/util_types.h"
-#include "util/util_math.h"
CCL_NAMESPACE_BEGIN
/* Pack Utility */
-struct BVHStackEntry
+BVHStackEntry::BVHStackEntry(const BVHNode *n, int i)
+ : node(n), idx(i)
{
- const BVHNode *node;
- int idx;
-
- BVHStackEntry(const BVHNode* n = 0, int i = 0)
- : node(n), idx(i)
- {
- }
+}
- int encodeIdx() const
- {
- return (node->is_leaf())? ~idx: idx;
- }
-};
+int BVHStackEntry::encodeIdx() const
+{
+ return (node->is_leaf())? ~idx: idx;
+}
/* BVH */
@@ -65,9 +52,9 @@ BVH::BVH(const BVHParams& params_, const vector<Object*>& objects_)
BVH *BVH::create(const BVHParams& params, const vector<Object*>& objects)
{
if(params.use_qbvh)
- return new QBVH(params, objects);
+ return new BVH4(params, objects);
else
- return new BinaryBVH(params, objects);
+ return new BVH2(params, objects);
}
/* Building */
@@ -418,832 +405,4 @@ void BVH::pack_instances(size_t nodes_size, size_t leaf_nodes_size)
}
}
-/* Regular BVH */
-
-static bool node_bvh_is_unaligned(const BVHNode *node)
-{
- const BVHNode *node0 = node->get_child(0),
- *node1 = node->get_child(1);
- return node0->is_unaligned || node1->is_unaligned;
-}
-
-BinaryBVH::BinaryBVH(const BVHParams& params_, const vector<Object*>& objects_)
-: BVH(params_, objects_)
-{
-}
-
-void BinaryBVH::pack_leaf(const BVHStackEntry& e,
- const LeafNode *leaf)
-{
- assert(e.idx + BVH_NODE_LEAF_SIZE <= pack.leaf_nodes.size());
- float4 data[BVH_NODE_LEAF_SIZE];
- memset(data, 0, sizeof(data));
- if(leaf->num_triangles() == 1 && pack.prim_index[leaf->lo] == -1) {
- /* object */
- data[0].x = __int_as_float(~(leaf->lo));
- data[0].y = __int_as_float(0);
- }
- else {
- /* triangle */
- data[0].x = __int_as_float(leaf->lo);
- data[0].y = __int_as_float(leaf->hi);
- }
- data[0].z = __uint_as_float(leaf->visibility);
- if(leaf->num_triangles() != 0) {
- data[0].w = __uint_as_float(pack.prim_type[leaf->lo]);
- }
-
- memcpy(&pack.leaf_nodes[e.idx], data, sizeof(float4)*BVH_NODE_LEAF_SIZE);
-}
-
-void BinaryBVH::pack_inner(const BVHStackEntry& e,
- const BVHStackEntry& e0,
- const BVHStackEntry& e1)
-{
- if(e0.node->is_unaligned || e1.node->is_unaligned) {
- pack_unaligned_inner(e, e0, e1);
- } else {
- pack_aligned_inner(e, e0, e1);
- }
-}
-
-void BinaryBVH::pack_aligned_inner(const BVHStackEntry& e,
- const BVHStackEntry& e0,
- const BVHStackEntry& e1)
-{
- pack_aligned_node(e.idx,
- e0.node->bounds, e1.node->bounds,
- e0.encodeIdx(), e1.encodeIdx(),
- e0.node->visibility, e1.node->visibility);
-}
-
-void BinaryBVH::pack_aligned_node(int idx,
- const BoundBox& b0,
- const BoundBox& b1,
- int c0, int c1,
- uint visibility0, uint visibility1)
-{
- assert(idx + BVH_NODE_SIZE <= pack.nodes.size());
- assert(c0 < 0 || c0 < pack.nodes.size());
- assert(c1 < 0 || c1 < pack.nodes.size());
-
- int4 data[BVH_NODE_SIZE] = {
- make_int4(visibility0 & ~PATH_RAY_NODE_UNALIGNED,
- visibility1 & ~PATH_RAY_NODE_UNALIGNED,
- c0, c1),
- make_int4(__float_as_int(b0.min.x),
- __float_as_int(b1.min.x),
- __float_as_int(b0.max.x),
- __float_as_int(b1.max.x)),
- make_int4(__float_as_int(b0.min.y),
- __float_as_int(b1.min.y),
- __float_as_int(b0.max.y),
- __float_as_int(b1.max.y)),
- make_int4(__float_as_int(b0.min.z),
- __float_as_int(b1.min.z),
- __float_as_int(b0.max.z),
- __float_as_int(b1.max.z)),
- };
-
- memcpy(&pack.nodes[idx], data, sizeof(int4)*BVH_NODE_SIZE);
-}
-
-void BinaryBVH::pack_unaligned_inner(const BVHStackEntry& e,
- const BVHStackEntry& e0,
- const BVHStackEntry& e1)
-{
- pack_unaligned_node(e.idx,
- e0.node->get_aligned_space(),
- e1.node->get_aligned_space(),
- e0.node->bounds,
- e1.node->bounds,
- e0.encodeIdx(), e1.encodeIdx(),
- e0.node->visibility, e1.node->visibility);
-}
-
-void BinaryBVH::pack_unaligned_node(int idx,
- const Transform& aligned_space0,
- const Transform& aligned_space1,
- const BoundBox& bounds0,
- const BoundBox& bounds1,
- int c0, int c1,
- uint visibility0, uint visibility1)
-{
- assert(idx + BVH_UNALIGNED_NODE_SIZE <= pack.nodes.size());
- assert(c0 < 0 || c0 < pack.nodes.size());
- assert(c1 < 0 || c1 < pack.nodes.size());
-
- float4 data[BVH_UNALIGNED_NODE_SIZE];
- Transform space0 = BVHUnaligned::compute_node_transform(bounds0,
- aligned_space0);
- Transform space1 = BVHUnaligned::compute_node_transform(bounds1,
- aligned_space1);
- data[0] = make_float4(__int_as_float(visibility0 | PATH_RAY_NODE_UNALIGNED),
- __int_as_float(visibility1 | PATH_RAY_NODE_UNALIGNED),
- __int_as_float(c0),
- __int_as_float(c1));
-
- data[1] = space0.x;
- data[2] = space0.y;
- data[3] = space0.z;
- data[4] = space1.x;
- data[5] = space1.y;
- data[6] = space1.z;
-
- memcpy(&pack.nodes[idx], data, sizeof(float4)*BVH_UNALIGNED_NODE_SIZE);
-}
-
-void BinaryBVH::pack_nodes(const BVHNode *root)
-{
- const size_t num_nodes = root->getSubtreeSize(BVH_STAT_NODE_COUNT);
- const size_t num_leaf_nodes = root->getSubtreeSize(BVH_STAT_LEAF_COUNT);
- assert(num_leaf_nodes <= num_nodes);
- const size_t num_inner_nodes = num_nodes - num_leaf_nodes;
- size_t node_size;
- if(params.use_unaligned_nodes) {
- const size_t num_unaligned_nodes =
- root->getSubtreeSize(BVH_STAT_UNALIGNED_INNER_COUNT);
- node_size = (num_unaligned_nodes * BVH_UNALIGNED_NODE_SIZE) +
- (num_inner_nodes - num_unaligned_nodes) * BVH_NODE_SIZE;
- }
- else {
- node_size = num_inner_nodes * BVH_NODE_SIZE;
- }
- /* Resize arrays */
- pack.nodes.clear();
- pack.leaf_nodes.clear();
- /* For top level BVH, first merge existing BVH's so we know the offsets. */
- if(params.top_level) {
- pack_instances(node_size, num_leaf_nodes*BVH_NODE_LEAF_SIZE);
- }
- else {
- pack.nodes.resize(node_size);
- pack.leaf_nodes.resize(num_leaf_nodes*BVH_NODE_LEAF_SIZE);
- }
-
- int nextNodeIdx = 0, nextLeafNodeIdx = 0;
-
- vector<BVHStackEntry> stack;
- stack.reserve(BVHParams::MAX_DEPTH*2);
- if(root->is_leaf()) {
- stack.push_back(BVHStackEntry(root, nextLeafNodeIdx++));
- }
- else {
- stack.push_back(BVHStackEntry(root, nextNodeIdx));
- nextNodeIdx += node_bvh_is_unaligned(root)
- ? BVH_UNALIGNED_NODE_SIZE
- : BVH_NODE_SIZE;
- }
-
- while(stack.size()) {
- BVHStackEntry e = stack.back();
- stack.pop_back();
-
- if(e.node->is_leaf()) {
- /* leaf node */
- const LeafNode *leaf = reinterpret_cast<const LeafNode*>(e.node);
- pack_leaf(e, leaf);
- }
- else {
- /* innner node */
- int idx[2];
- for(int i = 0; i < 2; ++i) {
- if(e.node->get_child(i)->is_leaf()) {
- idx[i] = nextLeafNodeIdx++;
- }
- else {
- idx[i] = nextNodeIdx;
- nextNodeIdx += node_bvh_is_unaligned(e.node->get_child(i))
- ? BVH_UNALIGNED_NODE_SIZE
- : BVH_NODE_SIZE;
- }
- }
-
- stack.push_back(BVHStackEntry(e.node->get_child(0), idx[0]));
- stack.push_back(BVHStackEntry(e.node->get_child(1), idx[1]));
-
- pack_inner(e, stack[stack.size()-2], stack[stack.size()-1]);
- }
- }
- assert(node_size == nextNodeIdx);
- /* root index to start traversal at, to handle case of single leaf node */
- pack.root_index = (root->is_leaf())? -1: 0;
-}
-
-void BinaryBVH::refit_nodes()
-{
- assert(!params.top_level);
-
- BoundBox bbox = BoundBox::empty;
- uint visibility = 0;
- refit_node(0, (pack.root_index == -1)? true: false, bbox, visibility);
-}
-
-void BinaryBVH::refit_node(int idx, bool leaf, BoundBox& bbox, uint& visibility)
-{
- if(leaf) {
- assert(idx + BVH_NODE_LEAF_SIZE <= pack.leaf_nodes.size());
- const int4 *data = &pack.leaf_nodes[idx];
- const int c0 = data[0].x;
- const int c1 = data[0].y;
- /* refit leaf node */
- for(int prim = c0; prim < c1; prim++) {
- int pidx = pack.prim_index[prim];
- int tob = pack.prim_object[prim];
- Object *ob = objects[tob];
-
- if(pidx == -1) {
- /* object instance */
- bbox.grow(ob->bounds);
- }
- else {
- /* primitives */
- const Mesh *mesh = ob->mesh;
-
- if(pack.prim_type[prim] & PRIMITIVE_ALL_CURVE) {
- /* curves */
- int str_offset = (params.top_level)? mesh->curve_offset: 0;
- Mesh::Curve curve = mesh->get_curve(pidx - str_offset);
- int k = PRIMITIVE_UNPACK_SEGMENT(pack.prim_type[prim]);
-
- curve.bounds_grow(k, &mesh->curve_keys[0], &mesh->curve_radius[0], bbox);
-
- visibility |= PATH_RAY_CURVE;
-
- /* motion curves */
- if(mesh->use_motion_blur) {
- Attribute *attr = mesh->curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
-
- if(attr) {
- size_t mesh_size = mesh->curve_keys.size();
- size_t steps = mesh->motion_steps - 1;
- float3 *key_steps = attr->data_float3();
-
- for(size_t i = 0; i < steps; i++)
- curve.bounds_grow(k, key_steps + i*mesh_size, &mesh->curve_radius[0], bbox);
- }
- }
- }
- else {
- /* triangles */
- int tri_offset = (params.top_level)? mesh->tri_offset: 0;
- Mesh::Triangle triangle = mesh->get_triangle(pidx - tri_offset);
- const float3 *vpos = &mesh->verts[0];
-
- triangle.bounds_grow(vpos, bbox);
-
- /* motion triangles */
- if(mesh->use_motion_blur) {
- Attribute *attr = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
-
- if(attr) {
- size_t mesh_size = mesh->verts.size();
- size_t steps = mesh->motion_steps - 1;
- float3 *vert_steps = attr->data_float3();
-
- for(size_t i = 0; i < steps; i++)
- triangle.bounds_grow(vert_steps + i*mesh_size, bbox);
- }
- }
- }
- }
-
- visibility |= ob->visibility;
- }
-
- /* TODO(sergey): De-duplicate with pack_leaf(). */
- float4 leaf_data[BVH_NODE_LEAF_SIZE];
- leaf_data[0].x = __int_as_float(c0);
- leaf_data[0].y = __int_as_float(c1);
- leaf_data[0].z = __uint_as_float(visibility);
- leaf_data[0].w = __uint_as_float(data[0].w);
- memcpy(&pack.leaf_nodes[idx], leaf_data, sizeof(float4)*BVH_NODE_LEAF_SIZE);
- }
- else {
- assert(idx + BVH_NODE_SIZE <= pack.nodes.size());
-
- const int4 *data = &pack.nodes[idx];
- const bool is_unaligned = (data[0].x & PATH_RAY_NODE_UNALIGNED) != 0;
- const int c0 = data[0].z;
- const int c1 = data[0].w;
- /* refit inner node, set bbox from children */
- BoundBox bbox0 = BoundBox::empty, bbox1 = BoundBox::empty;
- uint visibility0 = 0, visibility1 = 0;
-
- refit_node((c0 < 0)? -c0-1: c0, (c0 < 0), bbox0, visibility0);
- refit_node((c1 < 0)? -c1-1: c1, (c1 < 0), bbox1, visibility1);
-
- if(is_unaligned) {
- Transform aligned_space = transform_identity();
- pack_unaligned_node(idx,
- aligned_space, aligned_space,
- bbox0, bbox1,
- c0, c1,
- visibility0,
- visibility1);
- }
- else {
- pack_aligned_node(idx,
- bbox0, bbox1,
- c0, c1,
- visibility0,
- visibility1);
- }
-
- bbox.grow(bbox0);
- bbox.grow(bbox1);
- visibility = visibility0|visibility1;
- }
-}
-
-/* QBVH */
-
-/* Can we avoid this somehow or make more generic?
- *
- * Perhaps we can merge nodes in actual tree and make our
- * life easier all over the place.
- */
-static bool node_qbvh_is_unaligned(const BVHNode *node)
-{
- const BVHNode *node0 = node->get_child(0),
- *node1 = node->get_child(1);
- bool has_unaligned = false;
- if(node0->is_leaf()) {
- has_unaligned |= node0->is_unaligned;
- }
- else {
- has_unaligned |= node0->get_child(0)->is_unaligned;
- has_unaligned |= node0->get_child(1)->is_unaligned;
- }
- if(node1->is_leaf()) {
- has_unaligned |= node1->is_unaligned;
- }
- else {
- has_unaligned |= node1->get_child(0)->is_unaligned;
- has_unaligned |= node1->get_child(1)->is_unaligned;
- }
- return has_unaligned;
-}
-
-QBVH::QBVH(const BVHParams& params_, const vector<Object*>& objects_)
-: BVH(params_, objects_)
-{
- params.use_qbvh = true;
-}
-
-void QBVH::pack_leaf(const BVHStackEntry& e, const LeafNode *leaf)
-{
- float4 data[BVH_QNODE_LEAF_SIZE];
- memset(data, 0, sizeof(data));
- if(leaf->num_triangles() == 1 && pack.prim_index[leaf->lo] == -1) {
- /* object */
- data[0].x = __int_as_float(~(leaf->lo));
- data[0].y = __int_as_float(0);
- }
- else {
- /* triangle */
- data[0].x = __int_as_float(leaf->lo);
- data[0].y = __int_as_float(leaf->hi);
- }
- data[0].z = __uint_as_float(leaf->visibility);
- if(leaf->num_triangles() != 0) {
- data[0].w = __uint_as_float(pack.prim_type[leaf->lo]);
- }
-
- memcpy(&pack.leaf_nodes[e.idx], data, sizeof(float4)*BVH_QNODE_LEAF_SIZE);
-}
-
-void QBVH::pack_inner(const BVHStackEntry& e,
- const BVHStackEntry *en,
- int num)
-{
- bool has_unaligned = false;
- /* Check whether we have to create unaligned node or all nodes are aligned
- * and we can cut some corner here.
- */
- if(params.use_unaligned_nodes) {
- for(int i = 0; i < num; i++) {
- if(en[i].node->is_unaligned) {
- has_unaligned = true;
- break;
- }
- }
- }
- if(has_unaligned) {
- /* There's no unaligned children, pack into AABB node. */
- pack_unaligned_inner(e, en, num);
- }
- else {
- /* Create unaligned node with orientation transform for each of the
- * children.
- */
- pack_aligned_inner(e, en, num);
- }
-}
-
-void QBVH::pack_aligned_inner(const BVHStackEntry& e,
- const BVHStackEntry *en,
- int num)
-{
- BoundBox bounds[4];
- int child[4];
- for(int i = 0; i < num; ++i) {
- bounds[i] = en[i].node->bounds;
- child[i] = en[i].encodeIdx();
- }
- pack_aligned_node(e.idx,
- bounds,
- child,
- e.node->visibility,
- e.node->time_from,
- e.node->time_to,
- num);
-}
-
-void QBVH::pack_aligned_node(int idx,
- const BoundBox *bounds,
- const int *child,
- const uint visibility,
- const float time_from,
- const float time_to,
- const int num)
-{
- float4 data[BVH_QNODE_SIZE];
- memset(data, 0, sizeof(data));
-
- data[0].x = __uint_as_float(visibility & ~PATH_RAY_NODE_UNALIGNED);
- data[0].y = time_from;
- data[0].z = time_to;
-
- for(int i = 0; i < num; i++) {
- float3 bb_min = bounds[i].min;
- float3 bb_max = bounds[i].max;
-
- data[1][i] = bb_min.x;
- data[2][i] = bb_max.x;
- data[3][i] = bb_min.y;
- data[4][i] = bb_max.y;
- data[5][i] = bb_min.z;
- data[6][i] = bb_max.z;
-
- data[7][i] = __int_as_float(child[i]);
- }
-
- for(int i = num; i < 4; i++) {
- /* We store BB which would never be recorded as intersection
- * so kernel might safely assume there are always 4 child nodes.
- */
- data[1][i] = FLT_MAX;
- data[2][i] = -FLT_MAX;
-
- data[3][i] = FLT_MAX;
- data[4][i] = -FLT_MAX;
-
- data[5][i] = FLT_MAX;
- data[6][i] = -FLT_MAX;
-
- data[7][i] = __int_as_float(0);
- }
-
- memcpy(&pack.nodes[idx], data, sizeof(float4)*BVH_QNODE_SIZE);
-}
-
-void QBVH::pack_unaligned_inner(const BVHStackEntry& e,
- const BVHStackEntry *en,
- int num)
-{
- Transform aligned_space[4];
- BoundBox bounds[4];
- int child[4];
- for(int i = 0; i < num; ++i) {
- aligned_space[i] = en[i].node->get_aligned_space();
- bounds[i] = en[i].node->bounds;
- child[i] = en[i].encodeIdx();
- }
- pack_unaligned_node(e.idx,
- aligned_space,
- bounds,
- child,
- e.node->visibility,
- e.node->time_from,
- e.node->time_to,
- num);
-}
-
-void QBVH::pack_unaligned_node(int idx,
- const Transform *aligned_space,
- const BoundBox *bounds,
- const int *child,
- const uint visibility,
- const float time_from,
- const float time_to,
- const int num)
-{
- float4 data[BVH_UNALIGNED_QNODE_SIZE];
- memset(data, 0, sizeof(data));
-
- data[0].x = __uint_as_float(visibility | PATH_RAY_NODE_UNALIGNED);
- data[0].y = time_from;
- data[0].z = time_to;
-
- for(int i = 0; i < num; i++) {
- Transform space = BVHUnaligned::compute_node_transform(
- bounds[i],
- aligned_space[i]);
-
- data[1][i] = space.x.x;
- data[2][i] = space.x.y;
- data[3][i] = space.x.z;
-
- data[4][i] = space.y.x;
- data[5][i] = space.y.y;
- data[6][i] = space.y.z;
-
- data[7][i] = space.z.x;
- data[8][i] = space.z.y;
- data[9][i] = space.z.z;
-
- data[10][i] = space.x.w;
- data[11][i] = space.y.w;
- data[12][i] = space.z.w;
-
- data[13][i] = __int_as_float(child[i]);
- }
-
- for(int i = num; i < 4; i++) {
- /* We store BB which would never be recorded as intersection
- * so kernel might safely assume there are always 4 child nodes.
- */
-
- data[1][i] = 1.0f;
- data[2][i] = 0.0f;
- data[3][i] = 0.0f;
-
- data[4][i] = 0.0f;
- data[5][i] = 0.0f;
- data[6][i] = 0.0f;
-
- data[7][i] = 0.0f;
- data[8][i] = 0.0f;
- data[9][i] = 0.0f;
-
- data[10][i] = -FLT_MAX;
- data[11][i] = -FLT_MAX;
- data[12][i] = -FLT_MAX;
-
- data[13][i] = __int_as_float(0);
- }
-
- memcpy(&pack.nodes[idx], data, sizeof(float4)*BVH_UNALIGNED_QNODE_SIZE);
-}
-
-/* Quad SIMD Nodes */
-
-void QBVH::pack_nodes(const BVHNode *root)
-{
- /* Calculate size of the arrays required. */
- const size_t num_nodes = root->getSubtreeSize(BVH_STAT_QNODE_COUNT);
- const size_t num_leaf_nodes = root->getSubtreeSize(BVH_STAT_LEAF_COUNT);
- assert(num_leaf_nodes <= num_nodes);
- const size_t num_inner_nodes = num_nodes - num_leaf_nodes;
- size_t node_size;
- if(params.use_unaligned_nodes) {
- const size_t num_unaligned_nodes =
- root->getSubtreeSize(BVH_STAT_UNALIGNED_INNER_QNODE_COUNT);
- node_size = (num_unaligned_nodes * BVH_UNALIGNED_QNODE_SIZE) +
- (num_inner_nodes - num_unaligned_nodes) * BVH_QNODE_SIZE;
- }
- else {
- node_size = num_inner_nodes * BVH_QNODE_SIZE;
- }
- /* Resize arrays. */
- pack.nodes.clear();
- pack.leaf_nodes.clear();
- /* For top level BVH, first merge existing BVH's so we know the offsets. */
- if(params.top_level) {
- pack_instances(node_size, num_leaf_nodes*BVH_QNODE_LEAF_SIZE);
- }
- else {
- pack.nodes.resize(node_size);
- pack.leaf_nodes.resize(num_leaf_nodes*BVH_QNODE_LEAF_SIZE);
- }
-
- int nextNodeIdx = 0, nextLeafNodeIdx = 0;
-
- vector<BVHStackEntry> stack;
- stack.reserve(BVHParams::MAX_DEPTH*2);
- if(root->is_leaf()) {
- stack.push_back(BVHStackEntry(root, nextLeafNodeIdx++));
- }
- else {
- stack.push_back(BVHStackEntry(root, nextNodeIdx));
- nextNodeIdx += node_qbvh_is_unaligned(root)
- ? BVH_UNALIGNED_QNODE_SIZE
- : BVH_QNODE_SIZE;
- }
-
- while(stack.size()) {
- BVHStackEntry e = stack.back();
- stack.pop_back();
-
- if(e.node->is_leaf()) {
- /* leaf node */
- const LeafNode *leaf = reinterpret_cast<const LeafNode*>(e.node);
- pack_leaf(e, leaf);
- }
- else {
- /* Inner node. */
- const BVHNode *node = e.node;
- const BVHNode *node0 = node->get_child(0);
- const BVHNode *node1 = node->get_child(1);
- /* Collect nodes. */
- const BVHNode *nodes[4];
- int numnodes = 0;
- if(node0->is_leaf()) {
- nodes[numnodes++] = node0;
- }
- else {
- nodes[numnodes++] = node0->get_child(0);
- nodes[numnodes++] = node0->get_child(1);
- }
- if(node1->is_leaf()) {
- nodes[numnodes++] = node1;
- }
- else {
- nodes[numnodes++] = node1->get_child(0);
- nodes[numnodes++] = node1->get_child(1);
- }
- /* Push entries on the stack. */
- for(int i = 0; i < numnodes; ++i) {
- int idx;
- if(nodes[i]->is_leaf()) {
- idx = nextLeafNodeIdx++;
- }
- else {
- idx = nextNodeIdx;
- nextNodeIdx += node_qbvh_is_unaligned(nodes[i])
- ? BVH_UNALIGNED_QNODE_SIZE
- : BVH_QNODE_SIZE;
- }
- stack.push_back(BVHStackEntry(nodes[i], idx));
- }
- /* Set node. */
- pack_inner(e, &stack[stack.size()-numnodes], numnodes);
- }
- }
- assert(node_size == nextNodeIdx);
- /* Root index to start traversal at, to handle case of single leaf node. */
- pack.root_index = (root->is_leaf())? -1: 0;
-}
-
-void QBVH::refit_nodes()
-{
- assert(!params.top_level);
-
- BoundBox bbox = BoundBox::empty;
- uint visibility = 0;
- refit_node(0, (pack.root_index == -1)? true: false, bbox, visibility);
-}
-
-void QBVH::refit_node(int idx, bool leaf, BoundBox& bbox, uint& visibility)
-{
- if(leaf) {
- int4 *data = &pack.leaf_nodes[idx];
- int4 c = data[0];
- /* Refit leaf node. */
- for(int prim = c.x; prim < c.y; prim++) {
- int pidx = pack.prim_index[prim];
- int tob = pack.prim_object[prim];
- Object *ob = objects[tob];
-
- if(pidx == -1) {
- /* Object instance. */
- bbox.grow(ob->bounds);
- }
- else {
- /* Primitives. */
- const Mesh *mesh = ob->mesh;
-
- if(pack.prim_type[prim] & PRIMITIVE_ALL_CURVE) {
- /* Curves. */
- int str_offset = (params.top_level)? mesh->curve_offset: 0;
- Mesh::Curve curve = mesh->get_curve(pidx - str_offset);
- int k = PRIMITIVE_UNPACK_SEGMENT(pack.prim_type[prim]);
-
- curve.bounds_grow(k, &mesh->curve_keys[0], &mesh->curve_radius[0], bbox);
-
- visibility |= PATH_RAY_CURVE;
-
- /* Motion curves. */
- if(mesh->use_motion_blur) {
- Attribute *attr = mesh->curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
-
- if(attr) {
- size_t mesh_size = mesh->curve_keys.size();
- size_t steps = mesh->motion_steps - 1;
- float3 *key_steps = attr->data_float3();
-
- for(size_t i = 0; i < steps; i++)
- curve.bounds_grow(k, key_steps + i*mesh_size, &mesh->curve_radius[0], bbox);
- }
- }
- }
- else {
- /* Triangles. */
- int tri_offset = (params.top_level)? mesh->tri_offset: 0;
- Mesh::Triangle triangle = mesh->get_triangle(pidx - tri_offset);
- const float3 *vpos = &mesh->verts[0];
-
- triangle.bounds_grow(vpos, bbox);
-
- /* Motion triangles. */
- if(mesh->use_motion_blur) {
- Attribute *attr = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
-
- if(attr) {
- size_t mesh_size = mesh->verts.size();
- size_t steps = mesh->motion_steps - 1;
- float3 *vert_steps = attr->data_float3();
-
- for(size_t i = 0; i < steps; i++)
- triangle.bounds_grow(vert_steps + i*mesh_size, bbox);
- }
- }
- }
- }
-
- visibility |= ob->visibility;
- }
-
- /* TODO(sergey): This is actually a copy of pack_leaf(),
- * but this chunk of code only knows actual data and has
- * no idea about BVHNode.
- *
- * Would be nice to de-duplicate code, but trying to make
- * making code more general ends up in much nastier code
- * in my opinion so far.
- *
- * Same applies to the inner nodes case below.
- */
- float4 leaf_data[BVH_QNODE_LEAF_SIZE];
- leaf_data[0].x = __int_as_float(c.x);
- leaf_data[0].y = __int_as_float(c.y);
- leaf_data[0].z = __uint_as_float(visibility);
- leaf_data[0].w = __uint_as_float(c.w);
- memcpy(&pack.leaf_nodes[idx], leaf_data, sizeof(float4)*BVH_QNODE_LEAF_SIZE);
- }
- else {
- int4 *data = &pack.nodes[idx];
- bool is_unaligned = (data[0].x & PATH_RAY_NODE_UNALIGNED) != 0;
- int4 c;
- if(is_unaligned) {
- c = data[13];
- }
- else {
- c = data[7];
- }
- /* Refit inner node, set bbox from children. */
- BoundBox child_bbox[4] = {BoundBox::empty,
- BoundBox::empty,
- BoundBox::empty,
- BoundBox::empty};
- uint child_visibility[4] = {0};
- int num_nodes = 0;
-
- for(int i = 0; i < 4; ++i) {
- if(c[i] != 0) {
- refit_node((c[i] < 0)? -c[i]-1: c[i], (c[i] < 0),
- child_bbox[i], child_visibility[i]);
- ++num_nodes;
- bbox.grow(child_bbox[i]);
- visibility |= child_visibility[i];
- }
- }
-
- if(is_unaligned) {
- Transform aligned_space[4] = {transform_identity(),
- transform_identity(),
- transform_identity(),
- transform_identity()};
- pack_unaligned_node(idx,
- aligned_space,
- child_bbox,
- &c[0],
- visibility,
- 0.0f,
- 1.0f,
- 4);
- }
- else {
- pack_aligned_node(idx,
- child_bbox,
- &c[0],
- visibility,
- 0.0f,
- 1.0f,
- 4);
- }
- }
-}
-
CCL_NAMESPACE_END
diff --git a/intern/cycles/bvh/bvh.h b/intern/cycles/bvh/bvh.h
index 60bc62ee6e4..7bac6112fd9 100644
--- a/intern/cycles/bvh/bvh.h
+++ b/intern/cycles/bvh/bvh.h
@@ -33,15 +33,8 @@ class LeafNode;
class Object;
class Progress;
-#define BVH_NODE_SIZE 4
-#define BVH_NODE_LEAF_SIZE 1
-#define BVH_QNODE_SIZE 8
-#define BVH_QNODE_LEAF_SIZE 1
-#define BVH_ALIGN 4096
-#define TRI_NODE_SIZE 3
-
-#define BVH_UNALIGNED_NODE_SIZE 7
-#define BVH_UNALIGNED_QNODE_SIZE 14
+#define BVH_ALIGN 4096
+#define TRI_NODE_SIZE 3
/* Packed BVH
*
@@ -54,7 +47,7 @@ struct PackedBVH {
/* BVH leaf nodes storage. */
array<int4> leaf_nodes;
/* object index to BVH node index mapping for instances */
- array<int> object_node;
+ array<int> object_node;
/* Mapping from primitive index to index in triangle array. */
array<uint> prim_tri_index;
/* Continuous storage of triangle vertices. */
@@ -110,95 +103,16 @@ protected:
virtual void refit_nodes() = 0;
};
-/* Binary BVH
- *
- * Typical BVH with each node having two children. */
-
-class BinaryBVH : public BVH {
-protected:
- /* constructor */
- friend class BVH;
- BinaryBVH(const BVHParams& params, const vector<Object*>& objects);
-
- /* pack */
- void pack_nodes(const BVHNode *root);
-
- void pack_leaf(const BVHStackEntry& e,
- const LeafNode *leaf);
- void pack_inner(const BVHStackEntry& e,
- const BVHStackEntry& e0,
- const BVHStackEntry& e1);
-
- void pack_aligned_inner(const BVHStackEntry& e,
- const BVHStackEntry& e0,
- const BVHStackEntry& e1);
- void pack_aligned_node(int idx,
- const BoundBox& b0,
- const BoundBox& b1,
- int c0, int c1,
- uint visibility0, uint visibility1);
-
- void pack_unaligned_inner(const BVHStackEntry& e,
- const BVHStackEntry& e0,
- const BVHStackEntry& e1);
- void pack_unaligned_node(int idx,
- const Transform& aligned_space0,
- const Transform& aligned_space1,
- const BoundBox& b0,
- const BoundBox& b1,
- int c0, int c1,
- uint visibility0, uint visibility1);
-
- /* refit */
- void refit_nodes();
- void refit_node(int idx, bool leaf, BoundBox& bbox, uint& visibility);
-};
-
-/* QBVH
- *
- * Quad BVH, with each node having four children, to use with SIMD instructions. */
+/* Pack Utility */
+struct BVHStackEntry
+{
+ const BVHNode *node;
+ int idx;
-class QBVH : public BVH {
-protected:
- /* constructor */
- friend class BVH;
- QBVH(const BVHParams& params, const vector<Object*>& objects);
-
- /* pack */
- void pack_nodes(const BVHNode *root);
-
- void pack_leaf(const BVHStackEntry& e, const LeafNode *leaf);
- void pack_inner(const BVHStackEntry& e, const BVHStackEntry *en, int num);
-
- void pack_aligned_inner(const BVHStackEntry& e,
- const BVHStackEntry *en,
- int num);
- void pack_aligned_node(int idx,
- const BoundBox *bounds,
- const int *child,
- const uint visibility,
- const float time_from,
- const float time_to,
- const int num);
-
- void pack_unaligned_inner(const BVHStackEntry& e,
- const BVHStackEntry *en,
- int num);
- void pack_unaligned_node(int idx,
- const Transform *aligned_space,
- const BoundBox *bounds,
- const int *child,
- const uint visibility,
- const float time_from,
- const float time_to,
- const int num);
-
- /* refit */
- void refit_nodes();
- void refit_node(int idx, bool leaf, BoundBox& bbox, uint& visibility);
+ BVHStackEntry(const BVHNode *n = 0, int i = 0);
+ int encodeIdx() const;
};
CCL_NAMESPACE_END
#endif /* __BVH_H__ */
-
diff --git a/intern/cycles/bvh/bvh2.cpp b/intern/cycles/bvh/bvh2.cpp
new file mode 100644
index 00000000000..340ba7dcf53
--- /dev/null
+++ b/intern/cycles/bvh/bvh2.cpp
@@ -0,0 +1,364 @@
+/*
+ * Adapted from code copyright 2009-2010 NVIDIA Corporation
+ * Modifications Copyright 2011, Blender Foundation.
+ *
+ * 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 "bvh/bvh2.h"
+
+#include "render/mesh.h"
+#include "render/object.h"
+
+#include "bvh/bvh_node.h"
+#include "bvh/bvh_unaligned.h"
+
+CCL_NAMESPACE_BEGIN
+
+static bool node_bvh_is_unaligned(const BVHNode *node)
+{
+ const BVHNode *node0 = node->get_child(0),
+ *node1 = node->get_child(1);
+ return node0->is_unaligned || node1->is_unaligned;
+}
+
+BVH2::BVH2(const BVHParams& params_, const vector<Object*>& objects_)
+: BVH(params_, objects_)
+{
+}
+
+void BVH2::pack_leaf(const BVHStackEntry& e,
+ const LeafNode *leaf)
+{
+ assert(e.idx + BVH_NODE_LEAF_SIZE <= pack.leaf_nodes.size());
+ float4 data[BVH_NODE_LEAF_SIZE];
+ memset(data, 0, sizeof(data));
+ if(leaf->num_triangles() == 1 && pack.prim_index[leaf->lo] == -1) {
+ /* object */
+ data[0].x = __int_as_float(~(leaf->lo));
+ data[0].y = __int_as_float(0);
+ }
+ else {
+ /* triangle */
+ data[0].x = __int_as_float(leaf->lo);
+ data[0].y = __int_as_float(leaf->hi);
+ }
+ data[0].z = __uint_as_float(leaf->visibility);
+ if(leaf->num_triangles() != 0) {
+ data[0].w = __uint_as_float(pack.prim_type[leaf->lo]);
+ }
+
+ memcpy(&pack.leaf_nodes[e.idx], data, sizeof(float4)*BVH_NODE_LEAF_SIZE);
+}
+
+void BVH2::pack_inner(const BVHStackEntry& e,
+ const BVHStackEntry& e0,
+ const BVHStackEntry& e1)
+{
+ if(e0.node->is_unaligned || e1.node->is_unaligned) {
+ pack_unaligned_inner(e, e0, e1);
+ } else {
+ pack_aligned_inner(e, e0, e1);
+ }
+}
+
+void BVH2::pack_aligned_inner(const BVHStackEntry& e,
+ const BVHStackEntry& e0,
+ const BVHStackEntry& e1)
+{
+ pack_aligned_node(e.idx,
+ e0.node->bounds, e1.node->bounds,
+ e0.encodeIdx(), e1.encodeIdx(),
+ e0.node->visibility, e1.node->visibility);
+}
+
+void BVH2::pack_aligned_node(int idx,
+ const BoundBox& b0,
+ const BoundBox& b1,
+ int c0, int c1,
+ uint visibility0, uint visibility1)
+{
+ assert(idx + BVH_NODE_SIZE <= pack.nodes.size());
+ assert(c0 < 0 || c0 < pack.nodes.size());
+ assert(c1 < 0 || c1 < pack.nodes.size());
+
+ int4 data[BVH_NODE_SIZE] = {
+ make_int4(visibility0 & ~PATH_RAY_NODE_UNALIGNED,
+ visibility1 & ~PATH_RAY_NODE_UNALIGNED,
+ c0, c1),
+ make_int4(__float_as_int(b0.min.x),
+ __float_as_int(b1.min.x),
+ __float_as_int(b0.max.x),
+ __float_as_int(b1.max.x)),
+ make_int4(__float_as_int(b0.min.y),
+ __float_as_int(b1.min.y),
+ __float_as_int(b0.max.y),
+ __float_as_int(b1.max.y)),
+ make_int4(__float_as_int(b0.min.z),
+ __float_as_int(b1.min.z),
+ __float_as_int(b0.max.z),
+ __float_as_int(b1.max.z)),
+ };
+
+ memcpy(&pack.nodes[idx], data, sizeof(int4)*BVH_NODE_SIZE);
+}
+
+void BVH2::pack_unaligned_inner(const BVHStackEntry& e,
+ const BVHStackEntry& e0,
+ const BVHStackEntry& e1)
+{
+ pack_unaligned_node(e.idx,
+ e0.node->get_aligned_space(),
+ e1.node->get_aligned_space(),
+ e0.node->bounds,
+ e1.node->bounds,
+ e0.encodeIdx(), e1.encodeIdx(),
+ e0.node->visibility, e1.node->visibility);
+}
+
+void BVH2::pack_unaligned_node(int idx,
+ const Transform& aligned_space0,
+ const Transform& aligned_space1,
+ const BoundBox& bounds0,
+ const BoundBox& bounds1,
+ int c0, int c1,
+ uint visibility0, uint visibility1)
+{
+ assert(idx + BVH_UNALIGNED_NODE_SIZE <= pack.nodes.size());
+ assert(c0 < 0 || c0 < pack.nodes.size());
+ assert(c1 < 0 || c1 < pack.nodes.size());
+
+ float4 data[BVH_UNALIGNED_NODE_SIZE];
+ Transform space0 = BVHUnaligned::compute_node_transform(bounds0,
+ aligned_space0);
+ Transform space1 = BVHUnaligned::compute_node_transform(bounds1,
+ aligned_space1);
+ data[0] = make_float4(__int_as_float(visibility0 | PATH_RAY_NODE_UNALIGNED),
+ __int_as_float(visibility1 | PATH_RAY_NODE_UNALIGNED),
+ __int_as_float(c0),
+ __int_as_float(c1));
+
+ data[1] = space0.x;
+ data[2] = space0.y;
+ data[3] = space0.z;
+ data[4] = space1.x;
+ data[5] = space1.y;
+ data[6] = space1.z;
+
+ memcpy(&pack.nodes[idx], data, sizeof(float4)*BVH_UNALIGNED_NODE_SIZE);
+}
+
+void BVH2::pack_nodes(const BVHNode *root)
+{
+ const size_t num_nodes = root->getSubtreeSize(BVH_STAT_NODE_COUNT);
+ const size_t num_leaf_nodes = root->getSubtreeSize(BVH_STAT_LEAF_COUNT);
+ assert(num_leaf_nodes <= num_nodes);
+ const size_t num_inner_nodes = num_nodes - num_leaf_nodes;
+ size_t node_size;
+ if(params.use_unaligned_nodes) {
+ const size_t num_unaligned_nodes =
+ root->getSubtreeSize(BVH_STAT_UNALIGNED_INNER_COUNT);
+ node_size = (num_unaligned_nodes * BVH_UNALIGNED_NODE_SIZE) +
+ (num_inner_nodes - num_unaligned_nodes) * BVH_NODE_SIZE;
+ }
+ else {
+ node_size = num_inner_nodes * BVH_NODE_SIZE;
+ }
+ /* Resize arrays */
+ pack.nodes.clear();
+ pack.leaf_nodes.clear();
+ /* For top level BVH, first merge existing BVH's so we know the offsets. */
+ if(params.top_level) {
+ pack_instances(node_size, num_leaf_nodes*BVH_NODE_LEAF_SIZE);
+ }
+ else {
+ pack.nodes.resize(node_size);
+ pack.leaf_nodes.resize(num_leaf_nodes*BVH_NODE_LEAF_SIZE);
+ }
+
+ int nextNodeIdx = 0, nextLeafNodeIdx = 0;
+
+ vector<BVHStackEntry> stack;
+ stack.reserve(BVHParams::MAX_DEPTH*2);
+ if(root->is_leaf()) {
+ stack.push_back(BVHStackEntry(root, nextLeafNodeIdx++));
+ }
+ else {
+ stack.push_back(BVHStackEntry(root, nextNodeIdx));
+ nextNodeIdx += node_bvh_is_unaligned(root)
+ ? BVH_UNALIGNED_NODE_SIZE
+ : BVH_NODE_SIZE;
+ }
+
+ while(stack.size()) {
+ BVHStackEntry e = stack.back();
+ stack.pop_back();
+
+ if(e.node->is_leaf()) {
+ /* leaf node */
+ const LeafNode *leaf = reinterpret_cast<const LeafNode*>(e.node);
+ pack_leaf(e, leaf);
+ }
+ else {
+ /* innner node */
+ int idx[2];
+ for(int i = 0; i < 2; ++i) {
+ if(e.node->get_child(i)->is_leaf()) {
+ idx[i] = nextLeafNodeIdx++;
+ }
+ else {
+ idx[i] = nextNodeIdx;
+ nextNodeIdx += node_bvh_is_unaligned(e.node->get_child(i))
+ ? BVH_UNALIGNED_NODE_SIZE
+ : BVH_NODE_SIZE;
+ }
+ }
+
+ stack.push_back(BVHStackEntry(e.node->get_child(0), idx[0]));
+ stack.push_back(BVHStackEntry(e.node->get_child(1), idx[1]));
+
+ pack_inner(e, stack[stack.size()-2], stack[stack.size()-1]);
+ }
+ }
+ assert(node_size == nextNodeIdx);
+ /* root index to start traversal at, to handle case of single leaf node */
+ pack.root_index = (root->is_leaf())? -1: 0;
+}
+
+void BVH2::refit_nodes()
+{
+ assert(!params.top_level);
+
+ BoundBox bbox = BoundBox::empty;
+ uint visibility = 0;
+ refit_node(0, (pack.root_index == -1)? true: false, bbox, visibility);
+}
+
+void BVH2::refit_node(int idx, bool leaf, BoundBox& bbox, uint& visibility)
+{
+ if(leaf) {
+ assert(idx + BVH_NODE_LEAF_SIZE <= pack.leaf_nodes.size());
+ const int4 *data = &pack.leaf_nodes[idx];
+ const int c0 = data[0].x;
+ const int c1 = data[0].y;
+ /* refit leaf node */
+ for(int prim = c0; prim < c1; prim++) {
+ int pidx = pack.prim_index[prim];
+ int tob = pack.prim_object[prim];
+ Object *ob = objects[tob];
+
+ if(pidx == -1) {
+ /* object instance */
+ bbox.grow(ob->bounds);
+ }
+ else {
+ /* primitives */
+ const Mesh *mesh = ob->mesh;
+
+ if(pack.prim_type[prim] & PRIMITIVE_ALL_CURVE) {
+ /* curves */
+ int str_offset = (params.top_level)? mesh->curve_offset: 0;
+ Mesh::Curve curve = mesh->get_curve(pidx - str_offset);
+ int k = PRIMITIVE_UNPACK_SEGMENT(pack.prim_type[prim]);
+
+ curve.bounds_grow(k, &mesh->curve_keys[0], &mesh->curve_radius[0], bbox);
+
+ visibility |= PATH_RAY_CURVE;
+
+ /* motion curves */
+ if(mesh->use_motion_blur) {
+ Attribute *attr = mesh->curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
+
+ if(attr) {
+ size_t mesh_size = mesh->curve_keys.size();
+ size_t steps = mesh->motion_steps - 1;
+ float3 *key_steps = attr->data_float3();
+
+ for(size_t i = 0; i < steps; i++)
+ curve.bounds_grow(k, key_steps + i*mesh_size, &mesh->curve_radius[0], bbox);
+ }
+ }
+ }
+ else {
+ /* triangles */
+ int tri_offset = (params.top_level)? mesh->tri_offset: 0;
+ Mesh::Triangle triangle = mesh->get_triangle(pidx - tri_offset);
+ const float3 *vpos = &mesh->verts[0];
+
+ triangle.bounds_grow(vpos, bbox);
+
+ /* motion triangles */
+ if(mesh->use_motion_blur) {
+ Attribute *attr = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
+
+ if(attr) {
+ size_t mesh_size = mesh->verts.size();
+ size_t steps = mesh->motion_steps - 1;
+ float3 *vert_steps = attr->data_float3();
+
+ for(size_t i = 0; i < steps; i++)
+ triangle.bounds_grow(vert_steps + i*mesh_size, bbox);
+ }
+ }
+ }
+ }
+
+ visibility |= ob->visibility;
+ }
+
+ /* TODO(sergey): De-duplicate with pack_leaf(). */
+ float4 leaf_data[BVH_NODE_LEAF_SIZE];
+ leaf_data[0].x = __int_as_float(c0);
+ leaf_data[0].y = __int_as_float(c1);
+ leaf_data[0].z = __uint_as_float(visibility);
+ leaf_data[0].w = __uint_as_float(data[0].w);
+ memcpy(&pack.leaf_nodes[idx], leaf_data, sizeof(float4)*BVH_NODE_LEAF_SIZE);
+ }
+ else {
+ assert(idx + BVH_NODE_SIZE <= pack.nodes.size());
+
+ const int4 *data = &pack.nodes[idx];
+ const bool is_unaligned = (data[0].x & PATH_RAY_NODE_UNALIGNED) != 0;
+ const int c0 = data[0].z;
+ const int c1 = data[0].w;
+ /* refit inner node, set bbox from children */
+ BoundBox bbox0 = BoundBox::empty, bbox1 = BoundBox::empty;
+ uint visibility0 = 0, visibility1 = 0;
+
+ refit_node((c0 < 0)? -c0-1: c0, (c0 < 0), bbox0, visibility0);
+ refit_node((c1 < 0)? -c1-1: c1, (c1 < 0), bbox1, visibility1);
+
+ if(is_unaligned) {
+ Transform aligned_space = transform_identity();
+ pack_unaligned_node(idx,
+ aligned_space, aligned_space,
+ bbox0, bbox1,
+ c0, c1,
+ visibility0,
+ visibility1);
+ }
+ else {
+ pack_aligned_node(idx,
+ bbox0, bbox1,
+ c0, c1,
+ visibility0,
+ visibility1);
+ }
+
+ bbox.grow(bbox0);
+ bbox.grow(bbox1);
+ visibility = visibility0|visibility1;
+ }
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/bvh/bvh2.h b/intern/cycles/bvh/bvh2.h
new file mode 100644
index 00000000000..df65ddca5b7
--- /dev/null
+++ b/intern/cycles/bvh/bvh2.h
@@ -0,0 +1,87 @@
+/*
+ * Adapted from code copyright 2009-2010 NVIDIA Corporation
+ * Modifications Copyright 2011, Blender Foundation.
+ *
+ * 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+#ifndef __BVH2_H__
+#define __BVH2_H__
+
+#include "bvh/bvh.h"
+#include "bvh/bvh_params.h"
+
+#include "util/util_types.h"
+#include "util/util_vector.h"
+
+CCL_NAMESPACE_BEGIN
+
+class BVHNode;
+struct BVHStackEntry;
+class BVHParams;
+class BoundBox;
+class LeafNode;
+class Object;
+class Progress;
+
+#define BVH_NODE_SIZE 4
+#define BVH_NODE_LEAF_SIZE 1
+#define BVH_UNALIGNED_NODE_SIZE 7
+
+/* BVH2
+ *
+ * Typical BVH with each node having two children.
+ */
+class BVH2 : public BVH {
+protected:
+ /* constructor */
+ friend class BVH;
+ BVH2(const BVHParams& params, const vector<Object*>& objects);
+
+ /* pack */
+ void pack_nodes(const BVHNode *root);
+
+ void pack_leaf(const BVHStackEntry& e,
+ const LeafNode *leaf);
+ void pack_inner(const BVHStackEntry& e,
+ const BVHStackEntry& e0,
+ const BVHStackEntry& e1);
+
+ void pack_aligned_inner(const BVHStackEntry& e,
+ const BVHStackEntry& e0,
+ const BVHStackEntry& e1);
+ void pack_aligned_node(int idx,
+ const BoundBox& b0,
+ const BoundBox& b1,
+ int c0, int c1,
+ uint visibility0, uint visibility1);
+
+ void pack_unaligned_inner(const BVHStackEntry& e,
+ const BVHStackEntry& e0,
+ const BVHStackEntry& e1);
+ void pack_unaligned_node(int idx,
+ const Transform& aligned_space0,
+ const Transform& aligned_space1,
+ const BoundBox& b0,
+ const BoundBox& b1,
+ int c0, int c1,
+ uint visibility0, uint visibility1);
+
+ /* refit */
+ void refit_nodes();
+ void refit_node(int idx, bool leaf, BoundBox& bbox, uint& visibility);
+};
+
+CCL_NAMESPACE_END
+
+#endif /* __BVH2_H__ */
diff --git a/intern/cycles/bvh/bvh4.cpp b/intern/cycles/bvh/bvh4.cpp
new file mode 100644
index 00000000000..5034ab811d5
--- /dev/null
+++ b/intern/cycles/bvh/bvh4.cpp
@@ -0,0 +1,516 @@
+/*
+ * Adapted from code copyright 2009-2010 NVIDIA Corporation
+ * Modifications Copyright 2011, Blender Foundation.
+ *
+ * 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 "bvh/bvh4.h"
+
+#include "render/mesh.h"
+#include "render/object.h"
+
+#include "bvh/bvh_node.h"
+#include "bvh/bvh_unaligned.h"
+
+CCL_NAMESPACE_BEGIN
+
+/* Can we avoid this somehow or make more generic?
+ *
+ * Perhaps we can merge nodes in actual tree and make our
+ * life easier all over the place.
+ */
+static bool node_qbvh_is_unaligned(const BVHNode *node)
+{
+ const BVHNode *node0 = node->get_child(0),
+ *node1 = node->get_child(1);
+ bool has_unaligned = false;
+ if(node0->is_leaf()) {
+ has_unaligned |= node0->is_unaligned;
+ }
+ else {
+ has_unaligned |= node0->get_child(0)->is_unaligned;
+ has_unaligned |= node0->get_child(1)->is_unaligned;
+ }
+ if(node1->is_leaf()) {
+ has_unaligned |= node1->is_unaligned;
+ }
+ else {
+ has_unaligned |= node1->get_child(0)->is_unaligned;
+ has_unaligned |= node1->get_child(1)->is_unaligned;
+ }
+ return has_unaligned;
+}
+
+BVH4::BVH4(const BVHParams& params_, const vector<Object*>& objects_)
+: BVH(params_, objects_)
+{
+ params.use_qbvh = true;
+}
+
+void BVH4::pack_leaf(const BVHStackEntry& e, const LeafNode *leaf)
+{
+ float4 data[BVH_QNODE_LEAF_SIZE];
+ memset(data, 0, sizeof(data));
+ if(leaf->num_triangles() == 1 && pack.prim_index[leaf->lo] == -1) {
+ /* object */
+ data[0].x = __int_as_float(~(leaf->lo));
+ data[0].y = __int_as_float(0);
+ }
+ else {
+ /* triangle */
+ data[0].x = __int_as_float(leaf->lo);
+ data[0].y = __int_as_float(leaf->hi);
+ }
+ data[0].z = __uint_as_float(leaf->visibility);
+ if(leaf->num_triangles() != 0) {
+ data[0].w = __uint_as_float(pack.prim_type[leaf->lo]);
+ }
+
+ memcpy(&pack.leaf_nodes[e.idx], data, sizeof(float4)*BVH_QNODE_LEAF_SIZE);
+}
+
+void BVH4::pack_inner(const BVHStackEntry& e,
+ const BVHStackEntry *en,
+ int num)
+{
+ bool has_unaligned = false;
+ /* Check whether we have to create unaligned node or all nodes are aligned
+ * and we can cut some corner here.
+ */
+ if(params.use_unaligned_nodes) {
+ for(int i = 0; i < num; i++) {
+ if(en[i].node->is_unaligned) {
+ has_unaligned = true;
+ break;
+ }
+ }
+ }
+ if(has_unaligned) {
+ /* There's no unaligned children, pack into AABB node. */
+ pack_unaligned_inner(e, en, num);
+ }
+ else {
+ /* Create unaligned node with orientation transform for each of the
+ * children.
+ */
+ pack_aligned_inner(e, en, num);
+ }
+}
+
+void BVH4::pack_aligned_inner(const BVHStackEntry& e,
+ const BVHStackEntry *en,
+ int num)
+{
+ BoundBox bounds[4];
+ int child[4];
+ for(int i = 0; i < num; ++i) {
+ bounds[i] = en[i].node->bounds;
+ child[i] = en[i].encodeIdx();
+ }
+ pack_aligned_node(e.idx,
+ bounds,
+ child,
+ e.node->visibility,
+ e.node->time_from,
+ e.node->time_to,
+ num);
+}
+
+void BVH4::pack_aligned_node(int idx,
+ const BoundBox *bounds,
+ const int *child,
+ const uint visibility,
+ const float time_from,
+ const float time_to,
+ const int num)
+{
+ float4 data[BVH_QNODE_SIZE];
+ memset(data, 0, sizeof(data));
+
+ data[0].x = __uint_as_float(visibility & ~PATH_RAY_NODE_UNALIGNED);
+ data[0].y = time_from;
+ data[0].z = time_to;
+
+ for(int i = 0; i < num; i++) {
+ float3 bb_min = bounds[i].min;
+ float3 bb_max = bounds[i].max;
+
+ data[1][i] = bb_min.x;
+ data[2][i] = bb_max.x;
+ data[3][i] = bb_min.y;
+ data[4][i] = bb_max.y;
+ data[5][i] = bb_min.z;
+ data[6][i] = bb_max.z;
+
+ data[7][i] = __int_as_float(child[i]);
+ }
+
+ for(int i = num; i < 4; i++) {
+ /* We store BB which would never be recorded as intersection
+ * so kernel might safely assume there are always 4 child nodes.
+ */
+ data[1][i] = FLT_MAX;
+ data[2][i] = -FLT_MAX;
+
+ data[3][i] = FLT_MAX;
+ data[4][i] = -FLT_MAX;
+
+ data[5][i] = FLT_MAX;
+ data[6][i] = -FLT_MAX;
+
+ data[7][i] = __int_as_float(0);
+ }
+
+ memcpy(&pack.nodes[idx], data, sizeof(float4)*BVH_QNODE_SIZE);
+}
+
+void BVH4::pack_unaligned_inner(const BVHStackEntry& e,
+ const BVHStackEntry *en,
+ int num)
+{
+ Transform aligned_space[4];
+ BoundBox bounds[4];
+ int child[4];
+ for(int i = 0; i < num; ++i) {
+ aligned_space[i] = en[i].node->get_aligned_space();
+ bounds[i] = en[i].node->bounds;
+ child[i] = en[i].encodeIdx();
+ }
+ pack_unaligned_node(e.idx,
+ aligned_space,
+ bounds,
+ child,
+ e.node->visibility,
+ e.node->time_from,
+ e.node->time_to,
+ num);
+}
+
+void BVH4::pack_unaligned_node(int idx,
+ const Transform *aligned_space,
+ const BoundBox *bounds,
+ const int *child,
+ const uint visibility,
+ const float time_from,
+ const float time_to,
+ const int num)
+{
+ float4 data[BVH_UNALIGNED_QNODE_SIZE];
+ memset(data, 0, sizeof(data));
+
+ data[0].x = __uint_as_float(visibility | PATH_RAY_NODE_UNALIGNED);
+ data[0].y = time_from;
+ data[0].z = time_to;
+
+ for(int i = 0; i < num; i++) {
+ Transform space = BVHUnaligned::compute_node_transform(
+ bounds[i],
+ aligned_space[i]);
+
+ data[1][i] = space.x.x;
+ data[2][i] = space.x.y;
+ data[3][i] = space.x.z;
+
+ data[4][i] = space.y.x;
+ data[5][i] = space.y.y;
+ data[6][i] = space.y.z;
+
+ data[7][i] = space.z.x;
+ data[8][i] = space.z.y;
+ data[9][i] = space.z.z;
+
+ data[10][i] = space.x.w;
+ data[11][i] = space.y.w;
+ data[12][i] = space.z.w;
+
+ data[13][i] = __int_as_float(child[i]);
+ }
+
+ for(int i = num; i < 4; i++) {
+ /* We store BB which would never be recorded as intersection
+ * so kernel might safely assume there are always 4 child nodes.
+ */
+
+ data[1][i] = 1.0f;
+ data[2][i] = 0.0f;
+ data[3][i] = 0.0f;
+
+ data[4][i] = 0.0f;
+ data[5][i] = 0.0f;
+ data[6][i] = 0.0f;
+
+ data[7][i] = 0.0f;
+ data[8][i] = 0.0f;
+ data[9][i] = 0.0f;
+
+ data[10][i] = -FLT_MAX;
+ data[11][i] = -FLT_MAX;
+ data[12][i] = -FLT_MAX;
+
+ data[13][i] = __int_as_float(0);
+ }
+
+ memcpy(&pack.nodes[idx], data, sizeof(float4)*BVH_UNALIGNED_QNODE_SIZE);
+}
+
+/* Quad SIMD Nodes */
+
+void BVH4::pack_nodes(const BVHNode *root)
+{
+ /* Calculate size of the arrays required. */
+ const size_t num_nodes = root->getSubtreeSize(BVH_STAT_QNODE_COUNT);
+ const size_t num_leaf_nodes = root->getSubtreeSize(BVH_STAT_LEAF_COUNT);
+ assert(num_leaf_nodes <= num_nodes);
+ const size_t num_inner_nodes = num_nodes - num_leaf_nodes;
+ size_t node_size;
+ if(params.use_unaligned_nodes) {
+ const size_t num_unaligned_nodes =
+ root->getSubtreeSize(BVH_STAT_UNALIGNED_INNER_QNODE_COUNT);
+ node_size = (num_unaligned_nodes * BVH_UNALIGNED_QNODE_SIZE) +
+ (num_inner_nodes - num_unaligned_nodes) * BVH_QNODE_SIZE;
+ }
+ else {
+ node_size = num_inner_nodes * BVH_QNODE_SIZE;
+ }
+ /* Resize arrays. */
+ pack.nodes.clear();
+ pack.leaf_nodes.clear();
+ /* For top level BVH, first merge existing BVH's so we know the offsets. */
+ if(params.top_level) {
+ pack_instances(node_size, num_leaf_nodes*BVH_QNODE_LEAF_SIZE);
+ }
+ else {
+ pack.nodes.resize(node_size);
+ pack.leaf_nodes.resize(num_leaf_nodes*BVH_QNODE_LEAF_SIZE);
+ }
+
+ int nextNodeIdx = 0, nextLeafNodeIdx = 0;
+
+ vector<BVHStackEntry> stack;
+ stack.reserve(BVHParams::MAX_DEPTH*2);
+ if(root->is_leaf()) {
+ stack.push_back(BVHStackEntry(root, nextLeafNodeIdx++));
+ }
+ else {
+ stack.push_back(BVHStackEntry(root, nextNodeIdx));
+ nextNodeIdx += node_qbvh_is_unaligned(root)
+ ? BVH_UNALIGNED_QNODE_SIZE
+ : BVH_QNODE_SIZE;
+ }
+
+ while(stack.size()) {
+ BVHStackEntry e = stack.back();
+ stack.pop_back();
+
+ if(e.node->is_leaf()) {
+ /* leaf node */
+ const LeafNode *leaf = reinterpret_cast<const LeafNode*>(e.node);
+ pack_leaf(e, leaf);
+ }
+ else {
+ /* Inner node. */
+ const BVHNode *node = e.node;
+ const BVHNode *node0 = node->get_child(0);
+ const BVHNode *node1 = node->get_child(1);
+ /* Collect nodes. */
+ const BVHNode *nodes[4];
+ int numnodes = 0;
+ if(node0->is_leaf()) {
+ nodes[numnodes++] = node0;
+ }
+ else {
+ nodes[numnodes++] = node0->get_child(0);
+ nodes[numnodes++] = node0->get_child(1);
+ }
+ if(node1->is_leaf()) {
+ nodes[numnodes++] = node1;
+ }
+ else {
+ nodes[numnodes++] = node1->get_child(0);
+ nodes[numnodes++] = node1->get_child(1);
+ }
+ /* Push entries on the stack. */
+ for(int i = 0; i < numnodes; ++i) {
+ int idx;
+ if(nodes[i]->is_leaf()) {
+ idx = nextLeafNodeIdx++;
+ }
+ else {
+ idx = nextNodeIdx;
+ nextNodeIdx += node_qbvh_is_unaligned(nodes[i])
+ ? BVH_UNALIGNED_QNODE_SIZE
+ : BVH_QNODE_SIZE;
+ }
+ stack.push_back(BVHStackEntry(nodes[i], idx));
+ }
+ /* Set node. */
+ pack_inner(e, &stack[stack.size()-numnodes], numnodes);
+ }
+ }
+ assert(node_size == nextNodeIdx);
+ /* Root index to start traversal at, to handle case of single leaf node. */
+ pack.root_index = (root->is_leaf())? -1: 0;
+}
+
+void BVH4::refit_nodes()
+{
+ assert(!params.top_level);
+
+ BoundBox bbox = BoundBox::empty;
+ uint visibility = 0;
+ refit_node(0, (pack.root_index == -1)? true: false, bbox, visibility);
+}
+
+void BVH4::refit_node(int idx, bool leaf, BoundBox& bbox, uint& visibility)
+{
+ if(leaf) {
+ int4 *data = &pack.leaf_nodes[idx];
+ int4 c = data[0];
+ /* Refit leaf node. */
+ for(int prim = c.x; prim < c.y; prim++) {
+ int pidx = pack.prim_index[prim];
+ int tob = pack.prim_object[prim];
+ Object *ob = objects[tob];
+
+ if(pidx == -1) {
+ /* Object instance. */
+ bbox.grow(ob->bounds);
+ }
+ else {
+ /* Primitives. */
+ const Mesh *mesh = ob->mesh;
+
+ if(pack.prim_type[prim] & PRIMITIVE_ALL_CURVE) {
+ /* Curves. */
+ int str_offset = (params.top_level)? mesh->curve_offset: 0;
+ Mesh::Curve curve = mesh->get_curve(pidx - str_offset);
+ int k = PRIMITIVE_UNPACK_SEGMENT(pack.prim_type[prim]);
+
+ curve.bounds_grow(k, &mesh->curve_keys[0], &mesh->curve_radius[0], bbox);
+
+ visibility |= PATH_RAY_CURVE;
+
+ /* Motion curves. */
+ if(mesh->use_motion_blur) {
+ Attribute *attr = mesh->curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
+
+ if(attr) {
+ size_t mesh_size = mesh->curve_keys.size();
+ size_t steps = mesh->motion_steps - 1;
+ float3 *key_steps = attr->data_float3();
+
+ for(size_t i = 0; i < steps; i++)
+ curve.bounds_grow(k, key_steps + i*mesh_size, &mesh->curve_radius[0], bbox);
+ }
+ }
+ }
+ else {
+ /* Triangles. */
+ int tri_offset = (params.top_level)? mesh->tri_offset: 0;
+ Mesh::Triangle triangle = mesh->get_triangle(pidx - tri_offset);
+ const float3 *vpos = &mesh->verts[0];
+
+ triangle.bounds_grow(vpos, bbox);
+
+ /* Motion triangles. */
+ if(mesh->use_motion_blur) {
+ Attribute *attr = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION);
+
+ if(attr) {
+ size_t mesh_size = mesh->verts.size();
+ size_t steps = mesh->motion_steps - 1;
+ float3 *vert_steps = attr->data_float3();
+
+ for(size_t i = 0; i < steps; i++)
+ triangle.bounds_grow(vert_steps + i*mesh_size, bbox);
+ }
+ }
+ }
+ }
+
+ visibility |= ob->visibility;
+ }
+
+ /* TODO(sergey): This is actually a copy of pack_leaf(),
+ * but this chunk of code only knows actual data and has
+ * no idea about BVHNode.
+ *
+ * Would be nice to de-duplicate code, but trying to make
+ * making code more general ends up in much nastier code
+ * in my opinion so far.
+ *
+ * Same applies to the inner nodes case below.
+ */
+ float4 leaf_data[BVH_QNODE_LEAF_SIZE];
+ leaf_data[0].x = __int_as_float(c.x);
+ leaf_data[0].y = __int_as_float(c.y);
+ leaf_data[0].z = __uint_as_float(visibility);
+ leaf_data[0].w = __uint_as_float(c.w);
+ memcpy(&pack.leaf_nodes[idx], leaf_data, sizeof(float4)*BVH_QNODE_LEAF_SIZE);
+ }
+ else {
+ int4 *data = &pack.nodes[idx];
+ bool is_unaligned = (data[0].x & PATH_RAY_NODE_UNALIGNED) != 0;
+ int4 c;
+ if(is_unaligned) {
+ c = data[13];
+ }
+ else {
+ c = data[7];
+ }
+ /* Refit inner node, set bbox from children. */
+ BoundBox child_bbox[4] = {BoundBox::empty,
+ BoundBox::empty,
+ BoundBox::empty,
+ BoundBox::empty};
+ uint child_visibility[4] = {0};
+ int num_nodes = 0;
+
+ for(int i = 0; i < 4; ++i) {
+ if(c[i] != 0) {
+ refit_node((c[i] < 0)? -c[i]-1: c[i], (c[i] < 0),
+ child_bbox[i], child_visibility[i]);
+ ++num_nodes;
+ bbox.grow(child_bbox[i]);
+ visibility |= child_visibility[i];
+ }
+ }
+
+ if(is_unaligned) {
+ Transform aligned_space[4] = {transform_identity(),
+ transform_identity(),
+ transform_identity(),
+ transform_identity()};
+ pack_unaligned_node(idx,
+ aligned_space,
+ child_bbox,
+ &c[0],
+ visibility,
+ 0.0f,
+ 1.0f,
+ 4);
+ }
+ else {
+ pack_aligned_node(idx,
+ child_bbox,
+ &c[0],
+ visibility,
+ 0.0f,
+ 1.0f,
+ 4);
+ }
+ }
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/bvh/bvh4.h b/intern/cycles/bvh/bvh4.h
new file mode 100644
index 00000000000..310909a37e1
--- /dev/null
+++ b/intern/cycles/bvh/bvh4.h
@@ -0,0 +1,87 @@
+/*
+ * Adapted from code copyright 2009-2010 NVIDIA Corporation
+ * Modifications Copyright 2011, Blender Foundation.
+ *
+ * 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.
+ */
+
+#ifndef __BVH4_H__
+#define __BVH4_H__
+
+#include "bvh/bvh.h"
+#include "bvh/bvh_params.h"
+
+#include "util/util_types.h"
+#include "util/util_vector.h"
+
+CCL_NAMESPACE_BEGIN
+
+class BVHNode;
+struct BVHStackEntry;
+class BVHParams;
+class BoundBox;
+class LeafNode;
+class Object;
+class Progress;
+
+#define BVH_QNODE_SIZE 8
+#define BVH_QNODE_LEAF_SIZE 1
+#define BVH_UNALIGNED_QNODE_SIZE 14
+
+/* BVH4
+ *
+ * Quad BVH, with each node having four children, to use with SIMD instructions.
+ */
+class BVH4 : public BVH {
+protected:
+ /* constructor */
+ friend class BVH;
+ BVH4(const BVHParams& params, const vector<Object*>& objects);
+
+ /* pack */
+ void pack_nodes(const BVHNode *root);
+
+ void pack_leaf(const BVHStackEntry& e, const LeafNode *leaf);
+ void pack_inner(const BVHStackEntry& e, const BVHStackEntry *en, int num);
+
+ void pack_aligned_inner(const BVHStackEntry& e,
+ const BVHStackEntry *en,
+ int num);
+ void pack_aligned_node(int idx,
+ const BoundBox *bounds,
+ const int *child,
+ const uint visibility,
+ const float time_from,
+ const float time_to,
+ const int num);
+
+ void pack_unaligned_inner(const BVHStackEntry& e,
+ const BVHStackEntry *en,
+ int num);
+ void pack_unaligned_node(int idx,
+ const Transform *aligned_space,
+ const BoundBox *bounds,
+ const int *child,
+ const uint visibility,
+ const float time_from,
+ const float time_to,
+ const int num);
+
+ /* refit */
+ void refit_nodes();
+ void refit_node(int idx, bool leaf, BoundBox& bbox, uint& visibility);
+};
+
+CCL_NAMESPACE_END
+
+#endif /* __BVH4_H__ */
diff --git a/intern/cycles/bvh/bvh_binning.cpp b/intern/cycles/bvh/bvh_binning.cpp
index 3226008f511..63a7fc11668 100644
--- a/intern/cycles/bvh/bvh_binning.cpp
+++ b/intern/cycles/bvh/bvh_binning.cpp
@@ -17,10 +17,10 @@
//#define __KERNEL_SSE__
-#include <stdlib.h>
-
#include "bvh/bvh_binning.h"
+#include <stdlib.h>
+
#include "util/util_algorithm.h"
#include "util/util_boundbox.h"
#include "util/util_types.h"
diff --git a/intern/cycles/bvh/bvh_binning.h b/intern/cycles/bvh/bvh_binning.h
index 285f9c56a62..c2e259b1696 100644
--- a/intern/cycles/bvh/bvh_binning.h
+++ b/intern/cycles/bvh/bvh_binning.h
@@ -111,5 +111,4 @@ protected:
CCL_NAMESPACE_END
-#endif
-
+#endif /* __BVH_BINNING_H__ */
diff --git a/intern/cycles/bvh/bvh_build.cpp b/intern/cycles/bvh/bvh_build.cpp
index 95c71b54da0..1880964355c 100644
--- a/intern/cycles/bvh/bvh_build.cpp
+++ b/intern/cycles/bvh/bvh_build.cpp
@@ -15,8 +15,9 @@
* limitations under the License.
*/
-#include "bvh/bvh_binning.h"
#include "bvh/bvh_build.h"
+
+#include "bvh/bvh_binning.h"
#include "bvh/bvh_node.h"
#include "bvh/bvh_params.h"
#include "bvh_split.h"
diff --git a/intern/cycles/bvh/bvh_build.h b/intern/cycles/bvh/bvh_build.h
index 5733708050d..7b245139819 100644
--- a/intern/cycles/bvh/bvh_build.h
+++ b/intern/cycles/bvh/bvh_build.h
@@ -20,17 +20,17 @@
#include <float.h>
-#include "bvh/bvh.h"
-#include "bvh/bvh_binning.h"
+#include "bvh/bvh_params.h"
#include "bvh/bvh_unaligned.h"
-#include "util/util_boundbox.h"
#include "util/util_task.h"
#include "util/util_vector.h"
CCL_NAMESPACE_BEGIN
+class Boundbox;
class BVHBuildTask;
+class BVHNode;
class BVHSpatialSplitBuildTask;
class BVHParams;
class InnerNode;
diff --git a/intern/cycles/bvh/bvh_node.cpp b/intern/cycles/bvh/bvh_node.cpp
index 4f788c66797..4237c62ab5b 100644
--- a/intern/cycles/bvh/bvh_node.cpp
+++ b/intern/cycles/bvh/bvh_node.cpp
@@ -15,9 +15,10 @@
* limitations under the License.
*/
+#include "bvh/bvh_node.h"
+
#include "bvh/bvh.h"
#include "bvh/bvh_build.h"
-#include "bvh/bvh_node.h"
#include "util/util_debug.h"
#include "util/util_vector.h"
diff --git a/intern/cycles/bvh/bvh_node.h b/intern/cycles/bvh/bvh_node.h
index 60511b4b012..1c875f5a524 100644
--- a/intern/cycles/bvh/bvh_node.h
+++ b/intern/cycles/bvh/bvh_node.h
@@ -19,7 +19,6 @@
#define __BVH_NODE_H__
#include "util/util_boundbox.h"
-#include "util/util_debug.h"
#include "util/util_types.h"
CCL_NAMESPACE_BEGIN
diff --git a/intern/cycles/bvh/bvh_params.h b/intern/cycles/bvh/bvh_params.h
index 9795a7a4350..7dd699b33a4 100644
--- a/intern/cycles/bvh/bvh_params.h
+++ b/intern/cycles/bvh/bvh_params.h
@@ -246,4 +246,3 @@ struct BVHSpatialStorage {
CCL_NAMESPACE_END
#endif /* __BVH_PARAMS_H__ */
-
diff --git a/intern/cycles/bvh/bvh_sort.cpp b/intern/cycles/bvh/bvh_sort.cpp
index d29629c0279..3a01061b285 100644
--- a/intern/cycles/bvh/bvh_sort.cpp
+++ b/intern/cycles/bvh/bvh_sort.cpp
@@ -15,9 +15,10 @@
* limitations under the License.
*/
-#include "bvh/bvh_build.h"
#include "bvh/bvh_sort.h"
+#include "bvh/bvh_build.h"
+
#include "util/util_algorithm.h"
#include "util/util_debug.h"
#include "util/util_task.h"
diff --git a/intern/cycles/bvh/bvh_sort.h b/intern/cycles/bvh/bvh_sort.h
index b49ca02eb60..936401d8607 100644
--- a/intern/cycles/bvh/bvh_sort.h
+++ b/intern/cycles/bvh/bvh_sort.h
@@ -18,8 +18,11 @@
#ifndef __BVH_SORT_H__
#define __BVH_SORT_H__
+#include <cstddef>
+
CCL_NAMESPACE_BEGIN
+class BVHReference;
class BVHUnaligned;
struct Transform;
@@ -33,4 +36,3 @@ void bvh_reference_sort(int start,
CCL_NAMESPACE_END
#endif /* __BVH_SORT_H__ */
-
diff --git a/intern/cycles/bvh/bvh_split.cpp b/intern/cycles/bvh/bvh_split.cpp
index b10d69a495d..c55ba40b565 100644
--- a/intern/cycles/bvh/bvh_split.cpp
+++ b/intern/cycles/bvh/bvh_split.cpp
@@ -15,8 +15,9 @@
* limitations under the License.
*/
-#include "bvh/bvh_build.h"
#include "bvh/bvh_split.h"
+
+#include "bvh/bvh_build.h"
#include "bvh/bvh_sort.h"
#include "render/mesh.h"
diff --git a/intern/cycles/bvh/bvh_unaligned.cpp b/intern/cycles/bvh/bvh_unaligned.cpp
index ef227d20ea9..b522a8f3e10 100644
--- a/intern/cycles/bvh/bvh_unaligned.cpp
+++ b/intern/cycles/bvh/bvh_unaligned.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
#include "bvh/bvh_unaligned.h"
#include "render/mesh.h"
diff --git a/intern/cycles/bvh/bvh_unaligned.h b/intern/cycles/bvh/bvh_unaligned.h
index f41bae79e2b..c3ece051cd5 100644
--- a/intern/cycles/bvh/bvh_unaligned.h
+++ b/intern/cycles/bvh/bvh_unaligned.h
@@ -78,4 +78,3 @@ protected:
CCL_NAMESPACE_END
#endif /* __BVH_UNALIGNED_H__ */
-
diff --git a/source/blender/alembic/intern/abc_exporter.cc b/source/blender/alembic/intern/abc_exporter.cc
index 61ad76b409c..1b218b98f21 100644
--- a/source/blender/alembic/intern/abc_exporter.cc
+++ b/source/blender/alembic/intern/abc_exporter.cc
@@ -370,7 +370,9 @@ void AbcExporter::createTransformWritersFlat()
if (!export_object(&m_settings, base)) {
std::string name = get_id_name(ob);
- m_xforms[name] = new AbcTransformWriter(ob, m_writer->archive().getTop(), 0, m_trans_sampling_index, m_settings);
+ m_xforms[name] = new AbcTransformWriter(
+ ob, m_writer->archive().getTop(), NULL,
+ m_trans_sampling_index, m_settings);
}
}
}
diff --git a/source/blender/alembic/intern/abc_mesh.cc b/source/blender/alembic/intern/abc_mesh.cc
index 63c69cae8cd..a0b175dbcdc 100644
--- a/source/blender/alembic/intern/abc_mesh.cc
+++ b/source/blender/alembic/intern/abc_mesh.cc
@@ -896,19 +896,31 @@ static void *add_customdata_cb(void *user_data, const char *name, int data_type)
{
DerivedMesh *dm = static_cast<DerivedMesh *>(user_data);
CustomDataType cd_data_type = static_cast<CustomDataType>(data_type);
- void *cd_ptr = NULL;
-
- if (ELEM(cd_data_type, CD_MLOOPUV, CD_MLOOPCOL)) {
- cd_ptr = CustomData_get_layer_named(dm->getLoopDataLayout(dm), cd_data_type, name);
-
- if (cd_ptr == NULL) {
- cd_ptr = CustomData_add_layer_named(dm->getLoopDataLayout(dm),
- cd_data_type,
- CD_DEFAULT,
- NULL,
- dm->getNumLoops(dm),
- name);
- }
+ void *cd_ptr;
+ CustomData *loopdata;
+ int numloops;
+
+ /* unsupported custom data type -- don't do anything. */
+ if (!ELEM(cd_data_type, CD_MLOOPUV, CD_MLOOPCOL)) {
+ return NULL;
+ }
+
+ loopdata = dm->getLoopDataLayout(dm);
+ cd_ptr = CustomData_get_layer_named(loopdata, cd_data_type, name);
+ if (cd_ptr != NULL) {
+ /* layer already exists, so just return it. */
+ return cd_ptr;
+ }
+
+ /* create a new layer, taking care to construct the hopefully-soon-to-be-removed
+ * CD_MTEXPOLY layer too, with the same name. */
+ numloops = dm->getNumLoops(dm);
+ cd_ptr = CustomData_add_layer_named(loopdata, cd_data_type, CD_DEFAULT,
+ NULL, numloops, name);
+ if (cd_data_type == CD_MLOOPUV) {
+ CustomData_add_layer_named(dm->getPolyDataLayout(dm),
+ CD_MTEXPOLY, CD_DEFAULT,
+ NULL, numloops, name);
}
return cd_ptr;
diff --git a/source/blender/alembic/intern/abc_transform.cc b/source/blender/alembic/intern/abc_transform.cc
index cad1eae4764..2e626edeb8b 100644
--- a/source/blender/alembic/intern/abc_transform.cc
+++ b/source/blender/alembic/intern/abc_transform.cc
@@ -71,6 +71,9 @@ AbcTransformWriter::AbcTransformWriter(Object *ob,
m_xform = OXform(abc_parent, get_id_name(m_object), time_sampling);
m_schema = m_xform.getSchema();
+
+ /* Blender objects can't have a parent without inheriting the transform. */
+ m_inherits_xform = parent != NULL;
}
void AbcTransformWriter::do_write()
@@ -86,20 +89,18 @@ void AbcTransformWriter::do_write()
}
float yup_mat[4][4];
- create_transform_matrix(m_object, yup_mat);
+ create_transform_matrix(m_object, yup_mat,
+ m_inherits_xform ? ABC_MATRIX_LOCAL : ABC_MATRIX_WORLD);
/* Only apply rotation to root camera, parenting will propagate it. */
- if (m_object->type == OB_CAMERA && !has_parent_camera(m_object)) {
+ if (m_object->type == OB_CAMERA && (!m_inherits_xform || !has_parent_camera(m_object))) {
float rot_mat[4][4];
axis_angle_to_mat4_single(rot_mat, 'X', -M_PI_2);
mul_m4_m4m4(yup_mat, yup_mat, rot_mat);
}
- if (!m_object->parent) {
+ if (!m_object->parent || !m_inherits_xform) {
/* Only apply scaling to root objects, parenting will propagate it. */
- /* TODO Sybren: when we're exporting as "flat", i.e. non-hierarchial,
- * we should apply the scale even when the object has a parent
- * Blender Object. */
float scale_mat[4][4];
scale_m4_fl(scale_mat, m_settings.global_scale);
scale_mat[3][3] = m_settings.global_scale; /* also scale translation */
@@ -108,6 +109,7 @@ void AbcTransformWriter::do_write()
m_matrix = convert_matrix(yup_mat);
m_sample.setMatrix(m_matrix);
+ m_sample.setInheritsXforms(m_inherits_xform);
m_schema.set(m_sample);
}
diff --git a/source/blender/alembic/intern/abc_transform.h b/source/blender/alembic/intern/abc_transform.h
index b55fa12dadf..714adc299c1 100644
--- a/source/blender/alembic/intern/abc_transform.h
+++ b/source/blender/alembic/intern/abc_transform.h
@@ -38,6 +38,7 @@ class AbcTransformWriter : public AbcObjectWriter {
bool m_is_animated;
bool m_visible;
+ bool m_inherits_xform;
public:
AbcTransformWriter(Object *ob,
diff --git a/source/blender/alembic/intern/abc_util.cc b/source/blender/alembic/intern/abc_util.cc
index b99be718c18..4b2541c23c4 100644
--- a/source/blender/alembic/intern/abc_util.cc
+++ b/source/blender/alembic/intern/abc_util.cc
@@ -245,15 +245,12 @@ void convert_matrix(const Imath::M44d &xform, Object *ob, float r_mat[4][4])
/* Recompute transform matrix of object in new coordinate system
* (from Z-Up to Y-Up). */
-void create_transform_matrix(Object *obj, float r_yup_mat[4][4])
+void create_transform_matrix(Object *obj, float r_yup_mat[4][4], AbcMatrixMode mode)
{
float zup_mat[4][4];
- /* get local matrix. */
- /* TODO Sybren: when we're exporting as "flat", i.e. non-hierarchial,
- * we should export the world matrix even when the object has a parent
- * Blender Object. */
- if (obj->parent) {
+ /* get local or world matrix. */
+ if (mode == ABC_MATRIX_LOCAL && obj->parent) {
/* Note that this produces another matrix than the local matrix, due to
* constraints and modifiers as well as the obj->parentinv matrix. */
invert_m4_m4(obj->parent->imat, obj->parent->obmat);
diff --git a/source/blender/alembic/intern/abc_util.h b/source/blender/alembic/intern/abc_util.h
index 7217d5d5cef..9fdcf5e701b 100644
--- a/source/blender/alembic/intern/abc_util.h
+++ b/source/blender/alembic/intern/abc_util.h
@@ -57,7 +57,12 @@ std::string get_object_dag_path_name(const Object * const ob, Object *dupli_pare
bool object_selected(const Base * const ob_base);
Imath::M44d convert_matrix(float mat[4][4]);
-void create_transform_matrix(Object *obj, float r_transform_mat[4][4]);
+
+typedef enum {
+ ABC_MATRIX_WORLD = 1,
+ ABC_MATRIX_LOCAL = 2,
+} AbcMatrixMode;
+void create_transform_matrix(Object *obj, float r_transform_mat[4][4], AbcMatrixMode mode);
void split(const std::string &s, const char delim, std::vector<std::string> &tokens);
diff --git a/source/blender/blenkernel/BKE_idprop.h b/source/blender/blenkernel/BKE_idprop.h
index 5b10d7ebc06..ab8728faedb 100644
--- a/source/blender/blenkernel/BKE_idprop.h
+++ b/source/blender/blenkernel/BKE_idprop.h
@@ -60,8 +60,6 @@ typedef union IDPropertyTemplate {
IDProperty *IDP_NewIDPArray(const char *name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
IDProperty *IDP_CopyIDPArray(const IDProperty *array) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
-void IDP_FreeIDPArray(IDProperty *prop);
-
/* shallow copies item */
void IDP_SetIndexArray(struct IDProperty *prop, int index, struct IDProperty *item) ATTR_NONNULL();
struct IDProperty *IDP_GetIndexArray(struct IDProperty *prop, int index) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
@@ -81,8 +79,8 @@ void IDP_ConcatString(struct IDProperty *str1, struct IDProperty *append) ATTR_N
void IDP_FreeString(struct IDProperty *prop) ATTR_NONNULL();
/*-------- ID Type -------*/
-void IDP_LinkID(struct IDProperty *prop, ID *id);
-void IDP_UnlinkID(struct IDProperty *prop);
+
+typedef void(*IDPWalkFunc)(void *userData, IDProperty *idp);
/*-------- Group Functions -------*/
@@ -112,11 +110,12 @@ bool IDP_EqualsProperties(struct IDProperty *prop1, struct IDProperty *prop2) AT
struct IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
+void IDP_FreeProperty_ex(struct IDProperty *prop, const bool do_id_user);
void IDP_FreeProperty(struct IDProperty *prop);
void IDP_ClearProperty(IDProperty *prop);
-void IDP_UnlinkProperty(struct IDProperty *prop);
+void IDP_RelinkProperty(struct IDProperty *prop);
#define IDP_Int(prop) ((prop)->data.val)
#define IDP_Array(prop) ((prop)->data.pointer)
@@ -134,11 +133,15 @@ void IDP_UnlinkProperty(struct IDProperty *prop);
# define IDP_IDPArray(prop) _Generic((prop), \
IDProperty *: ((IDProperty *) (prop)->data.pointer), \
const IDProperty *: ((const IDProperty *) (prop)->data.pointer))
+# define IDP_Id(prop) _Generic((prop), \
+ IDProperty *: ((ID *) (prop)->data.pointer), \
+ const IDProperty *: ((const ID *) (prop)->data.pointer))
#else
# define IDP_Float(prop) (*(float *)&(prop)->data.val)
# define IDP_Double(prop) (*(double *)&(prop)->data.val)
# define IDP_String(prop) ((char *) (prop)->data.pointer)
# define IDP_IDPArray(prop) ((IDProperty *) (prop)->data.pointer)
+# define IDP_Id(prop) ((ID *) (prop)->data.pointer)
#endif
#ifndef NDEBUG
diff --git a/source/blender/blenkernel/BKE_library.h b/source/blender/blenkernel/BKE_library.h
index 72ae2cf4efa..6649cfbb585 100644
--- a/source/blender/blenkernel/BKE_library.h
+++ b/source/blender/blenkernel/BKE_library.h
@@ -66,7 +66,7 @@ struct ID *BKE_libblock_find_name(const short type, const char *name) ATTR_WARN_
void BKE_libblock_free(struct Main *bmain, void *idv) ATTR_NONNULL();
void BKE_libblock_free_ex(struct Main *bmain, void *idv, const bool do_id_user, const bool do_ui_user) ATTR_NONNULL();
void BKE_libblock_free_us(struct Main *bmain, void *idv) ATTR_NONNULL();
-void BKE_libblock_free_data(struct Main *bmain, struct ID *id) ATTR_NONNULL();
+void BKE_libblock_free_data(struct Main *bmain, struct ID *id, const bool do_id_user) ATTR_NONNULL();
void BKE_libblock_delete(struct Main *bmain, void *idv) ATTR_NONNULL();
void BKE_id_lib_local_paths(struct Main *bmain, struct Library *lib, struct ID *id);
diff --git a/source/blender/blenkernel/BKE_library_query.h b/source/blender/blenkernel/BKE_library_query.h
index 1258e2fa72e..68bf9f43fe1 100644
--- a/source/blender/blenkernel/BKE_library_query.h
+++ b/source/blender/blenkernel/BKE_library_query.h
@@ -87,7 +87,7 @@ void BKE_library_update_ID_link_user(struct ID *id_dst, struct ID *id_src, const
int BKE_library_ID_use_ID(struct ID *id_user, struct ID *id_used);
-bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id_type_used);
+bool BKE_library_id_can_use_idtype(struct ID *id_owner, const short id_type_used);
bool BKE_library_ID_is_locally_used(struct Main *bmain, void *idv);
bool BKE_library_ID_is_indirectly_used(struct Main *bmain, void *idv);
diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c
index 7c79e13cb01..78a5273f0d0 100644
--- a/source/blender/blenkernel/intern/idprop.c
+++ b/source/blender/blenkernel/intern/idprop.c
@@ -117,14 +117,14 @@ IDProperty *IDP_CopyIDPArray(const IDProperty *array)
return narray;
}
-void IDP_FreeIDPArray(IDProperty *prop)
+static void IDP_FreeIDPArray(IDProperty *prop, const bool do_id_user)
{
int i;
BLI_assert(prop->type == IDP_IDPARRAY);
for (i = 0; i < prop->len; i++)
- IDP_FreeProperty(GETPROP(prop, i));
+ IDP_FreeProperty_ex(GETPROP(prop, i), do_id_user);
if (prop->data.pointer)
MEM_freeN(prop->data.pointer);
@@ -437,22 +437,24 @@ void IDP_FreeString(IDProperty *prop)
/* -------------------------------------------------------------------- */
-/* ID Type (not in use yet) */
+/* ID Type */
-/** \name IDProperty ID API (unused)
+/** \name IDProperty ID API
* \{ */
-void IDP_LinkID(IDProperty *prop, ID *id)
-{
- if (prop->data.pointer)
- id_us_min(((ID *)prop->data.pointer));
- prop->data.pointer = id;
- id_us_plus(id);
-}
-void IDP_UnlinkID(IDProperty *prop)
+static IDProperty *IDP_CopyID(const IDProperty *prop)
{
- id_us_min(((ID *)prop->data.pointer));
+ IDProperty *newp;
+
+ BLI_assert(prop->type == IDP_ID);
+ newp = idp_generic_copy(prop);
+
+ newp->data.pointer = prop->data.pointer;
+ id_us_plus(IDP_Id(newp));
+
+ return newp;
}
+
/** \} */
@@ -711,13 +713,13 @@ IDProperty *IDP_GetPropertyTypeFromGroup(IDProperty *prop, const char *name, con
* This is because all ID Property freeing functions free only direct data (not the ID Property
* struct itself), but for Groups the child properties *are* considered
* direct data. */
-static void IDP_FreeGroup(IDProperty *prop)
+static void IDP_FreeGroup(IDProperty *prop, const bool do_id_user)
{
IDProperty *loop;
BLI_assert(prop->type == IDP_GROUP);
for (loop = prop->data.group.first; loop; loop = loop->next) {
- IDP_FreeProperty(loop);
+ IDP_FreeProperty_ex(loop, do_id_user);
}
BLI_freelistN(&prop->data.group);
}
@@ -734,12 +736,51 @@ IDProperty *IDP_CopyProperty(const IDProperty *prop)
switch (prop->type) {
case IDP_GROUP: return IDP_CopyGroup(prop);
case IDP_STRING: return IDP_CopyString(prop);
+ case IDP_ID: return IDP_CopyID(prop);
case IDP_ARRAY: return IDP_CopyArray(prop);
case IDP_IDPARRAY: return IDP_CopyIDPArray(prop);
default: return idp_generic_copy(prop);
}
}
+/* Updates ID pointers after an object has been copied */
+/* TODO Nuke this once its only user has been correctly converted to use generic ID management from BKE_library! */
+void IDP_RelinkProperty(struct IDProperty *prop)
+{
+ if (!prop)
+ return;
+
+ switch (prop->type) {
+ case IDP_GROUP:
+ {
+ for (IDProperty *loop = prop->data.group.first; loop; loop = loop->next) {
+ IDP_RelinkProperty(loop);
+ }
+ break;
+ }
+ case IDP_IDPARRAY:
+ {
+ IDProperty *idp_array = IDP_Array(prop);
+ for (int i = 0; i < prop->len; i++) {
+ IDP_RelinkProperty(&idp_array[i]);
+ }
+ break;
+ }
+ case IDP_ID:
+ {
+ ID *id = IDP_Id(prop);
+ if (id && id->newid) {
+ id_us_min(IDP_Id(prop));
+ prop->data.pointer = id->newid;
+ id_us_plus(IDP_Id(prop));
+ }
+ break;
+ }
+ default:
+ break; /* Nothing to do for other IDProp types. */
+ }
+}
+
/**
* Get the Group property that contains the id properties for ID id. Set create_if_needed
* to create the Group property and attach it to id if it doesn't exist; otherwise
@@ -836,6 +877,8 @@ bool IDP_EqualsProperties_ex(IDProperty *prop1, IDProperty *prop2, const bool is
return false;
return true;
}
+ case IDP_ID:
+ return (IDP_Id(prop1) == IDP_Id(prop2));
default:
/* should never get here */
BLI_assert(0);
@@ -911,6 +954,7 @@ IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char *
prop->len = prop->totallen = val->array.len;
break;
}
+ printf("%s: bad array type.\n",__func__);
return NULL;
}
case IDP_STRING:
@@ -957,6 +1001,14 @@ IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char *
/* heh I think all needed values are set properly by calloc anyway :) */
break;
}
+ case IDP_ID:
+ {
+ prop = MEM_callocN(sizeof(IDProperty), "IDProperty datablock");
+ prop->data.pointer = (void *)val->id;
+ prop->type = IDP_ID;
+ id_us_plus(IDP_Id(prop));
+ break;
+ }
default:
{
prop = MEM_callocN(sizeof(IDProperty), "IDProperty array");
@@ -971,11 +1023,10 @@ IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char *
}
/**
- * \note this will free all child properties of list arrays and groups!
- * Also, note that this does NOT unlink anything! Plus it doesn't free
- * the actual struct IDProperty struct either.
+ * \note This will free allocated data, all child properties of arrays and groups, and unlink IDs!
+ * But it does not free the actual IDProperty struct itself.
*/
-void IDP_FreeProperty(IDProperty *prop)
+void IDP_FreeProperty_ex(IDProperty *prop, const bool do_id_user)
{
switch (prop->type) {
case IDP_ARRAY:
@@ -985,14 +1036,24 @@ void IDP_FreeProperty(IDProperty *prop)
IDP_FreeString(prop);
break;
case IDP_GROUP:
- IDP_FreeGroup(prop);
+ IDP_FreeGroup(prop, do_id_user);
break;
case IDP_IDPARRAY:
- IDP_FreeIDPArray(prop);
+ IDP_FreeIDPArray(prop, do_id_user);
+ break;
+ case IDP_ID:
+ if (do_id_user) {
+ id_us_min(IDP_Id(prop));
+ }
break;
}
}
+void IDP_FreeProperty(IDProperty *prop)
+{
+ IDP_FreeProperty_ex(prop, true);
+}
+
void IDP_ClearProperty(IDProperty *prop)
{
IDP_FreeProperty(prop);
@@ -1000,18 +1061,4 @@ void IDP_ClearProperty(IDProperty *prop)
prop->len = prop->totallen = 0;
}
-/**
- * Unlinks any struct IDProperty<->ID linkage that might be going on.
- *
- * \note currently unused
- */
-void IDP_UnlinkProperty(IDProperty *prop)
-{
- switch (prop->type) {
- case IDP_ID:
- IDP_UnlinkID(prop);
- break;
- }
-}
-
/** \} */
diff --git a/source/blender/blenkernel/intern/library_query.c b/source/blender/blenkernel/intern/library_query.c
index c9853f791ce..c6cb336382e 100644
--- a/source/blender/blenkernel/intern/library_query.c
+++ b/source/blender/blenkernel/intern/library_query.c
@@ -33,6 +33,7 @@
#include "DNA_actuator_types.h"
#include "DNA_anim_types.h"
+#include "DNA_armature_types.h"
#include "DNA_brush_types.h"
#include "DNA_camera_types.h"
#include "DNA_constraint_types.h"
@@ -71,10 +72,12 @@
#include "BKE_collection.h"
#include "BKE_constraint.h"
#include "BKE_fcurve.h"
+#include "BKE_idprop.h"
#include "BKE_library.h"
#include "BKE_library_query.h"
#include "BKE_main.h"
#include "BKE_modifier.h"
+#include "BKE_node.h"
#include "BKE_particle.h"
#include "BKE_rigidbody.h"
#include "BKE_sca.h"
@@ -83,7 +86,9 @@
#define FOREACH_FINALIZE _finalize
-#define FOREACH_FINALIZE_VOID FOREACH_FINALIZE: (void)0
+#define FOREACH_FINALIZE_VOID \
+ if (0) { goto FOREACH_FINALIZE; } \
+ FOREACH_FINALIZE: ((void)0)
#define FOREACH_CALLBACK_INVOKE_ID_PP(_data, id_pp, _cb_flag) \
CHECK_TYPE(id_pp, ID **); \
@@ -141,6 +146,37 @@ typedef struct LibraryForeachIDData {
BLI_LINKSTACK_DECLARE(ids_todo, ID *);
} LibraryForeachIDData;
+static void library_foreach_idproperty_ID_link(LibraryForeachIDData *data, IDProperty *prop, int flag)
+{
+ if (!prop)
+ return;
+
+ switch (prop->type) {
+ case IDP_GROUP:
+ {
+ for (IDProperty *loop = prop->data.group.first; loop; loop = loop->next) {
+ library_foreach_idproperty_ID_link(data, loop, flag);
+ }
+ break;
+ }
+ case IDP_IDPARRAY:
+ {
+ IDProperty *loop = IDP_Array(prop);
+ for (int i = 0; i < prop->len; i++) {
+ library_foreach_idproperty_ID_link(data, &loop[i], flag);
+ }
+ break;
+ }
+ case IDP_ID:
+ FOREACH_CALLBACK_INVOKE_ID(data, prop->data.pointer, flag);
+ break;
+ default:
+ break; /* Nothing to do here with other types of IDProperties... */
+ }
+
+ FOREACH_FINALIZE_VOID;
+}
+
static void library_foreach_rigidbodyworldSceneLooper(
struct RigidBodyWorld *UNUSED(rbw), ID **id_pointer, void *user_data, int cb_flag)
{
@@ -266,6 +302,17 @@ static void library_foreach_paint(LibraryForeachIDData *data, Paint *paint)
FOREACH_FINALIZE_VOID;
}
+static void library_foreach_bone(LibraryForeachIDData *data, Bone *bone)
+{
+ library_foreach_idproperty_ID_link(data, bone->prop, IDWALK_CB_USER);
+
+ for (Bone *curbone = bone->childbase.first; curbone; curbone = curbone->next) {
+ library_foreach_bone(data, curbone);
+ }
+
+ FOREACH_FINALIZE_VOID;
+}
+
static void library_foreach_ID_as_subdata_link(
ID **id_pp, LibraryIDLinkCallback callback, void *user_data, int flag, LibraryForeachIDData *data)
{
@@ -338,6 +385,8 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call
continue;
}
+ library_foreach_idproperty_ID_link(&data, id->properties, IDWALK_CB_USER);
+
AnimData *adt = BKE_animdata_from_id(id);
if (adt) {
library_foreach_animationData(&data, adt);
@@ -403,6 +452,7 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call
CALLBACK_INVOKE(seq->clip, IDWALK_CB_USER);
CALLBACK_INVOKE(seq->mask, IDWALK_CB_USER);
CALLBACK_INVOKE(seq->sound, IDWALK_CB_USER);
+ library_foreach_idproperty_ID_link(&data, seq->prop, IDWALK_CB_USER);
for (SequenceModifierData *smd = seq->modifiers.first; smd; smd = smd->next) {
CALLBACK_INVOKE(smd->mask_id, IDWALK_CB_USER);
}
@@ -535,6 +585,7 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call
data.cb_flag |= proxy_cb_flag;
for (pchan = object->pose->chanbase.first; pchan; pchan = pchan->next) {
+ library_foreach_idproperty_ID_link(&data, pchan->prop, IDWALK_CB_USER);
CALLBACK_INVOKE(pchan->custom, IDWALK_CB_USER);
BKE_constraints_id_loop(&pchan->constraints, library_foreach_constraintObjectLooper, &data);
}
@@ -574,6 +625,16 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call
break;
}
+ case ID_AR:
+ {
+ bArmature *arm = (bArmature *)id;
+
+ for (Bone *bone = arm->bonebase.first; bone; bone = bone->next) {
+ library_foreach_bone(&data, bone);
+ }
+ break;
+ }
+
case ID_ME:
{
Mesh *mesh = (Mesh *) id;
@@ -756,9 +817,27 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call
{
bNodeTree *ntree = (bNodeTree *) id;
bNode *node;
+ bNodeSocket *sock;
+
CALLBACK_INVOKE(ntree->gpd, IDWALK_CB_USER);
+
for (node = ntree->nodes.first; node; node = node->next) {
CALLBACK_INVOKE_ID(node->id, IDWALK_CB_USER);
+
+ library_foreach_idproperty_ID_link(&data, node->prop, IDWALK_CB_USER);
+ for (sock = node->inputs.first; sock; sock = sock->next) {
+ library_foreach_idproperty_ID_link(&data, sock->prop, IDWALK_CB_USER);
+ }
+ for (sock = node->outputs.first; sock; sock = sock->next) {
+ library_foreach_idproperty_ID_link(&data, sock->prop, IDWALK_CB_USER);
+ }
+ }
+
+ for (sock = ntree->inputs.first; sock; sock = sock->next) {
+ library_foreach_idproperty_ID_link(&data, sock->prop, IDWALK_CB_USER);
+ }
+ for (sock = ntree->outputs.first; sock; sock = sock->next) {
+ library_foreach_idproperty_ID_link(&data, sock->prop, IDWALK_CB_USER);
}
break;
}
@@ -918,7 +997,6 @@ void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback call
case ID_VF:
case ID_TXT:
case ID_SO:
- case ID_AR:
case ID_GD:
case ID_WM:
case ID_PAL:
@@ -968,9 +1046,25 @@ void BKE_library_update_ID_link_user(ID *id_dst, ID *id_src, const int cb_flag)
*/
/* XXX This has to be fully rethink, basing check on ID type is not really working anymore (and even worth once
* IDProps will support ID pointers), we'll have to do some quick checks on IDs themselves... */
-bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id_type_used)
+bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used)
{
- if (id_type_can_have_animdata(id_type_owner)) {
+ /* any type of ID can be used in custom props. */
+ if (id_owner->properties) {
+ return true;
+ }
+
+ const short id_type_owner = GS(id_owner->name);
+
+ /* IDProps of armature bones and nodes, and bNode->id can use virtually any type of ID. */
+ if (ELEM(id_type_owner, ID_NT, ID_AR)) {
+ return true;
+ }
+
+ if (ntreeFromID(id_owner)) {
+ return true;
+ }
+
+ if (BKE_animdata_from_id(id_owner)) {
return true; /* AnimationData can use virtually any kind of datablocks, through drivers especially. */
}
@@ -979,8 +1073,7 @@ bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id
return ELEM(id_type_used, ID_LI);
case ID_SCE:
return (ELEM(id_type_used, ID_OB, ID_WO, ID_SCE, ID_MC, ID_MA, ID_GR, ID_TXT,
- ID_LS, ID_MSK, ID_SO, ID_GD, ID_BR, ID_PAL, ID_IM, ID_NT) ||
- BKE_library_idtype_can_use_idtype(ID_NT, id_type_used));
+ ID_LS, ID_MSK, ID_SO, ID_GD, ID_BR, ID_PAL, ID_IM, ID_NT));
case ID_OB:
/* Could be the following, but simpler to just always say 'yes' here. */
#if 0
@@ -997,13 +1090,13 @@ bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id
case ID_MB:
return ELEM(id_type_used, ID_MA);
case ID_MA:
- return (ELEM(id_type_used, ID_TE, ID_GR) || BKE_library_idtype_can_use_idtype(ID_NT, id_type_used));
+ return (ELEM(id_type_used, ID_TE, ID_GR));
case ID_TE:
- return (ELEM(id_type_used, ID_IM, ID_OB) || BKE_library_idtype_can_use_idtype(ID_NT, id_type_used));
+ return (ELEM(id_type_used, ID_IM, ID_OB));
case ID_LT:
return ELEM(id_type_used, ID_KE);
case ID_LA:
- return (ELEM(id_type_used, ID_TE) || BKE_library_idtype_can_use_idtype(ID_NT, id_type_used));
+ return (ELEM(id_type_used, ID_TE));
case ID_CA:
return ELEM(id_type_used, ID_OB);
case ID_KE:
@@ -1011,7 +1104,7 @@ bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id
case ID_SCR:
return ELEM(id_type_used, ID_SCE);
case ID_WO:
- return (ELEM(id_type_used, ID_TE) || BKE_library_idtype_can_use_idtype(ID_NT, id_type_used));
+ return (ELEM(id_type_used, ID_TE));
case ID_SPK:
return ELEM(id_type_used, ID_SO);
case ID_GR:
@@ -1032,7 +1125,7 @@ bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id
case ID_MSK:
return ELEM(id_type_used, ID_MC); /* WARNING! mask->parent.id, not typed. */
case ID_LS:
- return (ELEM(id_type_used, ID_TE, ID_OB) || BKE_library_idtype_can_use_idtype(ID_NT, id_type_used));
+ return (ELEM(id_type_used, ID_TE, ID_OB));
case ID_IM:
case ID_VF:
case ID_TXT:
@@ -1138,7 +1231,7 @@ static bool library_ID_is_used(Main *bmain, void *idv, const bool check_linked)
while (i-- && !is_defined) {
ID *id_curr = lb_array[i]->first;
- if (!id_curr || !BKE_library_idtype_can_use_idtype(GS(id_curr->name), GS(id->name))) {
+ if (!id_curr || !BKE_library_id_can_use_idtype(id_curr, GS(id->name))) {
continue;
}
@@ -1190,7 +1283,7 @@ void BKE_library_ID_test_usages(Main *bmain, void *idv, bool *is_used_local, boo
while (i-- && !is_defined) {
ID *id_curr = lb_array[i]->first;
- if (!id_curr || !BKE_library_idtype_can_use_idtype(GS(id_curr->name), GS(id->name))) {
+ if (!id_curr || !BKE_library_id_can_use_idtype(id_curr, GS(id->name))) {
continue;
}
diff --git a/source/blender/blenkernel/intern/library_remap.c b/source/blender/blenkernel/intern/library_remap.c
index 1f146ece690..255904841b2 100644
--- a/source/blender/blenkernel/intern/library_remap.c
+++ b/source/blender/blenkernel/intern/library_remap.c
@@ -472,20 +472,16 @@ ATTR_NONNULL(1) static void libblock_remap_data(
* objects actually using given old_id... sounds rather unlikely currently, though, so this will do for now. */
while (i--) {
- ID *id_curr = lb_array[i]->first;
-
- if (!id_curr || !BKE_library_idtype_can_use_idtype(GS(id_curr->name), GS(old_id->name))) {
- continue;
- }
-
- for (; id_curr; id_curr = id_curr->next) {
- /* Note that we cannot skip indirect usages of old_id here (if requested), we still need to check it for
- * the user count handling...
- * XXX No more true (except for debug usage of those skipping counters). */
- r_id_remap_data->id = id_curr;
- libblock_remap_data_preprocess(r_id_remap_data);
- BKE_library_foreach_ID_link(
- NULL, id_curr, foreach_libblock_remap_callback, (void *)r_id_remap_data, IDWALK_NOP);
+ for (ID *id_curr = lb_array[i]->first; id_curr; id_curr = id_curr->next) {
+ if (BKE_library_id_can_use_idtype(id_curr, GS(old_id->name))) {
+ /* Note that we cannot skip indirect usages of old_id here (if requested), we still need to check it for
+ * the user count handling...
+ * XXX No more true (except for debug usage of those skipping counters). */
+ r_id_remap_data->id = id_curr;
+ libblock_remap_data_preprocess(r_id_remap_data);
+ BKE_library_foreach_ID_link(
+ NULL, id_curr, foreach_libblock_remap_callback, (void *)r_id_remap_data, IDWALK_NOP);
+ }
}
}
}
@@ -747,10 +743,10 @@ void BKE_libblock_relink_to_newid(ID *id)
BKE_library_foreach_ID_link(NULL, id, id_relink_to_newid_looper, NULL, 0);
}
-void BKE_libblock_free_data(Main *UNUSED(bmain), ID *id)
+void BKE_libblock_free_data(Main *UNUSED(bmain), ID *id, const bool do_id_user)
{
if (id->properties) {
- IDP_FreeProperty(id->properties);
+ IDP_FreeProperty_ex(id->properties, do_id_user);
MEM_freeN(id->properties);
}
}
@@ -900,7 +896,7 @@ void BKE_libblock_free_ex(Main *bmain, void *idv, const bool do_id_user, const b
BLI_remlink(lb, id);
- BKE_libblock_free_data(bmain, id);
+ BKE_libblock_free_data(bmain, id, do_id_user);
BKE_main_unlock(bmain);
MEM_freeN(id);
diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c
index 05422a01dbf..60c48bb5dd3 100644
--- a/source/blender/blenkernel/intern/node.c
+++ b/source/blender/blenkernel/intern/node.c
@@ -1828,7 +1828,7 @@ void ntreeFreeTree(bNodeTree *ntree)
if (tntree == ntree)
break;
if (tntree == NULL) {
- BKE_libblock_free_data(G.main, &ntree->id);
+ BKE_libblock_free_data(G.main, &ntree->id, true);
}
}
diff --git a/source/blender/blenlib/intern/task.c b/source/blender/blenlib/intern/task.c
index 297d0f0b310..c92881bb741 100644
--- a/source/blender/blenlib/intern/task.c
+++ b/source/blender/blenlib/intern/task.c
@@ -162,6 +162,13 @@ struct TaskPool {
*/
int thread_id;
+ /* For the pools which are created from non-main thread which is not a
+ * scheduler worker thread we can't re-use any of scheduler's threads TLS
+ * and have to use our own one.
+ */
+ bool use_local_tls;
+ TaskThreadLocalStorage local_tls;
+
#ifdef DEBUG_STATS
TaskMemPoolStats *mempool_stats;
#endif
@@ -202,12 +209,21 @@ BLI_INLINE void task_data_free(Task *task, const int thread_id)
}
}
+BLI_INLINE void initialize_task_tls(TaskThreadLocalStorage *tls)
+{
+ memset(tls, 0, sizeof(TaskThreadLocalStorage));
+}
+
BLI_INLINE TaskThreadLocalStorage *get_task_tls(TaskPool *pool,
const int thread_id)
{
TaskScheduler *scheduler = pool->scheduler;
BLI_assert(thread_id >= 0);
BLI_assert(thread_id <= scheduler->num_threads);
+ if (pool->use_local_tls) {
+ BLI_assert(pool->thread_id == 0);
+ return &pool->local_tls;
+ }
if (thread_id == 0) {
return &scheduler->task_threads[pool->thread_id].tls;
}
@@ -424,9 +440,12 @@ TaskScheduler *BLI_task_scheduler_create(int num_threads)
num_threads = 1;
}
- scheduler->task_threads = MEM_callocN(sizeof(TaskThread) * (num_threads + 1),
+ scheduler->task_threads = MEM_mallocN(sizeof(TaskThread) * (num_threads + 1),
"TaskScheduler task threads");
+ /* Initialize TLS for main thread. */
+ initialize_task_tls(&scheduler->task_threads[0].tls);
+
pthread_key_create(&scheduler->tls_id_key, NULL);
/* launch threads that will be waiting for work */
@@ -440,6 +459,7 @@ TaskScheduler *BLI_task_scheduler_create(int num_threads)
TaskThread *thread = &scheduler->task_threads[i + 1];
thread->scheduler = scheduler;
thread->id = i + 1;
+ initialize_task_tls(&thread->tls);
if (pthread_create(&scheduler->threads[i], NULL, task_scheduler_thread_run, thread) != 0) {
fprintf(stderr, "TaskScheduler failed to launch thread %d/%d\n", i, num_threads);
@@ -572,6 +592,7 @@ static TaskPool *task_pool_create_ex(TaskScheduler *scheduler,
pool->num_suspended = 0;
pool->suspended_queue.first = pool->suspended_queue.last = NULL;
pool->run_in_background = is_background;
+ pool->use_local_tls = false;
BLI_mutex_init(&pool->num_mutex);
BLI_condition_init(&pool->num_cond);
@@ -584,13 +605,15 @@ static TaskPool *task_pool_create_ex(TaskScheduler *scheduler,
}
else {
TaskThread *thread = pthread_getspecific(scheduler->tls_id_key);
- /* NOTE: It is possible that pool is created from non-main thread
- * which isn't a scheduler thread. In this case pthread's TLS will
- * be NULL and we can safely consider thread id 0 for the main
- * thread of this pool (the one which does wort_and_wait()).
- */
if (thread == NULL) {
+ /* NOTE: Task pool is created from non-main thread which is not
+ * managed by the task scheduler. We identify ourselves as thread ID
+ * 0 but we do not use scheduler's TLS storage and use our own
+ * instead to avoid any possible threading conflicts.
+ */
pool->thread_id = 0;
+ pool->use_local_tls = true;
+ initialize_task_tls(&pool->local_tls);
}
else {
pool->thread_id = thread->id;
@@ -670,6 +693,10 @@ void BLI_task_pool_free(TaskPool *pool)
MEM_freeN(pool->mempool_stats);
#endif
+ if (pool->use_local_tls) {
+ free_task_tls(&pool->local_tls);
+ }
+
MEM_freeN(pool);
BLI_end_threaded_malloc();
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 08f2c56e0ad..c81d591af59 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -130,6 +130,7 @@
#include "BKE_library_idmap.h"
#include "BKE_library_query.h"
#include "BKE_idcode.h"
+#include "BKE_idprop.h"
#include "BKE_material.h"
#include "BKE_main.h" // for Main
#include "BKE_mesh.h" // for ME_ defines (patching)
@@ -2004,7 +2005,7 @@ static void test_pointer_array(FileData *fd, void **mat)
/* ************ READ ID Properties *************** */
static void IDP_DirectLinkProperty(IDProperty *prop, int switch_endian, FileData *fd);
-static void IDP_LibLinkProperty(IDProperty *prop, int switch_endian, FileData *fd);
+static void IDP_LibLinkProperty(IDProperty *prop, FileData *fd);
static void IDP_DirectLinkIDPArray(IDProperty *prop, int switch_endian, FileData *fd)
{
@@ -2135,10 +2136,39 @@ static void _IDP_DirectLinkGroup_OrFree(IDProperty **prop, int switch_endian, Fi
}
}
-/* stub function */
-static void IDP_LibLinkProperty(IDProperty *UNUSED(prop), int UNUSED(switch_endian), FileData *UNUSED(fd))
+static void IDP_LibLinkProperty(IDProperty *prop, FileData *fd)
{
- /* Should we do something here, prop should be ensured to be non-NULL first... */
+ if (!prop)
+ return;
+
+ switch (prop->type) {
+ case IDP_ID: /* PointerProperty */
+ {
+ void *newaddr = newlibadr_us(fd, NULL, IDP_Id(prop));
+ if (IDP_Id(prop) && !newaddr && G.debug) {
+ printf("Error while loading \"%s\". Data not found in file!\n", prop->name);
+ }
+ prop->data.pointer = newaddr;
+ break;
+ }
+ case IDP_IDPARRAY: /* CollectionProperty */
+ {
+ IDProperty *idp_array = IDP_IDPArray(prop);
+ for (int i = 0; i < prop->len; i++) {
+ IDP_LibLinkProperty(&(idp_array[i]), fd);
+ }
+ break;
+ }
+ case IDP_GROUP: /* PointerProperty */
+ {
+ for (IDProperty *loop = prop->data.group.first; loop; loop = loop->next) {
+ IDP_LibLinkProperty(loop, fd);
+ }
+ break;
+ }
+ default:
+ break; /* Nothing to do for other IDProps. */
+ }
}
/* ************ READ IMAGE PREVIEW *************** */
@@ -2198,7 +2228,7 @@ static void lib_link_brush(FileData *fd, Main *main)
/* only link ID pointers */
for (Brush *brush = main->brush.first; brush; brush = brush->id.next) {
if (brush->id.tag & LIB_TAG_NEED_LINK) {
- IDP_LibLinkProperty(brush->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(brush->id.properties, fd);
/* brush->(mask_)mtex.obj is ignored on purpose? */
brush->mtex.tex = newlibadr_us(fd, brush->id.lib, brush->mtex.tex);
@@ -2235,7 +2265,7 @@ static void lib_link_palette(FileData *fd, Main *main)
/* only link ID pointers */
for (Palette *palette = main->palettes.first; palette; palette = palette->id.next) {
if (palette->id.tag & LIB_TAG_NEED_LINK) {
- IDP_LibLinkProperty(palette->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(palette->id.properties, fd);
palette->id.tag &= ~LIB_TAG_NEED_LINK;
}
@@ -2253,7 +2283,7 @@ static void lib_link_paint_curve(FileData *fd, Main *main)
/* only link ID pointers */
for (PaintCurve *pc = main->paintcurves.first; pc; pc = pc->id.next) {
if (pc->id.tag & LIB_TAG_NEED_LINK) {
- IDP_LibLinkProperty(pc->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(pc->id.properties, fd);
pc->id.tag &= ~LIB_TAG_NEED_LINK;
}
@@ -2508,7 +2538,7 @@ static void lib_link_action(FileData *fd, Main *main)
{
for (bAction *act = main->action.first; act; act = act->id.next) {
if (act->id.tag & LIB_TAG_NEED_LINK) {
- IDP_LibLinkProperty(act->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(act->id.properties, fd);
// XXX deprecated - old animation system <<<
for (bActionChannel *chan = act->chanbase.first; chan; chan = chan->next) {
@@ -2715,7 +2745,7 @@ static void lib_link_cachefiles(FileData *fd, Main *bmain)
/* only link ID pointers */
for (CacheFile *cache_file = bmain->cachefiles.first; cache_file; cache_file = cache_file->id.next) {
if (cache_file->id.tag & LIB_TAG_NEED_LINK) {
- IDP_LibLinkProperty(cache_file->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(cache_file->id.properties, fd);
lib_link_animdata(fd, &cache_file->id, cache_file->adt);
cache_file->id.tag &= ~LIB_TAG_NEED_LINK;
@@ -2749,20 +2779,13 @@ static void direct_link_motionpath(FileData *fd, bMotionPath *mpath)
/* ************ READ NODE TREE *************** */
-static void lib_link_node_socket(FileData *fd, ID *UNUSED(id), bNodeSocket *sock)
-{
- /* Link ID Properties -- and copy this comment EXACTLY for easy finding
- * of library blocks that implement this.*/
- IDP_LibLinkProperty(sock->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
-}
-
/* Single node tree (also used for material/scene trees), ntree is not NULL */
static void lib_link_ntree(FileData *fd, ID *id, bNodeTree *ntree)
{
bNode *node;
bNodeSocket *sock;
- IDP_LibLinkProperty(ntree->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(ntree->id.properties, fd);
lib_link_animdata(fd, &ntree->id, ntree->adt);
ntree->gpd = newlibadr_us(fd, id->lib, ntree->gpd);
@@ -2770,27 +2793,23 @@ static void lib_link_ntree(FileData *fd, ID *id, bNodeTree *ntree)
for (node = ntree->nodes.first; node; node = node->next) {
/* Link ID Properties -- and copy this comment EXACTLY for easy finding
* of library blocks that implement this.*/
- IDP_LibLinkProperty(node->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(node->prop, fd);
node->id = newlibadr_us(fd, id->lib, node->id);
for (sock = node->inputs.first; sock; sock = sock->next) {
- IDP_LibLinkProperty(sock->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
- lib_link_node_socket(fd, id, sock);
+ IDP_LibLinkProperty(sock->prop, fd);
}
for (sock = node->outputs.first; sock; sock = sock->next) {
- IDP_LibLinkProperty(sock->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
- lib_link_node_socket(fd, id, sock);
+ IDP_LibLinkProperty(sock->prop, fd);
}
}
for (sock = ntree->inputs.first; sock; sock = sock->next) {
- IDP_LibLinkProperty(sock->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
- lib_link_node_socket(fd, id, sock);
+ IDP_LibLinkProperty(sock->prop, fd);
}
for (sock = ntree->outputs.first; sock; sock = sock->next) {
- IDP_LibLinkProperty(sock->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
- lib_link_node_socket(fd, id, sock);
+ IDP_LibLinkProperty(sock->prop, fd);
}
}
@@ -3295,6 +3314,8 @@ static void lib_link_pose(FileData *fd, Main *bmain, Object *ob, bPose *pose)
pchan->bone = BLI_ghash_lookup(bone_hash, pchan->name);
+ IDP_LibLinkProperty(pchan->prop, fd);
+
pchan->custom = newlibadr_us(fd, arm->id.lib, pchan->custom);
if (UNLIKELY(pchan->bone == NULL)) {
rebuild = true;
@@ -3315,13 +3336,26 @@ static void lib_link_pose(FileData *fd, Main *bmain, Object *ob, bPose *pose)
}
}
+static void lib_link_bones(FileData *fd, Bone *bone)
+{
+ IDP_LibLinkProperty(bone->prop, fd);
+
+ for (Bone *curbone = bone->childbase.first; curbone; curbone = curbone->next) {
+ lib_link_bones(fd, curbone);
+ }
+}
+
static void lib_link_armature(FileData *fd, Main *main)
{
for (bArmature *arm = main->armature.first; arm; arm = arm->id.next) {
if (arm->id.tag & LIB_TAG_NEED_LINK) {
- IDP_LibLinkProperty(arm->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(arm->id.properties, fd);
lib_link_animdata(fd, &arm->id, arm->adt);
+ for (Bone *curbone = arm->bonebase.first; curbone; curbone = curbone->next) {
+ lib_link_bones(fd, curbone);
+ }
+
arm->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
@@ -3368,7 +3402,7 @@ static void lib_link_camera(FileData *fd, Main *main)
{
for (Camera *ca = main->camera.first; ca; ca = ca->id.next) {
if (ca->id.tag & LIB_TAG_NEED_LINK) {
- IDP_LibLinkProperty(ca->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(ca->id.properties, fd);
lib_link_animdata(fd, &ca->id, ca->adt);
ca->ipo = newlibadr_us(fd, ca->id.lib, ca->ipo); // XXX deprecated - old animation system
@@ -3393,7 +3427,7 @@ static void lib_link_lamp(FileData *fd, Main *main)
{
for (Lamp *la = main->lamp.first; la; la = la->id.next) {
if (la->id.tag & LIB_TAG_NEED_LINK) {
- IDP_LibLinkProperty(la->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(la->id.properties, fd);
lib_link_animdata(fd, &la->id, la->adt);
for (int a = 0; a < MAX_MTEX; a++) {
@@ -3458,7 +3492,7 @@ static void lib_link_key(FileData *fd, Main *main)
BLI_assert((key->id.tag & LIB_TAG_EXTERN) == 0);
if (key->id.tag & LIB_TAG_NEED_LINK) {
- IDP_LibLinkProperty(key->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(key->id.properties, fd);
lib_link_animdata(fd, &key->id, key->adt);
key->ipo = newlibadr_us(fd, key->id.lib, key->ipo); // XXX deprecated - old animation system
@@ -3523,7 +3557,7 @@ static void lib_link_mball(FileData *fd, Main *main)
{
for (MetaBall *mb = main->mball.first; mb; mb = mb->id.next) {
if (mb->id.tag & LIB_TAG_NEED_LINK) {
- IDP_LibLinkProperty(mb->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(mb->id.properties, fd);
lib_link_animdata(fd, &mb->id, mb->adt);
for (int a = 0; a < mb->totcol; a++) {
@@ -3559,7 +3593,7 @@ static void lib_link_world(FileData *fd, Main *main)
{
for (World *wrld = main->world.first; wrld; wrld = wrld->id.next) {
if (wrld->id.tag & LIB_TAG_NEED_LINK) {
- IDP_LibLinkProperty(wrld->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(wrld->id.properties, fd);
lib_link_animdata(fd, &wrld->id, wrld->adt);
wrld->ipo = newlibadr_us(fd, wrld->id.lib, wrld->ipo); // XXX deprecated - old animation system
@@ -3610,7 +3644,7 @@ static void lib_link_vfont(FileData *fd, Main *main)
{
for (VFont *vf = main->vfont.first; vf; vf = vf->id.next) {
if (vf->id.tag & LIB_TAG_NEED_LINK) {
- IDP_LibLinkProperty(vf->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(vf->id.properties, fd);
vf->id.tag &= ~LIB_TAG_NEED_LINK;
}
@@ -3630,7 +3664,7 @@ static void lib_link_text(FileData *fd, Main *main)
{
for (Text *text = main->text.first; text; text = text->id.next) {
if (text->id.tag & LIB_TAG_NEED_LINK) {
- IDP_LibLinkProperty(text->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(text->id.properties, fd);
text->id.tag &= ~LIB_TAG_NEED_LINK;
}
@@ -3682,7 +3716,7 @@ static void lib_link_image(FileData *fd, Main *main)
{
for (Image *ima = main->image.first; ima; ima = ima->id.next) {
if (ima->id.tag & LIB_TAG_NEED_LINK) {
- IDP_LibLinkProperty(ima->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(ima->id.properties, fd);
ima->id.tag &= ~LIB_TAG_NEED_LINK;
}
@@ -3749,7 +3783,7 @@ static void lib_link_curve(FileData *fd, Main *main)
{
for (Curve *cu = main->curve.first; cu; cu = cu->id.next) {
if (cu->id.tag & LIB_TAG_NEED_LINK) {
- IDP_LibLinkProperty(cu->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(cu->id.properties, fd);
lib_link_animdata(fd, &cu->id, cu->adt);
for (int a = 0; a < cu->totcol; a++) {
@@ -3841,7 +3875,7 @@ static void lib_link_texture(FileData *fd, Main *main)
{
for (Tex *tex = main->tex.first; tex; tex = tex->id.next) {
if (tex->id.tag & LIB_TAG_NEED_LINK) {
- IDP_LibLinkProperty(tex->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(tex->id.properties, fd);
lib_link_animdata(fd, &tex->id, tex->adt);
tex->ima = newlibadr_us(fd, tex->id.lib, tex->ima);
@@ -3919,12 +3953,12 @@ static void lib_link_material(FileData *fd, Main *main)
{
for (Material *ma = main->mat.first; ma; ma = ma->id.next) {
if (ma->id.tag & LIB_TAG_NEED_LINK) {
- IDP_LibLinkProperty(ma->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(ma->id.properties, fd);
lib_link_animdata(fd, &ma->id, ma->adt);
/* Link ID Properties -- and copy this comment EXACTLY for easy finding
* of library blocks that implement this.*/
- IDP_LibLinkProperty(ma->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(ma->id.properties, fd);
ma->ipo = newlibadr_us(fd, ma->id.lib, ma->ipo); // XXX deprecated - old animation system
ma->group = newlibadr_us(fd, ma->id.lib, ma->group);
@@ -4063,7 +4097,7 @@ static void lib_link_particlesettings(FileData *fd, Main *main)
{
for (ParticleSettings *part = main->particle.first; part; part = part->id.next) {
if (part->id.tag & LIB_TAG_NEED_LINK) {
- IDP_LibLinkProperty(part->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(part->id.properties, fd);
lib_link_animdata(fd, &part->id, part->adt);
part->ipo = newlibadr_us(fd, part->id.lib, part->ipo); // XXX deprecated - old animation system
@@ -4385,7 +4419,7 @@ static void lib_link_mesh(FileData *fd, Main *main)
/* Link ID Properties -- and copy this comment EXACTLY for easy finding
* of library blocks that implement this.*/
- IDP_LibLinkProperty(me->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(me->id.properties, fd);
lib_link_animdata(fd, &me->id, me->adt);
/* this check added for python created meshes */
@@ -4661,7 +4695,7 @@ static void lib_link_latt(FileData *fd, Main *main)
{
for (Lattice *lt = main->latt.first; lt; lt = lt->id.next) {
if (lt->id.tag & LIB_TAG_NEED_LINK) {
- IDP_LibLinkProperty(lt->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(lt->id.properties, fd);
lib_link_animdata(fd, &lt->id, lt->adt);
lt->ipo = newlibadr_us(fd, lt->id.lib, lt->ipo); // XXX deprecated - old animation system
@@ -4711,7 +4745,7 @@ static void lib_link_object(FileData *fd, Main *main)
if (ob->id.tag & LIB_TAG_NEED_LINK) {
int a;
- IDP_LibLinkProperty(ob->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(ob->id.properties, fd);
lib_link_animdata(fd, &ob->id, ob->adt);
// XXX deprecated - old animation system <<<
@@ -5699,7 +5733,7 @@ static void lib_link_scene(FileData *fd, Main *main)
if (sce->id.tag & LIB_TAG_NEED_LINK) {
/* Link ID Properties -- and copy this comment EXACTLY for easy finding
* of library blocks that implement this.*/
- IDP_LibLinkProperty(sce->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(sce->id.properties, fd);
lib_link_animdata(fd, &sce->id, sce->adt);
lib_link_keyingsets(fd, &sce->id, &sce->keyingsets);
@@ -5752,6 +5786,8 @@ static void lib_link_scene(FileData *fd, Main *main)
Sequence *seq;
SEQ_BEGIN (sce->ed, seq)
{
+ IDP_LibLinkProperty(seq->prop, fd);
+
if (seq->ipo) seq->ipo = newlibadr_us(fd, sce->id.lib, seq->ipo); // XXX deprecated - old animation system
seq->scene_sound = NULL;
if (seq->scene) {
@@ -6348,7 +6384,7 @@ static void lib_link_gpencil(FileData *fd, Main *main)
{
for (bGPdata *gpd = main->gpencil.first; gpd; gpd = gpd->id.next) {
if (gpd->id.tag & LIB_TAG_NEED_LINK) {
- IDP_LibLinkProperty(gpd->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(gpd->id.properties, fd);
lib_link_animdata(fd, &gpd->id, gpd->adt);
gpd->id.tag &= ~LIB_TAG_NEED_LINK;
@@ -6415,7 +6451,7 @@ static void lib_link_screen(FileData *fd, Main *main)
{
for (bScreen *sc = main->screen.first; sc; sc = sc->id.next) {
if (sc->id.tag & LIB_TAG_NEED_LINK) {
- IDP_LibLinkProperty(sc->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(sc->id.properties, fd);
id_us_ensure_real(&sc->id);
sc->scene = newlibadr(fd, sc->id.lib, sc->scene);
@@ -7505,7 +7541,7 @@ static void lib_link_speaker(FileData *fd, Main *main)
{
for (Speaker *spk = main->speaker.first; spk; spk = spk->id.next) {
if (spk->id.tag & LIB_TAG_NEED_LINK) {
- IDP_LibLinkProperty(spk->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(spk->id.properties, fd);
lib_link_animdata(fd, &spk->id, spk->adt);
spk->sound = newlibadr_us(fd, spk->id.lib, spk->sound);
@@ -7561,7 +7597,7 @@ static void lib_link_sound(FileData *fd, Main *main)
{
for (bSound *sound = main->sound.first; sound; sound = sound->id.next) {
if (sound->id.tag & LIB_TAG_NEED_LINK) {
- IDP_LibLinkProperty(sound->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(sound->id.properties, fd);
sound->ipo = newlibadr_us(fd, sound->id.lib, sound->ipo); // XXX deprecated - old animation system
@@ -7584,7 +7620,7 @@ static void lib_link_group(FileData *fd, Main *main)
{
for (Group *group = main->group.first; group; group = group->id.next) {
if (group->id.tag & LIB_TAG_NEED_LINK) {
- IDP_LibLinkProperty(group->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(group->id.properties, fd);
bool add_us = false;
@@ -7710,7 +7746,7 @@ static void lib_link_movieclip(FileData *fd, Main *main)
if (clip->id.tag & LIB_TAG_NEED_LINK) {
MovieTracking *tracking = &clip->tracking;
- IDP_LibLinkProperty(clip->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(clip->id.properties, fd);
lib_link_animdata(fd, &clip->id, clip->adt);
clip->gpd = newlibadr_us(fd, clip->id.lib, clip->gpd);
@@ -7797,7 +7833,7 @@ static void lib_link_mask(FileData *fd, Main *main)
{
for (Mask *mask = main->mask.first; mask; mask = mask->id.next) {
if (mask->id.tag & LIB_TAG_NEED_LINK) {
- IDP_LibLinkProperty(mask->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(mask->id.properties, fd);
lib_link_animdata(fd, &mask->id, mask->adt);
for (MaskLayer *masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
@@ -7832,7 +7868,7 @@ static void lib_link_linestyle(FileData *fd, Main *main)
if (linestyle->id.tag & LIB_TAG_NEED_LINK) {
LineStyleModifier *m;
- IDP_LibLinkProperty(linestyle->id.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
+ IDP_LibLinkProperty(linestyle->id.properties, fd);
lib_link_animdata(fd, &linestyle->id, linestyle->adt);
for (m = linestyle->color_modifiers.first; m; m = m->next) {
@@ -9001,6 +9037,31 @@ static void expand_constraint_channels(FileData *fd, Main *mainvar, ListBase *ch
}
}
+static void expand_idprops(FileData *fd, Main *mainvar, IDProperty *prop)
+{
+ if (!prop)
+ return;
+
+ switch (prop->type) {
+ case IDP_ID:
+ expand_doit(fd, mainvar, IDP_Id(prop));
+ break;
+ case IDP_IDPARRAY:
+ {
+ IDProperty *idp_array = IDP_IDPArray(prop);
+ for (int i = 0; i < prop->len; i++) {
+ expand_idprops(fd, mainvar, &idp_array[i]);
+ }
+ break;
+ }
+ case IDP_GROUP:
+ for (IDProperty *loop = prop->data.group.first; loop; loop = loop->next) {
+ expand_idprops(fd, mainvar, loop);
+ }
+ break;
+ }
+}
+
static void expand_fmodifiers(FileData *fd, Main *mainvar, ListBase *list)
{
FModifier *fcm;
@@ -9186,6 +9247,7 @@ static void expand_key(FileData *fd, Main *mainvar, Key *key)
static void expand_nodetree(FileData *fd, Main *mainvar, bNodeTree *ntree)
{
bNode *node;
+ bNodeSocket *sock;
if (ntree->adt)
expand_animdata(fd, mainvar, ntree->adt);
@@ -9194,10 +9256,22 @@ static void expand_nodetree(FileData *fd, Main *mainvar, bNodeTree *ntree)
expand_doit(fd, mainvar, ntree->gpd);
for (node = ntree->nodes.first; node; node = node->next) {
- if (node->id && node->type != CMP_NODE_R_LAYERS)
+ if (node->id && node->type != CMP_NODE_R_LAYERS) {
expand_doit(fd, mainvar, node->id);
+ }
+
+ expand_idprops(fd, mainvar, node->prop);
+
+ for (sock = node->inputs.first; sock; sock = sock->next)
+ expand_doit(fd, mainvar, sock->prop);
+ for (sock = node->outputs.first; sock; sock = sock->next)
+ expand_doit(fd, mainvar, sock->prop);
}
+ for (sock = ntree->inputs.first; sock; sock = sock->next)
+ expand_doit(fd, mainvar, sock->prop);
+ for (sock = ntree->outputs.first; sock; sock = sock->next)
+ expand_doit(fd, mainvar, sock->prop);
}
static void expand_texture(FileData *fd, Main *mainvar, Tex *tex)
@@ -9415,17 +9489,6 @@ static void expand_constraints(FileData *fd, Main *mainvar, ListBase *lb)
}
}
-#if 0 /* Disabled as it doesn't actually do anything except recurse... */
-static void expand_bones(FileData *fd, Main *mainvar, Bone *bone)
-{
- Bone *curBone;
-
- for (curBone = bone->childbase.first; curBone; curBone=curBone->next) {
- expand_bones(fd, mainvar, curBone);
- }
-}
-#endif
-
static void expand_pose(FileData *fd, Main *mainvar, bPose *pose)
{
bPoseChannel *chan;
@@ -9435,24 +9498,28 @@ static void expand_pose(FileData *fd, Main *mainvar, bPose *pose)
for (chan = pose->chanbase.first; chan; chan = chan->next) {
expand_constraints(fd, mainvar, &chan->constraints);
+ expand_idprops(fd, mainvar, chan->prop);
expand_doit(fd, mainvar, chan->custom);
}
}
+static void expand_bones(FileData *fd, Main *mainvar, Bone *bone)
+{
+ expand_idprops(fd, mainvar, bone->prop);
+
+ for (Bone *curBone = bone->childbase.first; curBone; curBone = curBone->next) {
+ expand_bones(fd, mainvar, curBone);
+ }
+}
+
static void expand_armature(FileData *fd, Main *mainvar, bArmature *arm)
-{
+{
if (arm->adt)
expand_animdata(fd, mainvar, arm->adt);
-
-#if 0 /* Disabled as this currently only recurses down the chain doing nothing */
- {
- Bone *curBone;
-
- for (curBone = arm->bonebase.first; curBone; curBone=curBone->next) {
- expand_bones(fd, mainvar, curBone);
- }
+
+ for (Bone *curBone = arm->bonebase.first; curBone; curBone = curBone->next) {
+ expand_bones(fd, mainvar, curBone);
}
-#endif
}
static void expand_object_expandModifiers(
@@ -9695,6 +9762,8 @@ static void expand_scene(FileData *fd, Main *mainvar, Scene *sce)
SEQ_BEGIN (sce->ed, seq)
{
+ expand_idprops(fd, mainvar, seq->prop);
+
if (seq->scene) expand_doit(fd, mainvar, seq->scene);
if (seq->scene_camera) expand_doit(fd, mainvar, seq->scene_camera);
if (seq->clip) expand_doit(fd, mainvar, seq->clip);
@@ -9854,6 +9923,8 @@ void BLO_expand_main(void *fdhandle, Main *mainvar)
id = lbarray[a]->first;
while (id) {
if (id->tag & LIB_TAG_NEED_EXPAND) {
+ expand_idprops(fd, mainvar, id->properties);
+
switch (GS(id->name)) {
case ID_OB:
expand_object(fd, mainvar, (Object *)id);
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 75c28331c4b..2bfeebd4d9a 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -701,7 +701,7 @@ void UI_but_func_search_set(
int UI_searchbox_size_y(void);
int UI_searchbox_size_x(void);
/* check if a string is in an existing search box */
-int UI_search_items_find_index(uiSearchItems *items, const char *name);
+int UI_search_items_find_index(uiSearchItems *items, const char *name, const size_t offset);
void UI_block_func_handle_set(uiBlock *block, uiBlockHandleFunc func, void *arg);
void UI_block_func_butmenu_set(uiBlock *block, uiMenuHandleFunc func, void *arg);
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index 771f9d24e3c..bc7f835390c 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -4368,7 +4368,7 @@ void UI_but_func_search_set(
if (0 == (but->block->flag & UI_BLOCK_LOOP)) {
/* skip empty buttons, not all buttons need input, we only show invalid */
if (but->drawstr[0])
- ui_but_search_refresh(but);
+ ui_but_search_refresh(but, false);
}
}
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index 68bb48f66fb..29808d84818 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -606,7 +606,7 @@ int ui_searchbox_autocomplete(struct bContext *C, struct ARegion *ar, uiBut *but
void ui_searchbox_event(struct bContext *C, struct ARegion *ar, uiBut *but, const struct wmEvent *event);
bool ui_searchbox_apply(uiBut *but, struct ARegion *ar);
void ui_searchbox_free(struct bContext *C, struct ARegion *ar);
-void ui_but_search_refresh(uiBut *but);
+void ui_but_search_refresh(uiBut *but, const bool is_template_ID);
uiBlock *ui_popup_block_refresh(
struct bContext *C, uiPopupBlockHandle *handle,
diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c
index c5557a400ae..6ec0c39a76d 100644
--- a/source/blender/editors/interface/interface_regions.c
+++ b/source/blender/editors/interface/interface_regions.c
@@ -813,11 +813,11 @@ int UI_searchbox_size_x(void)
return 12 * UI_UNIT_X;
}
-int UI_search_items_find_index(uiSearchItems *items, const char *name)
+int UI_search_items_find_index(uiSearchItems *items, const char *name, const size_t offset)
{
int i;
for (i = 0; i < items->totitem; i++) {
- if (STREQ(name, items->names[i])) {
+ if (STREQ(name, items->names[i] + offset)) {
return i;
}
}
@@ -896,7 +896,7 @@ static void ui_searchbox_butrect(rcti *r_rect, uiSearchboxData *data, int itemnr
int ui_searchbox_find_index(ARegion *ar, const char *name)
{
uiSearchboxData *data = ar->regiondata;
- return UI_search_items_find_index(&data->items, name);
+ return UI_search_items_find_index(&data->items, name, 0);
}
/* x and y in screencoords */
@@ -1425,14 +1425,14 @@ void ui_searchbox_free(bContext *C, ARegion *ar)
/* sets red alert if button holds a string it can't find */
/* XXX weak: search_func adds all partial matches... */
-void ui_but_search_refresh(uiBut *but)
+void ui_but_search_refresh(uiBut *but, const bool is_template_ID)
{
uiSearchItems *items;
int x1;
- /* possibly very large lists (such as ID datablocks) only
- * only validate string RNA buts (not pointers) */
- if (but->rnaprop && RNA_property_type(but->rnaprop) != PROP_STRING) {
+ /* possibly very large lists (such as ID datablocks),
+ * only validate string and pointer RNA buts */
+ if (but->rnaprop && !ELEM(RNA_property_type(but->rnaprop), PROP_STRING, PROP_POINTER)) {
return;
}
@@ -1452,7 +1452,8 @@ void ui_but_search_refresh(uiBut *but)
UI_but_flag_enable(but, UI_BUT_REDALERT);
}
else if (items->more == 0) {
- if (UI_search_items_find_index(items, but->drawstr) == -1) {
+ const size_t offset = is_template_ID ? 3 : 0;
+ if (UI_search_items_find_index(items, but->drawstr, offset) == -1) {
UI_but_flag_enable(but, UI_BUT_REDALERT);
}
}
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index e3a71feb1a9..6efaeb88205 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -467,6 +467,11 @@ static void template_ID(
but = uiDefButR(block, UI_BTYPE_TEXT, 0, name, 0, 0, UI_UNIT_X * 6, UI_UNIT_Y,
&idptr, "name", -1, 0, 0, -1, -1, RNA_struct_ui_description(type));
UI_but_funcN_set(but, template_id_cb, MEM_dupallocN(template), SET_INT_IN_POINTER(UI_ID_RENAME));
+
+ but->search_func = id_search_cb;
+ but->search_arg = template;
+ ui_but_search_refresh(but, true);
+
if (user_alert) UI_but_flag_enable(but, UI_BUT_REDALERT);
if (id->lib) {
diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c
index c8b0955c70f..be5f1013630 100644
--- a/source/blender/editors/mesh/meshtools.c
+++ b/source/blender/editors/mesh/meshtools.c
@@ -57,6 +57,7 @@
#include "BKE_main.h"
#include "BKE_mesh.h"
#include "BKE_material.h"
+#include "BKE_object.h"
#include "BKE_report.h"
#include "BKE_editmesh.h"
#include "BKE_multires.h"
@@ -596,6 +597,9 @@ int join_mesh_exec(bContext *C, wmOperator *op)
BKE_key_sort(key);
}
+ /* Due to dependnecy cycle some other object might access old derived data. */
+ BKE_object_free_derived_caches(ob);
+
DAG_relations_tag_update(bmain); /* removed objects, need to rebuild dag */
DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA);
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index 10439d49787..3f3c770dbe1 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -73,6 +73,7 @@
#include "BKE_global.h"
#include "BKE_group.h"
#include "BKE_fcurve.h"
+#include "BKE_idprop.h"
#include "BKE_lamp.h"
#include "BKE_lattice.h"
#include "BKE_layer.h"
@@ -2007,6 +2008,34 @@ void ED_object_single_users(Main *bmain, Scene *scene, const bool full, const bo
single_tex_users_expand(bmain);
}
+ /* Relink datablock pointer properties */
+ {
+ IDP_RelinkProperty(scene->id.properties);
+
+ for (Base *base = scene->base.first; base; base = base->next) {
+ Object *ob = base->object;
+ if (!ID_IS_LINKED_DATABLOCK(ob)) {
+ IDP_RelinkProperty(ob->id.properties);
+ }
+ }
+
+ if (scene->nodetree) {
+ IDP_RelinkProperty(scene->nodetree->id.properties);
+ for (bNode *node = scene->nodetree->nodes.first; node; node = node->next) {
+ IDP_RelinkProperty(node->prop);
+ }
+ }
+
+ if (scene->gpd) {
+ IDP_RelinkProperty(scene->gpd->id.properties);
+ }
+
+ IDP_RelinkProperty(scene->world->id.properties);
+
+ if (scene->clip) {
+ IDP_RelinkProperty(scene->clip->id.properties);
+ }
+ }
BKE_main_id_clear_newpoins(bmain);
DAG_relations_tag_update(bmain);
}
diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h
index 9c3e194b78c..14b9b17e02c 100644
--- a/source/blender/makesdna/DNA_ID.h
+++ b/source/blender/makesdna/DNA_ID.h
@@ -82,8 +82,6 @@ enum {
IDP_FLOAT = 2,
IDP_ARRAY = 5,
IDP_GROUP = 6,
- /* the ID link property type hasn't been implemented yet, this will require
- * some cleanup of blenkernel, most likely. */
IDP_ID = 7,
IDP_DOUBLE = 8,
IDP_IDPARRAY = 9,
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index e8cb54448de..1a6b3008a79 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -773,6 +773,8 @@ void RNA_struct_blender_type_set(StructRNA *srna, void *blender_type);
struct IDProperty *RNA_struct_idprops(PointerRNA *ptr, bool create);
bool RNA_struct_idprops_check(StructRNA *srna);
bool RNA_struct_idprops_register_check(const StructRNA *type);
+bool RNA_struct_idprops_datablock_allowed(const StructRNA *type);
+bool RNA_struct_idprops_contains_datablock(const StructRNA *type);
bool RNA_struct_idprops_unset(PointerRNA *ptr, const char *identifier);
PropertyRNA *RNA_struct_find_property(PointerRNA *ptr, const char *identifier);
diff --git a/source/blender/makesrna/RNA_define.h b/source/blender/makesrna/RNA_define.h
index 2a680b6eaeb..6e62313b00a 100644
--- a/source/blender/makesrna/RNA_define.h
+++ b/source/blender/makesrna/RNA_define.h
@@ -167,6 +167,7 @@ void RNA_def_property_editable_func(PropertyRNA *prop, const char *editable);
void RNA_def_property_editable_array_func(PropertyRNA *prop, const char *editable);
void RNA_def_property_update_runtime(PropertyRNA *prop, const void *func);
+void RNA_def_property_poll_runtime(PropertyRNA *prop, const void *func);
void RNA_def_property_dynamic_array_funcs(PropertyRNA *prop, const char *getlength);
void RNA_def_property_boolean_funcs(PropertyRNA *prop, const char *get, const char *set);
diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h
index dee8df7d933..cd04f9e8a6d 100644
--- a/source/blender/makesrna/RNA_types.h
+++ b/source/blender/makesrna/RNA_types.h
@@ -434,6 +434,8 @@ typedef enum StructFlag {
STRUCT_GENERATED = (1 << 4),
STRUCT_FREE_POINTERS = (1 << 5),
STRUCT_NO_IDPROPERTIES = (1 << 6), /* Menus and Panels don't need properties */
+ STRUCT_NO_DATABLOCK_IDPROPERTIES = (1 << 7), /* e.g. for Operator */
+ STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES = (1 << 8), /* for PropertyGroup which contains pointers to datablocks */
} StructFlag;
typedef int (*StructValidateFunc)(struct PointerRNA *ptr, void *data, int *have_function);
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index 671902c5cc7..6ed2c55138c 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -802,7 +802,11 @@ static void rna_def_ID_properties(BlenderRNA *brna)
RNA_def_struct_name_property(srna, prop);
#endif
- /* IDP_ID -- not implemented yet in id properties */
+ /* IDP_ID */
+ prop = RNA_def_property(srna, "id", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_EXPORT | PROP_IDPROPERTY | PROP_NEVER_UNLINK);
+ RNA_def_property_struct_type(prop, "ID");
+
/* ID property groups > level 0, since level 0 group is merged
* with native RNA properties. the builtin_properties will take
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c
index 7266cfb12d6..c3d2d92fc5e 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -50,6 +50,7 @@
#include "BKE_idcode.h"
#include "BKE_idprop.h"
#include "BKE_fcurve.h"
+#include "BKE_library.h"
#include "BKE_main.h"
#include "BKE_report.h"
@@ -380,6 +381,7 @@ static bool rna_idproperty_verify_valid(PointerRNA *ptr, PropertyRNA *prop, IDPr
return false;
break;
case IDP_GROUP:
+ case IDP_ID:
if (prop->type != PROP_POINTER)
return false;
break;
@@ -395,7 +397,8 @@ static PropertyRNA *typemap[IDP_NUMTYPES] = {
(PropertyRNA *)&rna_PropertyGroupItem_int,
(PropertyRNA *)&rna_PropertyGroupItem_float,
NULL, NULL, NULL,
- (PropertyRNA *)&rna_PropertyGroupItem_group, NULL,
+ (PropertyRNA *)&rna_PropertyGroupItem_group,
+ (PropertyRNA *)&rna_PropertyGroupItem_id,
(PropertyRNA *)&rna_PropertyGroupItem_double,
(PropertyRNA *)&rna_PropertyGroupItem_idp_array
};
@@ -587,6 +590,21 @@ bool RNA_struct_idprops_register_check(const StructRNA *type)
return (type->flag & STRUCT_NO_IDPROPERTIES) == 0;
}
+bool RNA_struct_idprops_datablock_allowed(const StructRNA *type)
+{
+ return (type->flag & (STRUCT_NO_DATABLOCK_IDPROPERTIES | STRUCT_NO_IDPROPERTIES)) == 0;
+}
+
+/**
+ * Whether given type implies datablock usage by IDProperties.
+ * This is used to prevent classes allowed to have IDProperties, but not datablock ones, to indirectly use some
+ * (e.g. by assigning an IDP_GROUP containing some IDP_ID pointers...).
+ */
+bool RNA_struct_idprops_contains_datablock(const StructRNA *type)
+{
+ return (type->flag & (STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES | STRUCT_ID)) != 0;
+}
+
/* remove an id-property */
bool RNA_struct_idprops_unset(PointerRNA *ptr, const char *identifier)
{
@@ -628,8 +646,11 @@ PropertyRNA *RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
/* id prop lookup, not so common */
PropertyRNA *r_prop = NULL;
PointerRNA r_ptr; /* only support single level props */
- if (RNA_path_resolve(ptr, identifier, &r_ptr, &r_prop) && (r_ptr.type == ptr->type) && (r_ptr.data == ptr->data))
+ if (RNA_path_resolve_property(ptr, identifier, &r_ptr, &r_prop) &&
+ (r_ptr.type == ptr->type) && (r_ptr.data == ptr->data))
+ {
return r_prop;
+ }
}
else {
/* most common case */
@@ -1201,13 +1222,20 @@ int RNA_property_pointer_poll(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *va
if (prop->type == PROP_POINTER) {
PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop;
- if (pprop->poll)
- return pprop->poll(ptr, *value);
+
+ if (pprop->poll) {
+ if (rna_idproperty_check(&prop, ptr)) {
+ return ((PropPointerPollFuncPy) pprop->poll)(ptr, *value, prop);
+ }
+ else {
+ return pprop->poll(ptr, *value);
+ }
+ }
return 1;
}
- printf("%s %s: is not a pointer property.\n", __func__, prop->identifier);
+ printf("%s: %s is not a pointer property.\n", __func__, prop->identifier);
return 0;
}
@@ -2967,6 +2995,10 @@ PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop)
if ((idprop = rna_idproperty_check(&prop, ptr))) {
pprop = (PointerPropertyRNA *)prop;
+ if (RNA_struct_is_ID(pprop->type)) {
+ return rna_pointer_inherit_refine(ptr, pprop->type, IDP_Id(idprop));
+ }
+
/* for groups, data is idprop itself */
if (pprop->typef)
return rna_pointer_inherit_refine(ptr, pprop->typef(ptr), idprop);
@@ -2989,22 +3021,32 @@ PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop)
void RNA_property_pointer_set(PointerRNA *ptr, PropertyRNA *prop, PointerRNA ptr_value)
{
- /*IDProperty *idprop;*/
-
+ PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop;
BLI_assert(RNA_property_type(prop) == PROP_POINTER);
- if ((/*idprop = */ rna_idproperty_check(&prop, ptr))) {
- /* not supported */
- /* rna_idproperty_touch(idprop); */
+ /* Check types */
+ if (ptr_value.type != NULL && !RNA_struct_is_a(ptr_value.type, pprop->type)) {
+ printf("%s: expected %s type, not %s.\n", __func__, pprop->type->identifier, ptr_value.type->identifier);
+ return;
}
- else {
- PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop;
- if (pprop->set &&
- !((prop->flag & PROP_NEVER_NULL) && ptr_value.data == NULL) &&
- !((prop->flag & PROP_ID_SELF_CHECK) && ptr->id.data == ptr_value.id.data))
- {
- pprop->set(ptr, ptr_value);
+ /* RNA */
+ if (pprop->set &&
+ !((prop->flag & PROP_NEVER_NULL) && ptr_value.data == NULL) &&
+ !((prop->flag & PROP_ID_SELF_CHECK) && ptr->id.data == ptr_value.id.data))
+ {
+ pprop->set(ptr, ptr_value);
+ }
+ /* IDProperty */
+ else if (prop->flag & PROP_EDITABLE) {
+ IDPropertyTemplate val = {0};
+ IDProperty *group;
+
+ val.id = ptr_value.data;
+
+ group = RNA_struct_idprops(ptr, true);
+ if (group) {
+ IDP_ReplaceInGroup(group, IDP_New(IDP_ID, &val, prop->identifier));
}
}
}
diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c
index 1d232d2df39..49983b0bc2b 100644
--- a/source/blender/makesrna/intern/rna_define.c
+++ b/source/blender/makesrna/intern/rna_define.c
@@ -2168,6 +2168,16 @@ void RNA_def_property_update_runtime(PropertyRNA *prop, const void *func)
prop->update = (void *)func;
}
+void RNA_def_property_poll_runtime(PropertyRNA *prop, const void *func)
+{
+ if (prop->type == PROP_POINTER) {
+ ((PointerPropertyRNA *)prop)->poll = func;
+ }
+ else {
+ fprintf(stderr, "%s: %s is not a Pointer Property.\n", __func__, prop->identifier);
+ }
+}
+
void RNA_def_property_dynamic_array_funcs(PropertyRNA *prop, const char *getlength)
{
if (!DefRNA.preprocess) {
@@ -2982,6 +2992,9 @@ PropertyRNA *RNA_def_pointer_runtime(StructOrFunctionRNA *cont_, const char *ide
prop = RNA_def_property(cont, identifier, PROP_POINTER, PROP_NONE);
RNA_def_property_struct_runtime(prop, type);
+ if ((type->flag & STRUCT_ID) != 0) {
+ prop->flag |= PROP_EDITABLE;
+ }
RNA_def_property_ui_text(prop, ui_name, ui_description);
return prop;
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index c79f6c3bd9a..b4c3916ffbe 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -344,6 +344,7 @@ extern IntPropertyRNA rna_PropertyGroupItem_int_array;
extern FloatPropertyRNA rna_PropertyGroupItem_float;
extern FloatPropertyRNA rna_PropertyGroupItem_float_array;
extern PointerPropertyRNA rna_PropertyGroupItem_group;
+extern PointerPropertyRNA rna_PropertyGroupItem_id;
extern CollectionPropertyRNA rna_PropertyGroupItem_collection;
extern CollectionPropertyRNA rna_PropertyGroupItem_idp_array;
extern FloatPropertyRNA rna_PropertyGroupItem_double;
diff --git a/source/blender/makesrna/intern/rna_internal_types.h b/source/blender/makesrna/intern/rna_internal_types.h
index fce81e6967e..df591659fdb 100644
--- a/source/blender/makesrna/intern/rna_internal_types.h
+++ b/source/blender/makesrna/intern/rna_internal_types.h
@@ -94,6 +94,7 @@ typedef PointerRNA (*PropPointerGetFunc)(struct PointerRNA *ptr);
typedef StructRNA *(*PropPointerTypeFunc)(struct PointerRNA *ptr);
typedef void (*PropPointerSetFunc)(struct PointerRNA *ptr, const PointerRNA value);
typedef int (*PropPointerPollFunc)(struct PointerRNA *ptr, const PointerRNA value);
+typedef int (*PropPointerPollFuncPy)(struct PointerRNA *ptr, const PointerRNA value, const PropertyRNA *prop);
typedef void (*PropCollectionBeginFunc)(struct CollectionPropertyIterator *iter, struct PointerRNA *ptr);
typedef void (*PropCollectionNextFunc)(struct CollectionPropertyIterator *iter);
typedef void (*PropCollectionEndFunc)(struct CollectionPropertyIterator *iter);
diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c
index 7a3c862f04c..54b82fc89d6 100644
--- a/source/blender/makesrna/intern/rna_ui.c
+++ b/source/blender/makesrna/intern/rna_ui.c
@@ -1039,6 +1039,7 @@ static void rna_def_uilist(BlenderRNA *brna)
RNA_def_struct_refine_func(srna, "rna_UIList_refine");
RNA_def_struct_register_funcs(srna, "rna_UIList_register", "rna_UIList_unregister", NULL);
RNA_def_struct_idprops_func(srna, "rna_UIList_idprops");
+ RNA_def_struct_flag(srna, STRUCT_NO_DATABLOCK_IDPROPERTIES);
/* Registration */
prop = RNA_def_property(srna, "bl_idname", PROP_STRING, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index 4d70b87843a..d1e89ea18d0 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -3156,6 +3156,7 @@ static void rna_def_userdef_addon_pref(BlenderRNA *brna)
RNA_def_struct_refine_func(srna, "rna_AddonPref_refine");
RNA_def_struct_register_funcs(srna, "rna_AddonPref_register", "rna_AddonPref_unregister", NULL);
RNA_def_struct_idprops_func(srna, "rna_AddonPref_idprops");
+ RNA_def_struct_flag(srna, STRUCT_NO_DATABLOCK_IDPROPERTIES); /* Mandatory! */
/* registration */
RNA_define_verify_sdna(0);
diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c
index 35c9c9bcc89..b458b2e69d5 100644
--- a/source/blender/makesrna/intern/rna_wm.c
+++ b/source/blender/makesrna/intern/rna_wm.c
@@ -1556,6 +1556,7 @@ static void rna_def_operator(BlenderRNA *brna)
RNA_def_struct_ui_text(srna, "Operator Properties", "Input properties of an Operator");
RNA_def_struct_refine_func(srna, "rna_OperatorProperties_refine");
RNA_def_struct_idprops_func(srna, "rna_OperatorProperties_idprops");
+ RNA_def_struct_flag(srna, STRUCT_NO_DATABLOCK_IDPROPERTIES);
}
static void rna_def_macro_operator(BlenderRNA *brna)
diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c
index 0a9931f2683..5d6a7c578a2 100644
--- a/source/blender/python/generic/idprop_py_api.c
+++ b/source/blender/python/generic/idprop_py_api.c
@@ -43,6 +43,9 @@
#include "python_utildefines.h"
+extern bool pyrna_id_FromPyObject(PyObject *obj, ID **id);
+extern PyObject *pyrna_id_CreatePyObject(ID *id);
+extern bool pyrna_id_CheckPyObject(PyObject *obj);
/*********************** ID Property Main Wrapper Stuff ***************/
@@ -88,6 +91,11 @@ static PyObject *idprop_py_from_idp_group(ID *id, IDProperty *prop, IDProperty *
return (PyObject *)group;
}
+static PyObject *idprop_py_from_idp_id(IDProperty *prop)
+{
+ return pyrna_id_CreatePyObject(prop->data.pointer);
+}
+
static PyObject *idprop_py_from_idp_array(ID *id, IDProperty *prop)
{
BPy_IDProperty *array = PyObject_New(BPy_IDProperty, &BPy_IDArray_Type);
@@ -148,6 +156,7 @@ PyObject *BPy_IDGroup_WrapData(ID *id, IDProperty *prop, IDProperty *parent)
case IDP_GROUP: return idprop_py_from_idp_group(id, prop, parent);
case IDP_ARRAY: return idprop_py_from_idp_array(id, prop);
case IDP_IDPARRAY: return idprop_py_from_idp_idparray(id, prop); /* this could be better a internal type */
+ case IDP_ID: return idprop_py_from_idp_id(prop);
default: Py_RETURN_NONE;
}
}
@@ -586,8 +595,15 @@ static IDProperty *idp_from_PyMapping(const char *name, PyObject *ob)
return prop;
}
+static IDProperty *idp_from_DatablockPointer(const char *name, PyObject *ob, IDPropertyTemplate *val)
+{
+ pyrna_id_FromPyObject(ob, &val->id);
+ return IDP_New(IDP_ID, val, name);
+}
+
static IDProperty *idp_from_PyObject(PyObject *name_obj, PyObject *ob)
{
+ IDPropertyTemplate val = {0};
const char *name = idp_try_read_name(name_obj);
if (name == NULL) {
return NULL;
@@ -608,6 +624,9 @@ static IDProperty *idp_from_PyObject(PyObject *name_obj, PyObject *ob)
else if (PySequence_Check(ob)) {
return idp_from_PySequence(name, ob);
}
+ else if (ob == Py_None || pyrna_id_CheckPyObject(ob)) {
+ return idp_from_DatablockPointer(name, ob, &val);
+ }
else if (PyMapping_Check(ob)) {
return idp_from_PyMapping(name, ob);
}
@@ -732,6 +751,8 @@ static PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop)
return idprop_py_from_idp_float(prop);
case IDP_DOUBLE:
return idprop_py_from_idp_double(prop);
+ case IDP_ID:
+ return idprop_py_from_idp_id(prop);
case IDP_ARRAY:
{
PyObject *seq = PyList_New(prop->len);
diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c
index f7348ac2250..2656e612b18 100644
--- a/source/blender/python/intern/bpy_props.c
+++ b/source/blender/python/intern/bpy_props.c
@@ -50,11 +50,13 @@
#include "../generic/py_capi_utils.h"
/* initial definition of callback slots we'll probably have more than 1 */
-#define BPY_DATA_CB_SLOT_SIZE 3
-
-#define BPY_DATA_CB_SLOT_UPDATE 0
-#define BPY_DATA_CB_SLOT_GET 1
-#define BPY_DATA_CB_SLOT_SET 2
+enum {
+ BPY_DATA_CB_SLOT_UPDATE = 0,
+ BPY_DATA_CB_SLOT_GET = 1,
+ BPY_DATA_CB_SLOT_SET = 2,
+ BPY_DATA_CB_SLOT_POLL = 3,
+ BPY_DATA_CB_SLOT_SIZE = 4,
+};
extern BPy_StructRNA *bpy_context_module;
@@ -71,6 +73,9 @@ static EnumPropertyItem property_flag_items[] = {
" :arg options: Enumerator in ['HIDDEN', 'SKIP_SAVE', 'ANIMATABLE', 'LIBRARY_EDITABLE', 'PROPORTIONAL'," \
"'TEXTEDIT_UPDATE'].\n" \
" :type options: set\n" \
+" :arg poll: function to be called to determine whether an item is valid for this property.\n" \
+" The function must take 2 values (self,object) and return Bool.\n" \
+" :type poll: function\n" \
static EnumPropertyItem property_flag_enum_items[] = {
{PROP_HIDDEN, "HIDDEN", 0, "Hidden", ""},
@@ -389,6 +394,51 @@ static void bpy_prop_boolean_set_cb(struct PointerRNA *ptr, struct PropertyRNA *
}
}
+static int bpy_prop_poll_cb(struct PointerRNA *self, PointerRNA candidate, struct PropertyRNA *prop)
+{
+ PyObject *py_self;
+ PyObject *py_candidate;
+ PyObject *py_func;
+ PyObject **py_data = RNA_property_py_data_get(prop);
+ PyObject *args;
+ PyObject *ret;
+ bool result;
+ const int is_write_ok = pyrna_write_check();
+ PyGILState_STATE gilstate = PyGILState_Ensure();
+
+ BLI_assert(self != NULL);
+
+ py_self = pyrna_struct_as_instance(self);
+ py_candidate = pyrna_struct_as_instance(&candidate);
+ py_func = py_data[BPY_DATA_CB_SLOT_POLL];
+
+ if (!is_write_ok)
+ pyrna_write_set(true);
+
+ args = PyTuple_New(2);
+ PyTuple_SET_ITEM(args, 0, py_self);
+ PyTuple_SET_ITEM(args, 1, py_candidate);
+
+ ret = PyObject_CallObject(py_func, args);
+
+ Py_DECREF(args);
+
+ if (ret == NULL) {
+ printf_func_error(py_func);
+ result = false;
+ }
+ else {
+ result = PyObject_IsTrue(ret);
+ Py_DECREF(ret);
+ }
+
+ PyGILState_Release(gilstate);
+ if (!is_write_ok)
+ pyrna_write_set(false);
+
+ return result;
+}
+
static void bpy_prop_boolean_array_get_cb(struct PointerRNA *ptr, struct PropertyRNA *prop, int *values)
{
PyObject **py_data = RNA_property_py_data_get(prop);
@@ -1598,6 +1648,16 @@ static void bpy_prop_callback_assign_update(struct PropertyRNA *prop, PyObject *
}
}
+static void bpy_prop_callback_assign_pointer(struct PropertyRNA *prop, PyObject *poll_cb)
+{
+ if (poll_cb && poll_cb != Py_None) {
+ PyObject **py_data = bpy_prop_py_data_get(prop);
+
+ RNA_def_property_poll_runtime(prop, (void *) bpy_prop_poll_cb);
+ py_data[BPY_DATA_CB_SLOT_POLL] = poll_cb;
+ }
+}
+
static void bpy_prop_callback_assign_boolean(struct PropertyRNA *prop, PyObject *get_cb, PyObject *set_cb)
{
BooleanPropertyGetFunc rna_get_cb = NULL;
@@ -1904,7 +1964,7 @@ static void bpy_prop_callback_assign_enum(struct PropertyRNA *prop, PyObject *ge
" :type set: function\n" \
#define BPY_PROPDEF_TYPE_DOC \
-" :arg type: A subclass of :class:`bpy.types.PropertyGroup`.\n" \
+" :arg type: A subclass of :class:`bpy.types.PropertyGroup` or :class:`bpy.types.ID`.\n" \
" :type type: class\n" \
#if 0
@@ -2772,7 +2832,7 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw)
Py_RETURN_NONE;
}
-static StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix)
+StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix)
{
StructRNA *srna;
@@ -2782,25 +2842,18 @@ static StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix
PyObject *msg = PyC_ExceptionBuffer();
const char *msg_char = _PyUnicode_AsString(msg);
PyErr_Format(PyExc_TypeError,
- "%.200s expected an RNA type derived from PropertyGroup, failed with: %s",
+ "%.200s expected an RNA type, failed with: %s",
error_prefix, msg_char);
Py_DECREF(msg);
}
else {
PyErr_Format(PyExc_TypeError,
- "%.200s expected an RNA type derived from PropertyGroup, failed with type '%s'",
+ "%.200s expected an RNA type, failed with type '%s'",
error_prefix, Py_TYPE(value)->tp_name);
}
return NULL;
}
- if (!RNA_struct_is_a(srna, &RNA_PropertyGroup)) {
- PyErr_Format(PyExc_TypeError,
- "%.200s expected an RNA type derived from PropertyGroup",
- error_prefix);
- return NULL;
- }
-
return srna;
}
@@ -2809,7 +2862,8 @@ PyDoc_STRVAR(BPy_PointerProperty_doc,
"name=\"\", "
"description=\"\", "
"options={'ANIMATABLE'}, "
- "update=None)\n"
+ "update=None,\n"
+ "poll=None)\n"
"\n"
" Returns a new pointer property definition.\n"
"\n"
@@ -2819,14 +2873,14 @@ BPY_PROPDEF_DESC_DOC
BPY_PROPDEF_OPTIONS_DOC
BPY_PROPDEF_UPDATE_DOC
);
-static PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw)
+PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
BPY_PROPDEF_HEAD(PointerProperty);
if (srna) {
- static const char *kwlist[] = {"attr", "type", "name", "description", "options", "update", NULL};
+ static const char *kwlist[] = {"attr", "type", "name", "description", "options", "poll", "update", NULL};
const char *id = NULL, *name = NULL, *description = "";
int id_len;
PropertyRNA *prop;
@@ -2834,33 +2888,47 @@ static PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *k
PyObject *type = Py_None;
PyObject *pyopts = NULL;
int opts = 0;
- PyObject *update_cb = NULL;
+ PyObject *update_cb = NULL, *poll_cb = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kw,
- "s#O|ssO!O:PointerProperty",
+ "s#O|ssO!OOO:PointerProperty",
(char **)kwlist, &id, &id_len,
&type, &name, &description,
&PySet_Type, &pyopts,
- &update_cb))
+ &poll_cb, &update_cb))
{
return NULL;
}
BPY_PROPDEF_CHECK(PointerProperty, property_flag_items);
- ptype = pointer_type_from_py(type, "PointerProperty(...):");
+ ptype = pointer_type_from_py(type, "PointerProperty(...)");
if (!ptype)
return NULL;
-
+ if (!RNA_struct_is_a(ptype, &RNA_PropertyGroup) && !RNA_struct_is_ID(ptype)) {
+ PyErr_Format(PyExc_TypeError,
+ "PointerProperty(...) expected an RNA type derived from %.200s or %.200s",
+ RNA_struct_ui_name(&RNA_ID), RNA_struct_ui_name(&RNA_PropertyGroup));
+ return NULL;
+ }
if (bpy_prop_callback_check(update_cb, "update", 2) == -1) {
return NULL;
}
-
+ if (bpy_prop_callback_check(poll_cb, "poll", 2) == -1) {
+ return NULL;
+ }
prop = RNA_def_pointer_runtime(srna, id, ptype, name ? name : id, description);
if (pyopts) {
bpy_prop_assign_flag(prop, opts);
}
+
+ if (RNA_struct_idprops_contains_datablock(ptype)) {
+ if (RNA_struct_is_a(srna, &RNA_PropertyGroup)) {
+ RNA_def_struct_flag(srna, STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES);
+ }
+ }
bpy_prop_callback_assign_update(prop, update_cb);
+ bpy_prop_callback_assign_pointer(prop, poll_cb);
RNA_def_property_duplicate_pointers(srna, prop);
}
Py_RETURN_NONE;
@@ -2879,7 +2947,7 @@ BPY_PROPDEF_NAME_DOC
BPY_PROPDEF_DESC_DOC
BPY_PROPDEF_OPTIONS_DOC
);
-static PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw)
+PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw)
{
StructRNA *srna;
@@ -2910,10 +2978,23 @@ static PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject
if (!ptype)
return NULL;
+ if (!RNA_struct_is_a(ptype, &RNA_PropertyGroup)) {
+ PyErr_Format(PyExc_TypeError,
+ "CollectionProperty(...) expected an RNA type derived from %.200s",
+ RNA_struct_ui_name(&RNA_ID), RNA_struct_ui_name(&RNA_PropertyGroup));
+ return NULL;
+ }
+
prop = RNA_def_collection_runtime(srna, id, ptype, name ? name : id, description);
if (pyopts) {
bpy_prop_assign_flag(prop, opts);
}
+
+ if (RNA_struct_idprops_contains_datablock(ptype)) {
+ if (RNA_struct_is_a(srna, &RNA_PropertyGroup)) {
+ RNA_def_struct_flag(srna, STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES);
+ }
+ }
RNA_def_property_duplicate_pointers(srna, prop);
}
Py_RETURN_NONE;
diff --git a/source/blender/python/intern/bpy_props.h b/source/blender/python/intern/bpy_props.h
index c9934ca0cf3..614c1b4b708 100644
--- a/source/blender/python/intern/bpy_props.h
+++ b/source/blender/python/intern/bpy_props.h
@@ -30,6 +30,10 @@
PyObject *BPY_rna_props(void);
+PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw);
+PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw);
+StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix);
+
#define PYRNA_STACK_ARRAY 32
#endif
diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c
index 2fd46ab94f0..00627030db3 100644
--- a/source/blender/python/intern/bpy_rna.c
+++ b/source/blender/python/intern/bpy_rna.c
@@ -1934,16 +1934,10 @@ static int pyrna_py_to_prop(
}
else {
/* data == NULL, assign to RNA */
- if (value == Py_None) {
- PointerRNA valueptr = {{NULL}};
- RNA_property_pointer_set(ptr, prop, valueptr);
- }
- else if (RNA_struct_is_a(param->ptr.type, ptr_type)) {
- RNA_property_pointer_set(ptr, prop, param->ptr);
- }
- else {
+ if (value == Py_None || RNA_struct_is_a(param->ptr.type, ptr_type))
+ RNA_property_pointer_set(ptr, prop, value == Py_None ? PointerRNA_NULL : param->ptr);
+ else
raise_error = true;
- }
}
if (raise_error) {
@@ -3277,6 +3271,16 @@ static int pyrna_struct_ass_subscript(BPy_StructRNA *self, PyObject *key, PyObje
return -1;
}
+ BPy_StructRNA *val = (BPy_StructRNA *)value;
+ if (val && self->ptr.type && val->ptr.type) {
+ if (!RNA_struct_idprops_datablock_allowed(self->ptr.type) &&
+ RNA_struct_idprops_contains_datablock(val->ptr.type))
+ {
+ PyErr_SetString(PyExc_TypeError, "bpy_struct[key] = val: datablock id properties not supported for this type");
+ return -1;
+ }
+ }
+
return BPy_Wrap_SetMapItem(group, key, value);
}
@@ -6745,7 +6749,7 @@ PyObject *pyrna_id_CreatePyObject(ID *id)
bool pyrna_id_FromPyObject(PyObject *obj, ID **id)
{
- if (BPy_StructRNA_Check(obj) && (RNA_struct_is_ID(((BPy_StructRNA *)obj)->ptr.type))) {
+ if (pyrna_id_CheckPyObject(obj)) {
*id = ((BPy_StructRNA *)obj)->ptr.id.data;
return true;
}
@@ -6755,6 +6759,11 @@ bool pyrna_id_FromPyObject(PyObject *obj, ID **id)
}
}
+bool pyrna_id_CheckPyObject(PyObject *obj)
+{
+ return BPy_StructRNA_Check(obj) && (RNA_struct_is_ID(((BPy_StructRNA *) obj)->ptr.type));
+}
+
void BPY_rna_init(void)
{
#ifdef USE_MATHUTILS /* register mathutils callbacks, ok to run more than once. */
@@ -7089,6 +7098,21 @@ static int deferred_register_prop(StructRNA *srna, PyObject *key, PyObject *item
args_fake = PyTuple_New(1);
PyTuple_SET_ITEM(args_fake, 0, py_srna_cobject);
+ PyObject *type = PyDict_GetItemString(py_kw, "type");
+ StructRNA *type_srna = srna_from_self(type, "");
+ if (type_srna) {
+ if (!RNA_struct_idprops_datablock_allowed(srna) &&
+ (*(PyCFunctionWithKeywords)PyCFunction_GET_FUNCTION(py_func) == BPy_PointerProperty ||
+ *(PyCFunctionWithKeywords)PyCFunction_GET_FUNCTION(py_func) == BPy_CollectionProperty) &&
+ RNA_struct_idprops_contains_datablock(type_srna))
+ {
+ PyErr_Format(PyExc_ValueError,
+ "bpy_struct \"%.200s\" doesn't support datablock properties \n",
+ RNA_struct_identifier(srna));
+ return -1;
+ }
+ }
+
py_ret = PyObject_Call(py_func, args_fake, py_kw);
if (py_ret) {
diff --git a/source/blender/python/intern/bpy_rna.h b/source/blender/python/intern/bpy_rna.h
index e38d4f095d6..605f79b1ad8 100644
--- a/source/blender/python/intern/bpy_rna.h
+++ b/source/blender/python/intern/bpy_rna.h
@@ -179,6 +179,7 @@ PyObject *pyrna_prop_CreatePyObject(PointerRNA *ptr, PropertyRNA *prop);
/* extern'd by other modules which don't deal closely with RNA */
PyObject *pyrna_id_CreatePyObject(struct ID *id);
bool pyrna_id_FromPyObject(PyObject *obj, struct ID **id);
+bool pyrna_id_CheckPyObject(PyObject *obj);
/* operators also need this to set args */
int pyrna_pydict_to_props(PointerRNA *ptr, PyObject *kw, const bool all_args, const char *error_prefix);
diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c
index 4351cd22b18..d0522fdd7d4 100644
--- a/source/blender/windowmanager/intern/wm.c
+++ b/source/blender/windowmanager/intern/wm.c
@@ -489,7 +489,7 @@ void wm_close_and_free_all(bContext *C, ListBase *wmlist)
while ((wm = wmlist->first)) {
wm_close_and_free(C, wm);
BLI_remlink(wmlist, wm);
- BKE_libblock_free_data(bmain, &wm->id);
+ BKE_libblock_free_data(bmain, &wm->id, true);
MEM_freeN(wm);
}
}
diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt
index 5e02b05f995..cd1ce1ea47d 100644
--- a/tests/python/CMakeLists.txt
+++ b/tests/python/CMakeLists.txt
@@ -91,6 +91,10 @@ add_test(script_pyapi_idprop ${TEST_BLENDER_EXE}
--python ${CMAKE_CURRENT_LIST_DIR}/bl_pyapi_idprop.py
)
+add_test(script_pyapi_idprop_datablock ${TEST_BLENDER_EXE}
+ --python ${CMAKE_CURRENT_LIST_DIR}/bl_pyapi_idprop_datablock.py
+)
+
# ------------------------------------------------------------------------------
# MODELING TESTS
add_test(bevel ${TEST_BLENDER_EXE}
diff --git a/tests/python/bl_pyapi_idprop_datablock.py b/tests/python/bl_pyapi_idprop_datablock.py
new file mode 100644
index 00000000000..4acfb83bd95
--- /dev/null
+++ b/tests/python/bl_pyapi_idprop_datablock.py
@@ -0,0 +1,338 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# 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.
+#
+# 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.
+#
+# 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.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+import bpy
+import sys
+import os
+import tempfile
+import traceback
+import inspect
+from bpy.types import UIList
+
+arr_len = 100
+ob_cp_count = 100
+lib_path = os.path.join(tempfile.gettempdir(), "lib.blend")
+test_path = os.path.join(tempfile.gettempdir(), "test.blend")
+
+
+def print_fail_msg_and_exit(msg):
+ def __LINE__():
+ try:
+ raise Exception
+ except:
+ return sys.exc_info()[2].tb_frame.f_back.f_back.f_back.f_lineno
+
+ def __FILE__():
+ return inspect.currentframe().f_code.co_filename
+
+ print("'%s': %d >> %s" % (__FILE__(), __LINE__(), msg), file=sys.stderr)
+ sys.stderr.flush()
+ sys.stdout.flush()
+ os._exit(1)
+
+
+def abort_if_false(expr, msg=None):
+ if not expr:
+ if not msg:
+ msg = "test failed"
+ print_fail_msg_and_exit(msg)
+
+
+class TestClass(bpy.types.PropertyGroup):
+ test_prop = bpy.props.PointerProperty(type=bpy.types.Object)
+ name = bpy.props.StringProperty()
+
+
+def get_scene(lib_name, sce_name):
+ for s in bpy.data.scenes:
+ if s.name == sce_name:
+ if (s.library and s.library.name == lib_name) or \
+ (lib_name == None and s.library == None):
+ return s
+
+
+def check_crash(fnc, args=None):
+ try:
+ fnc(args) if args else fnc()
+ except:
+ return
+ print_fail_msg_and_exit("test failed")
+
+
+def init():
+ bpy.utils.register_class(TestClass)
+ bpy.types.Object.prop_array = bpy.props.CollectionProperty(
+ name="prop_array",
+ type=TestClass)
+ bpy.types.Object.prop = bpy.props.PointerProperty(type=bpy.types.Object)
+
+
+def make_lib():
+ bpy.ops.wm.read_factory_settings()
+
+ # datablock pointer to the Camera object
+ bpy.data.objects["Cube"].prop = bpy.data.objects['Camera']
+
+ # array of datablock pointers to the Lamp object
+ for i in range(0, arr_len):
+ a = bpy.data.objects["Cube"].prop_array.add()
+ a.test_prop = bpy.data.objects['Lamp']
+ a.name = a.test_prop.name
+
+ # make unique named copy of the cube
+ ob = bpy.data.objects["Cube"].copy()
+ bpy.context.scene.objects.link(ob)
+
+ bpy.data.objects["Cube.001"].name = "Unique_Cube"
+
+ # duplicating of Cube
+ for i in range(0, ob_cp_count):
+ ob = bpy.data.objects["Cube"].copy()
+ bpy.context.scene.objects.link(ob)
+
+ # nodes
+ bpy.data.scenes["Scene"].use_nodes = True
+ bpy.data.scenes["Scene"].node_tree.nodes['Render Layers']["prop"] =\
+ bpy.data.objects['Camera']
+
+ # rename scene and save
+ bpy.data.scenes["Scene"].name = "Scene_lib"
+ bpy.ops.wm.save_as_mainfile(filepath=lib_path)
+
+
+def check_lib():
+ # check pointer
+ abort_if_false(bpy.data.objects["Cube"].prop == bpy.data.objects['Camera'])
+
+ # check array of pointers in duplicated object
+ for i in range(0, arr_len):
+ abort_if_false(bpy.data.objects["Cube.001"].prop_array[i].test_prop ==
+ bpy.data.objects['Lamp'])
+
+
+def check_lib_linking():
+ # open startup file
+ bpy.ops.wm.read_factory_settings()
+
+ # link scene to the startup file
+ with bpy.data.libraries.load(lib_path, link=True) as (data_from, data_to):
+ data_to.scenes = ["Scene_lib"]
+
+ o = bpy.data.scenes["Scene_lib"].objects['Unique_Cube']
+
+ abort_if_false(o.prop_array[0].test_prop == bpy.data.scenes["Scene_lib"].objects['Lamp'])
+ abort_if_false(o.prop == bpy.data.scenes["Scene_lib"].objects['Camera'])
+ abort_if_false(o.prop.library == o.library)
+
+ bpy.ops.wm.save_as_mainfile(filepath=test_path)
+
+
+def check_linked_scene_copying():
+ # full copy of the scene with datablock props
+ bpy.ops.wm.open_mainfile(filepath=test_path)
+ bpy.data.screens['Default'].scene = bpy.data.scenes["Scene_lib"]
+ bpy.ops.scene.new(type='FULL_COPY')
+
+ # check save/open
+ bpy.ops.wm.save_as_mainfile(filepath=test_path)
+ bpy.ops.wm.open_mainfile(filepath=test_path)
+
+ intern_sce = get_scene(None, "Scene_lib")
+ extern_sce = get_scene("Lib", "Scene_lib")
+
+ # check node's props
+ # we made full copy from linked scene, so pointers must equal each other
+ abort_if_false(intern_sce.node_tree.nodes['Render Layers']["prop"] and
+ intern_sce.node_tree.nodes['Render Layers']["prop"] ==
+ extern_sce.node_tree.nodes['Render Layers']["prop"])
+
+
+def check_scene_copying():
+ # full copy of the scene with datablock props
+ bpy.ops.wm.open_mainfile(filepath=lib_path)
+ bpy.data.screens['Default'].scene = bpy.data.scenes["Scene_lib"]
+ bpy.ops.scene.new(type='FULL_COPY')
+
+ path = test_path + "_"
+ # check save/open
+ bpy.ops.wm.save_as_mainfile(filepath=path)
+ bpy.ops.wm.open_mainfile(filepath=path)
+
+ first_sce = get_scene(None, "Scene_lib")
+ second_sce = get_scene(None, "Scene_lib.001")
+
+ # check node's props
+ # must point to own scene camera
+ abort_if_false(not (first_sce.node_tree.nodes['Render Layers']["prop"] ==
+ second_sce.node_tree.nodes['Render Layers']["prop"]))
+
+
+# count users
+def test_users_counting():
+ bpy.ops.wm.read_factory_settings()
+ lamp_us = bpy.data.objects["Lamp"].data.users
+ n = 1000
+ for i in range(0, n):
+ bpy.data.objects["Cube"]["a%s" % i] = bpy.data.objects["Lamp"].data
+ abort_if_false(bpy.data.objects["Lamp"].data.users == lamp_us + n)
+
+ for i in range(0, int(n / 2)):
+ bpy.data.objects["Cube"]["a%s" % i] = 1
+ abort_if_false(bpy.data.objects["Lamp"].data.users == lamp_us + int(n / 2))
+
+
+# linking
+def test_linking():
+ make_lib()
+ check_lib()
+ check_lib_linking()
+ check_linked_scene_copying()
+ check_scene_copying()
+
+
+# check restrictions for datablock pointers for some classes; GUI for manual testing
+def test_restrictions1():
+ class TEST_Op(bpy.types.Operator):
+ bl_idname = 'scene.test_op'
+ bl_label = 'Test'
+ bl_options = {"INTERNAL"}
+ str_prop = bpy.props.StringProperty(name="str_prop")
+
+ # disallow registration of datablock properties in operators
+ # will be checked in the draw method (test manually)
+ # also, see console:
+ # ValueError: bpy_struct "SCENE_OT_test_op" doesn't support datablock properties
+ id_prop = bpy.props.PointerProperty(type=bpy.types.Object)
+
+ def execute(self, context):
+ return {'FINISHED'}
+
+ # just panel for testing the poll callback with lots of objects
+ class TEST_PT_DatablockProp(bpy.types.Panel):
+ bl_label = "Datablock IDProp"
+ bl_space_type = "PROPERTIES"
+ bl_region_type = "WINDOW"
+ bl_context = "render"
+
+ def draw(self, context):
+ self.layout.prop_search(context.scene, "prop", bpy.data,
+ "objects")
+ self.layout.template_ID(context.scene, "prop1")
+ self.layout.prop_search(context.scene, "prop2", bpy.data, "node_groups")
+
+ op = self.layout.operator("scene.test_op")
+ op.str_prop = "test string"
+
+ def test_fnc(op):
+ op["ob"] = bpy.data.objects['Unique_Cube']
+ check_crash(test_fnc, op)
+ abort_if_false(not hasattr(op, "id_prop"))
+
+ bpy.utils.register_class(TEST_PT_DatablockProp)
+ bpy.utils.register_class(TEST_Op)
+
+ def poll(self, value):
+ return value.name in bpy.data.scenes["Scene_lib"].objects
+
+ def poll1(self, value):
+ return True
+
+ bpy.types.Scene.prop = bpy.props.PointerProperty(type=bpy.types.Object)
+ bpy.types.Scene.prop1 = bpy.props.PointerProperty(type=bpy.types.Object, poll=poll)
+ bpy.types.Scene.prop2 = bpy.props.PointerProperty(type=bpy.types.NodeTree, poll=poll1)
+
+ # check poll effect on UI (poll returns false => red alert)
+ bpy.context.scene.prop = bpy.data.objects["Lamp.001"]
+ bpy.context.scene.prop1 = bpy.data.objects["Lamp.001"]
+
+ # check incorrect type assignment
+ def sub_test():
+ # NodeTree id_prop
+ bpy.context.scene.prop2 = bpy.data.objects["Lamp.001"]
+
+ check_crash(sub_test)
+
+ bpy.context.scene.prop2 = bpy.data.node_groups.new("Shader", "ShaderNodeTree")
+
+ print("Please, test GUI performance manually on the Render tab, '%s' panel" %
+ TEST_PT_DatablockProp.bl_label, file=sys.stderr)
+ sys.stderr.flush()
+
+
+# check some possible regressions
+def test_regressions():
+ bpy.types.Object.prop_str = bpy.props.StringProperty(name="str")
+ bpy.data.objects["Unique_Cube"].prop_str = "test"
+
+ bpy.types.Object.prop_gr = bpy.props.PointerProperty(
+ name="prop_gr",
+ type=TestClass,
+ description="test")
+
+ bpy.data.objects["Unique_Cube"].prop_gr = None
+
+
+# test restrictions for datablock pointers
+def test_restrictions2():
+ class TestClassCollection(bpy.types.PropertyGroup):
+ prop = bpy.props.CollectionProperty(
+ name="prop_array",
+ type=TestClass)
+ bpy.utils.register_class(TestClassCollection)
+
+ class TestPrefs(bpy.types.AddonPreferences):
+ bl_idname = "testprefs"
+ # expecting crash during registering
+ my_prop2 = bpy.props.PointerProperty(type=TestClass)
+
+ prop = bpy.props.PointerProperty(
+ name="prop",
+ type=TestClassCollection,
+ description="test")
+
+ bpy.types.Addon.a = bpy.props.PointerProperty(type=bpy.types.Object)
+
+ class TestUIList(UIList):
+ test = bpy.props.PointerProperty(type=bpy.types.Object)
+ def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
+ layout.prop(item, "name", text="", emboss=False, icon_value=icon)
+
+ check_crash(bpy.utils.register_class, TestPrefs)
+ check_crash(bpy.utils.register_class, TestUIList)
+
+ bpy.utils.unregister_class(TestClassCollection)
+
+
+def main():
+ init()
+ test_users_counting()
+ test_linking()
+ test_restrictions1()
+ check_crash(test_regressions)
+ test_restrictions2()
+
+
+if __name__ == "__main__":
+ try:
+ main()
+ except:
+ import traceback
+
+ traceback.print_exc()
+ sys.stderr.flush()
+ os._exit(1)