diff options
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, <->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) |