clever node tree modify and restore - how to?

having as a starting condition the render tree with his camera cam in the scengraf, suppose, as happens every time in our scripts, to modify this condition with the wide range of possibilities Panda3D offer, applying modifiers to camera, lens, changing position, aspect ratio, adding filters to the tree etc. and then we want to restore our scene to that starting condition: is there a clean and fast way to achieve this, maybe in a couple of shots, as like TEMPrender=render, …(modify render tree), render=TEMPrender ?

The “TEMPrender = render” won’t work (since you’re only copying a reference to a NodePath), instead, create a temporary node and copy render to it:

originalScene = NodePath("TEMPrender")
render.node().copySubgraph(originalScene.node())

Whenever you need to restore, do something like:

render.node().removeAllChildren()
render.node().stealChildren(originalScene.node())

I didn’t test it or so, so I can’t guarantee that this works.

in 1.5.4 your code have to be:

originalScene = render.node().copySubgraph()
...
render.node().removeAllChildren() 
render.node().stealChildren(originalScene)

but yep pro_rsoft this is a try I already did and don’t work, 'cos if I try to load i.e. a model after that I can’t see it - this is a piece of code to toy with:

'''
'''
from pandac.PandaModules import *
from direct.directbase.DirectStart import *
from direct.actor.Actor import Actor
from direct.showbase.DirectObject import DirectObject
from direct.gui.OnscreenText import OnscreenText

def loadact():
  global actor
  print ">>>load actor"
  actor = Actor('panda', {'walk' : 'panda-walk'})
  actor.reparentTo(render)
  actor.loop("walk")

def reset():
  #restoring render default situation
  global originalScene
  print ">>>reset"
  render.node().removeAllChildren()
  render.node().stealChildren(originalScene)
  renderlist()

def renderlist():
  print
  render.ls()

#default scene setup
dlight = NodePath(DirectionalLight('dlight'))
dlight.reparentTo(base.cam)
render.setLight(dlight)
base.disableMouse()
camera.setPosHpr(-41, -23, 18, -61, -15, 0)
OnscreenText(text="C to cleanup\nA to load the actor again\nL to list render", style=1, fg=(1,1,1,1), pos=(-1.3, 0.9), align=TextNode.ALeft, scale = .05)
actor=None

#render preservation
renderlist()
originalScene = render.node().copySubgraph()

#--------------------------------------------
DO=DirectObject()
DO.accept("c", reset)
DO.accept("C", reset)
DO.accept("a", loadact)
DO.accept("A", loadact)
DO.accept("l", renderlist)
DO.accept("L", renderlist)

loadact()

run()

anyhow I guess we’re close to have it working and there is something we miss to do to refresh or who knows what to make the render node shows again to the window.

Remember also that there’s nothing particularly special about the “render” node itself–it just happens to be the root of the scene into which the camera has been parented. If your goal is to temporarily set the camera to view another scene, it’s easier just to reparent your camera into that other scene!

David

my need is more a total unaware backoff to the render and render2d default situations and I’m finding this thing a mayor PITA because the lack of a copy feature who duplicate nodes and their instances and not just the references. The pro_rsoft proposal is the closest I found so far but, even if the tree looks rebuilt as before (as showed by .ls()), everything reparented after the render.node().stealChildren(originalScene) line is no more visible to the main showbase window.

ok then, looks like I gotcha …
*** EDIT ***
no, I was wrong indeed

The code below is a try to preserve the render subnode I set a default scene with a light and a smiley on the middle.
.Click A lo load a panda actor in the scene and shift a little the smiley from his default position; you may also move by hand the cam with the mouse, just to shift it from the default position
.Click then C to rollback the scene to its default situation with just the light and the smiley.
You’ll see the panda disappear but the cam is not restored at its default position and hpr and the same goes for the smiley so it’s clear that .copyChildren() don’t do a real instance copy as I would expect and need.

from pandac.PandaModules import *
from direct.directbase.DirectStart import *
from direct.actor.Actor import Actor
from direct.showbase.DirectObject import DirectObject
from direct.gui.OnscreenText import OnscreenText

