diff --git a/panda/src/pgraph/lightAttrib.cxx b/panda/src/pgraph/lightAttrib.cxx index 9bc99674f8..9820f1bcfa 100644 --- a/panda/src/pgraph/lightAttrib.cxx +++ b/panda/src/pgraph/lightAttrib.cxx @@ -443,6 +443,36 @@ remove_on_light(const NodePath &light) const { return return_new(attrib); } +/** + * Returns a new LightAttrib, just like this one, but with the indicated light + * replaced with the given other light. + */ +CPT(RenderAttrib) LightAttrib:: +replace_on_light(const NodePath &source, const NodePath &dest) const { + if (source == dest) { + return this; + } + + nassertr(!source.is_empty(), this); + Light *slobj = source.node()->as_light(); + nassertr(slobj != nullptr, this); + + nassertr(!dest.is_empty(), this); + Light *dlobj = dest.node()->as_light(); + nassertr(dlobj != nullptr, this); + + LightAttrib *attrib = new LightAttrib(*this); + + auto it = attrib->_on_lights.find(source); + if (it != attrib->_on_lights.end()) { + dlobj->attrib_ref(); + slobj->attrib_unref(); + + *it = dest; + } + return return_new(attrib); +} + /** * Returns a new LightAttrib, just like this one, but with the indicated light * added to the list of lights turned off by this attrib. @@ -475,6 +505,36 @@ remove_off_light(const NodePath &light) const { return return_new(attrib); } +/** + * Returns a new LightAttrib, just like this one, but with the indicated light + * replaced with the given other light. + */ +CPT(RenderAttrib) LightAttrib:: +replace_off_light(const NodePath &source, const NodePath &dest) const { + if (source == dest) { + return this; + } + + nassertr(!source.is_empty(), this); + Light *slobj = source.node()->as_light(); + nassertr(slobj != nullptr, this); + + nassertr(!dest.is_empty(), this); + Light *dlobj = dest.node()->as_light(); + nassertr(dlobj != nullptr, this); + + LightAttrib *attrib = new LightAttrib(*this); + + auto it = attrib->_off_lights.find(source); + if (it != attrib->_off_lights.end()) { + dlobj->attrib_ref(); + slobj->attrib_unref(); + + *it = dest; + } + return return_new(attrib); +} + /** * Returns the most important light (that is, the light with the highest * priority) in the LightAttrib, excluding any ambient lights. Returns an diff --git a/panda/src/pgraph/lightAttrib.h b/panda/src/pgraph/lightAttrib.h index 4872ebc623..361a4a6c1e 100644 --- a/panda/src/pgraph/lightAttrib.h +++ b/panda/src/pgraph/lightAttrib.h @@ -85,8 +85,10 @@ PUBLISHED: CPT(RenderAttrib) add_on_light(const NodePath &light) const; CPT(RenderAttrib) remove_on_light(const NodePath &light) const; + CPT(RenderAttrib) replace_on_light(const NodePath &source, const NodePath &dest) const; CPT(RenderAttrib) add_off_light(const NodePath &light) const; CPT(RenderAttrib) remove_off_light(const NodePath &light) const; + CPT(RenderAttrib) replace_off_light(const NodePath &source, const NodePath &dest) const; NodePath get_most_important_light() const; LColor get_ambient_contribution() const; diff --git a/panda/src/pgraph/loader.cxx b/panda/src/pgraph/loader.cxx index ab4db6ea2f..15b130592d 100644 --- a/panda/src/pgraph/loader.cxx +++ b/panda/src/pgraph/loader.cxx @@ -292,7 +292,7 @@ try_load_file(const Filename &pathname, const LoaderOptions &options, << "Model " << pathname << " found in ModelPool.\n"; } // But return a deep copy of the shared model. - node = node->copy_subgraph(); + node = NodePath(node).copy_to(NodePath()).node(); } return node; } @@ -329,7 +329,7 @@ try_load_file(const Filename &pathname, const LoaderOptions &options, // from the RAM cached version. ModelPool::add_model(pathname, model_root); if ((options.get_flags() & LoaderOptions::LF_allow_instance) == 0) { - return model_root->copy_subgraph(); + return NodePath(model_root).copy_to(NodePath()).node(); } } } @@ -398,7 +398,7 @@ try_load_file(const Filename &pathname, const LoaderOptions &options, // cached version. ModelPool::add_model(pathname, DCAST(ModelRoot, result.p())); if ((options.get_flags() & LoaderOptions::LF_allow_instance) == 0) { - result = result->copy_subgraph(); + result = NodePath(result).copy_to(NodePath()).node(); } } diff --git a/panda/src/pgraph/nodePath.cxx b/panda/src/pgraph/nodePath.cxx index 1748942679..f955b9c380 100644 --- a/panda/src/pgraph/nodePath.cxx +++ b/panda/src/pgraph/nodePath.cxx @@ -539,12 +539,45 @@ copy_to(const NodePath &other, int sort, Thread *current_thread) const { nassertr(other._error_type == ET_ok, fail()); PandaNode *source_node = node(); - PT(PandaNode) copy_node = source_node->copy_subgraph(current_thread); + PandaNode::InstanceMap inst_map; + PT(PandaNode) copy_node = source_node->r_copy_subgraph(inst_map, current_thread); nassertr(copy_node != nullptr, fail()); copy_node->reset_prev_transform(current_thread); - return other.attach_new_node(copy_node, sort, current_thread); + NodePath result = other.attach_new_node(copy_node, sort, current_thread); + + // Temporary hack fix: if this root NodePath had lights applied that are + // located inside this subgraph, we need to fix them. + const RenderState *state = source_node->get_state(); + const LightAttrib *lattr; + if (state->get_attrib(lattr)) { + CPT(LightAttrib) new_lattr = lattr; + + for (size_t i = 0; i < lattr->get_num_off_lights(); ++i) { + NodePath light = lattr->get_off_light(i); + NodePath light2 = light; + + if (light2.replace_copied_nodes(*this, result, inst_map, current_thread)) { + new_lattr = DCAST(LightAttrib, new_lattr->replace_off_light(light, light2)); + } + } + + for (size_t i = 0; i < lattr->get_num_on_lights(); ++i) { + NodePath light = lattr->get_on_light(i); + NodePath light2 = light; + + if (light2.replace_copied_nodes(*this, result, inst_map, current_thread)) { + new_lattr = DCAST(LightAttrib, new_lattr->replace_on_light(light, light2)); + } + } + + if (new_lattr != lattr) { + result.set_state(state->set_attrib(std::move(new_lattr))); + } + } + + return result; } /** @@ -5803,6 +5836,48 @@ decode_from_bam_stream(vector_uchar data, BamReader *reader) { return result; } +/** + * If the given root node is an ancestor of this NodePath, replaces all + * components below it using the given instance map. + * + * This is a helper method used by copy_to(). + */ +bool NodePath:: +replace_copied_nodes(const NodePath &source, const NodePath &dest, + const PandaNode::InstanceMap &inst_map, + Thread *current_thread) { + nassertr(!dest.is_empty(), false); + + int pipeline_stage = current_thread->get_pipeline_stage(); + + pvector nodes; + + NodePathComponent *comp = _head; + while (comp != nullptr && comp != source._head) { + nodes.push_back(comp->get_node()); + + comp = comp->get_next(pipeline_stage, current_thread); + } + + if (comp == nullptr) { + // The given source NodePath isn't an ancestor of this NodePath. + return false; + } + + // Start at the dest NodePath and compose the new NodePath. + PT(NodePathComponent) new_comp = dest._head; + pvector::reverse_iterator it; + for (it = nodes.rbegin(); it != nodes.rend(); ++it) { + PandaNode::InstanceMap::const_iterator iit = inst_map.find(*it); + nassertr_always(iit != inst_map.end(), false); + new_comp = PandaNode::get_component(new_comp, iit->second, pipeline_stage, current_thread); + } + + nassertr(new_comp != nullptr, false); + _head = std::move(new_comp); + return true; +} + /** * Walks up from both NodePaths to find the first node that both have in * common, if any. Fills a_count and b_count with the number of nodes below diff --git a/panda/src/pgraph/nodePath.h b/panda/src/pgraph/nodePath.h index eaec935a29..187129d30c 100644 --- a/panda/src/pgraph/nodePath.h +++ b/panda/src/pgraph/nodePath.h @@ -952,6 +952,10 @@ PUBLISHED: static NodePath decode_from_bam_stream(vector_uchar data, BamReader *reader = nullptr); private: + bool replace_copied_nodes(const NodePath &source, const NodePath &dest, + const PandaNode::InstanceMap &inst_map, + Thread *current_thread); + static NodePathComponent * find_common_ancestor(const NodePath &a, const NodePath &b, int &a_count, int &b_count,