Browse Source

pgraph: add NodePath.replace_texture()

rdb 6 years ago
parent
commit
bac376541f

+ 60 - 0
panda/src/pgraph/nodePath.cxx

@@ -3136,6 +3136,19 @@ get_texture(TextureStage *stage) const {
   return nullptr;
   return nullptr;
 }
 }
 
 
+/**
+ * Recursively searches the scene graph for references to the given texture,
+ * and replaces them with the new texture.
+ */
+void NodePath::
+replace_texture(Texture *tex, Texture *new_tex) {
+  nassertv_always(!is_empty());
+  nassertv(tex != nullptr);
+  nassertv(new_tex != nullptr);
+
+  r_replace_texture(node(), tex, new_tex);
+}
+
 /**
 /**
  * Returns the sampler state that has been given for the base-level texture
  * Returns the sampler state that has been given for the base-level texture
  * that has been set on this particular node.  If no sampler state was given,
  * that has been set on this particular node.  If no sampler state was given,
@@ -6425,6 +6438,53 @@ r_find_all_textures(PandaNode *node, TextureStage *stage,
   }
   }
 }
 }
 
 
+/**
+ * Recursively replaces references to the given texture on this section of the
+ * scene graph with the given other texture.
+ */
+void NodePath::
+r_replace_texture(PandaNode *node, Texture *tex, Texture *new_tex) {
+  // Consider the state of the node itself.
+  {
+    CPT(RenderState) node_state = node->get_state();
+    const TextureAttrib *ta;
+    if (node_state->get_attrib(ta)) {
+      CPT(RenderAttrib) new_ta = ta->replace_texture(tex, new_tex);
+      if (new_ta != ta) {
+        node->set_state(node_state->set_attrib(new_ta));
+      }
+    }
+  }
+
+  // If this is a GeomNode, consider the state of any of its Geoms.
+  if (node->is_geom_node()) {
+    GeomNode *gnode;
+    DCAST_INTO_V(gnode, node);
+
+    int num_geoms = gnode->get_num_geoms();
+    for (int i = 0; i < num_geoms; i++) {
+      CPT(RenderState) geom_state = gnode->get_geom_state(i);
+
+      // Look for a TextureAttrib on the state.
+      const TextureAttrib *ta;
+      if (geom_state->get_attrib(ta)) {
+        CPT(RenderAttrib) new_ta = ta->replace_texture(tex, new_tex);
+        if (new_ta != ta) {
+          gnode->set_geom_state(i, geom_state->set_attrib(new_ta));
+        }
+      }
+    }
+  }
+
+  // Now consider children.
+  PandaNode::Children cr = node->get_children();
+  size_t num_children = cr.get_num_children();
+  for (size_t i = 0; i < num_children; ++i) {
+    PandaNode *child = cr.get_child(i);
+    r_replace_texture(child, tex, new_tex);
+  }
+}
+
 /**
 /**
  *
  *
  */
  */

+ 2 - 0
panda/src/pgraph/nodePath.h

@@ -620,6 +620,7 @@ PUBLISHED:
   bool has_texture_off(TextureStage *stage) const;
   bool has_texture_off(TextureStage *stage) const;
   Texture *get_texture() const;
   Texture *get_texture() const;
   Texture *get_texture(TextureStage *stage) const;
   Texture *get_texture(TextureStage *stage) const;
+  void replace_texture(Texture *tex, Texture *new_tex);
   const SamplerState &get_texture_sampler() const;
   const SamplerState &get_texture_sampler() const;
   const SamplerState &get_texture_sampler(TextureStage *stage) const;
   const SamplerState &get_texture_sampler(TextureStage *stage) const;
 
 
@@ -1003,6 +1004,7 @@ private:
   Texture *r_find_texture(PandaNode *node, TextureStage *stage) const;
   Texture *r_find_texture(PandaNode *node, TextureStage *stage) const;
   void r_find_all_textures(PandaNode *node, TextureStage *stage,
   void r_find_all_textures(PandaNode *node, TextureStage *stage,
                            Textures &textures) const;
                            Textures &textures) const;