def loadact():
  global actor, smiley
  print ">>>load actor"
  actor = Actor('panda', {'walk' : 'panda-walk'})
  actor.setScale(0.25,0.25,0.25)
  actor.setPos(0,25,.7)
  actor.reparentTo(render)
  actor.loop("walk")
  #shift also the smiley default pos
  smiley.setPosHpr(-1, 23, .5, -61, -15, 0)
  
def reset():
  #restoring render default situation
  global originalScene
  print ">>>reset"
  render.node().removeAllChildren()
  render.node().copyChildren(originalScene)

def renderlist():
  print
  render.ls()

#--------------------------------------------
#Default scene setup
dlight = NodePath(DirectionalLight('dlight'))
dlight.reparentTo(base.cam)
render.setLight(dlight)
smiley = loader.loadModel('smiley')
smiley.reparentTo(render)
smiley.setPos(0, 25,0)
smileyintv=smiley.hprInterval(120,Vec3(0,-10000,0))
camera.setPosHpr(-41, -23, 18, -61, -15, 0)
OnscreenText(text='''C to cleanup the scene
A to load the actor
L to get render info''', style=1, fg=(1,1,1,1), pos=(-1.3, 0.9), align=TextNode.ALeft, scale = .05)
actor=None

#render subtree preservation
originalScene = PandaNode('TEMPrend')
originalScene.copyChildren(render.node())

#--------------------------------------------
DO=DirectObject()
DO.accept("c", reset)
DO.accept("C", reset)
DO.accept("a", loadact)
DO.accept("A", loadact)
DO.accept("l", renderlist)
DO.accept("L", renderlist)

run()

As copyChildren says :
“Makes another instance of all the children of the other node, copying them to this node.”
so, it’s just instances, not copies.

Eventhough you make an independent copy of render, it has no use anyway, since all original references will be lost once you remove them, and whatever you do afterward won’t be applied to the new copy.

What purpose do you need this for ?

I’d do it by cleaning up (not simply remove all children, see HERE) and recreate the original from scratch.
This way, all things will be built naturally.

correct me if I’m wrong but with instance, I always meant “the object in memory” after his instantiation, so copying it I would expect another identical object with other memory room used of the same size. The copyChildren appears instead to make a copy of a ‘reference’ of each node, and with reference I always meant the pointer of the instance of an object in memory. Therefore if I modify a cam or whatever else after my (pretend to be) preservation with copyChildren I can’t see restored the former default situation but just the node tree. The objects on the tree are just the same I modified afterwards the preservation with copyChildren.

And now I understand why the try with copySubgraph() has failed! that method indeed make a real instance duplication but when I try to restore the render tree with stealChildren() the engine won’t show the former stuff anymore.

yep ynjh_jo I passed here many times and snatched and studied your IDE crusher but for some reason I always find very difficult to manage your code (in general) and I ain’t succeeded to fit the crusher into my app but you bet I found in it lot of great inspiration to not get totally stuck and in a way or another I will do a similar thing on my way. I was just asking if there where a better and clean way to do the same thing that maybe I missed out. Anyway thank you for your time

As a language python by default does copying by passing references for most objects, so in place operations behave differently then expected.

Check out the built in copy module, it may have what you need. I have no idea whether it will work with nodes/nodepaths and their c++ parts, but who knows?

well Thaago that was not in question, I know that.

beside the use of copy we already spoke about that and don’t work:

I’m pretty convinced there are no better ways than to make complicated routines to restore a default scene. Hope soon to finish and post stuff here but do not expect to see a ‘clever’ piece of code.

Doesn’t instance mean a pointer to an existing object ??

When I tried your scene with copySubGraph, I got the original rendered by this :

base.camNode.setScene(render)

How did it fail ?
How did you do it ?

not as I was used to know - this is the best (and authoritative) definition I’d found (HERE)

that that you’re referring to is usually called ‘reference’, that is the common way python ‘see’ objects, as correctly pointed out by Thaago.

was not your code fault but my fault to not been able to put your crusher code into my application. Anyhow as I said I’m searching a more simple way to achieve that goal. (simple for me)

sorry to not understand you here - what you mean with I got the original rendered by this?

*** EDIT ***
sheesh now I got it: adding this after the stealChildren I see the scene now - outstanding ynjh_jo!


The term “instance” there of course is P3D’s term, not OOP.