+  static void r_replace_texture(PandaNode *node, Texture *tex, Texture *new_tex);
 
 
   typedef phash_set<TextureStage *, pointer_hash> TextureStages;
   typedef phash_set<TextureStage *, pointer_hash> TextureStages;
   TextureStage *r_find_texture_stage(PandaNode *node, const RenderState *state,
   TextureStage *r_find_texture_stage(PandaNode *node, const RenderState *state,

+ 26 - 0
panda/src/pgraph/textureAttrib.cxx

@@ -250,6 +250,32 @@ unify_texture_stages(TextureStage *stage) const {
   return return_new(attrib);
   return return_new(attrib);
 }
 }
 
 
+/**
+ * Returns a new TextureAttrib, just like this one, but with all references to
+ * the given texture replaced with the new texture.
+ */
+CPT(RenderAttrib) TextureAttrib::
+replace_texture(Texture *tex, Texture *new_tex) const {
+  TextureAttrib *attrib = nullptr;
+
+  for (size_t i = 0; i < _on_stages.size(); ++i) {
+    const StageNode &sn = _on_stages[i];
+    if (sn._texture == tex) {
+      if (attrib == nullptr) {
+        attrib = new TextureAttrib(*this);
+      }
+
+      attrib->_on_stages[i]._texture = new_tex;
+    }
+  }
+
+  if (attrib != nullptr) {
+    return return_new(attrib);
+  } else {
+    return this;
+  }
+}
+
 /**
 /**
  * Returns a new TextureAttrib, very much like this one, but with the number
  * Returns a new TextureAttrib, very much like this one, but with the number
  * of on_stages reduced to be no more than max_texture_stages.  The number of
  * of on_stages reduced to be no more than max_texture_stages.  The number of

+ 1 - 0
panda/src/pgraph/textureAttrib.h

@@ -89,6 +89,7 @@ PUBLISHED:
   CPT(RenderAttrib) add_off_stage(TextureStage *stage, int override = 0) const;
   CPT(RenderAttrib) add_off_stage(TextureStage *stage, int override = 0) const;
   CPT(RenderAttrib) remove_off_stage(TextureStage *stage) const;
   CPT(RenderAttrib) remove_off_stage(TextureStage *stage) const;
   CPT(RenderAttrib) unify_texture_stages(TextureStage *stage) const;
   CPT(RenderAttrib) unify_texture_stages(TextureStage *stage) const;
+  CPT(RenderAttrib) replace_texture(Texture *tex, Texture *new_tex) const;
 
 
 public:
 public:
   CPT(TextureAttrib) filter_to_max(int max_texture_stages) const;
   CPT(TextureAttrib) filter_to_max(int max_texture_stages) const;

+ 19 - 0
tests/pgraph/test_nodepath.py

@@ -149,3 +149,22 @@ def test_nodepath_python_tags():
     rc1 = sys.getrefcount(path.python_tags)
     rc1 = sys.getrefcount(path.python_tags)
     rc2 = sys.getrefcount(path.python_tags)
     rc2 = sys.getrefcount(path.python_tags)
     assert rc1 == rc2
     assert rc1 == rc2
+
+
+def test_nodepath_replace_texture():
+    from panda3d.core import NodePath, Texture
+
+    tex1 = Texture()
+    tex2 = Texture()
+
+    path1 = NodePath("node1")
+    path1.set_texture(tex1)
+    path1.replace_texture(tex1, tex2)
+    assert path1.get_texture() == tex2
+
+    path1 = NodePath("node1")
+    path2 = path1.attach_new_node("node2")
+    path2.set_texture(tex1)
+    path1.replace_texture(tex1, tex2)
+    assert not path1.has_texture()
+    assert path2.get_texture() == tex2