Scene Serialization/Deserialization in 1.10.6?

Hi, I’m using panda3d to build a simple point-cloud & landmark placement & tagging editor, to support a deep-learning point-cloud training pipeline (learning point-cloud segmentation & landmark placement).

So far panda3d has been pretty intuitive and a pleasure to use. However, I’m trying to serialize/de-serialize my scene and I’m running into problems. From what I can tell, the scene deserializes correctly, however, the camera controller seems completely broken.

Here’s my nodePath hierarchy before serialization:

PandaNode render S:(CullFaceAttrib LightAttrib RescaleNormalAttrib)
  ModelNode camera T:m(pos 0 0 180 hpr 180 -90 -180)
    Camera cam ( PerspectiveLens )
      PerspectiveLens fov = 39.3201 30
    CollisionNode mouseRay (1 solids) (hidden)
    DirectionalLight directional ( OrthographicLens ):
      color 1 1 1 1
      direction 0 1 0
  PandaNode landmarks
    PandaNode landmark_instance_0 [] T:(pos -30.1249 10.9554 -0.576294)
      ModelRoot sphere.egg S:(ColorAttrib)
        GeomNode Sphere (1 geoms: S:(ColorAttrib))
    PandaNode landmark_instance_1 [] T:(pos -29.1386 5.50018 -1.78893)
      ModelRoot sphere.egg S:(ColorAttrib)
        GeomNode Sphere (1 geoms: S:(ColorAttrib))
    PandaNode landmark_instance_2 [] T:(pos -29.1552 -1.79042 -3.34111)
      ModelRoot sphere.egg S:(ColorAttrib)
        GeomNode Sphere (1 geoms: S:(ColorAttrib))
  AmbientLight ambient:
    color 0.1 0.1 0.125 1
  GeomNode pointcloud_geom_node (1 geoms) S:(ColorAttrib RenderModeAttrib)
  ModelRoot target.obj
    PandaNode target.obj
      GeomNode defaultobject (1 geoms) S:(MaterialAttrib TextureAttrib)
None

Here’s my code to serialize my scene:

bamFile = BamFile()
bamFile.openWrite('test.bam')
bamFile.writeObject(base.render.node())

Here’s my code to deserialize my scene:

bamFile = BamFile()
bamFile.openRead('test.bam')
newSceneNode = bamFile.readNode()
self.render.clear()         
self.render = self.render.attach_new_node(newSceneNode)
print(self.render.ls())

and here’s what my scene hierarchy looks like after I run this code:

PandaNode render S:(CullFaceAttrib LightAttrib RescaleNormalAttrib)
  ModelNode camera T:m(pos 0 0 180 hpr 180 -90 -180)
    Camera cam ( PerspectiveLens )
      PerspectiveLens fov = 39.3201 30
    CollisionNode mouseRay (1 solids) (hidden)
    DirectionalLight directional ( OrthographicLens ):
      color 1 1 1 1
      direction 0 1 0
  PandaNode landmarks
    PandaNode landmark_instance_0 T:(pos -30.1249 10.9554 -0.576294)
      ModelRoot sphere.egg S:(ColorAttrib)
        GeomNode Sphere (1 geoms: S:(ColorAttrib))
    PandaNode landmark_instance_1 T:(pos -29.1386 5.50018 -1.78893)
      ModelRoot sphere.egg S:(ColorAttrib)
        GeomNode Sphere (1 geoms: S:(ColorAttrib))
    PandaNode landmark_instance_2 T:(pos -29.1552 -1.79042 -3.34111)
      ModelRoot sphere.egg S:(ColorAttrib)
        GeomNode Sphere (1 geoms: S:(ColorAttrib))
  AmbientLight ambient:
    color 0.1 0.1 0.125 1
  GeomNode pointcloud_geom_node (1 geoms) S:(ColorAttrib RenderModeAttrib)
  ModelRoot target.obj
    PandaNode target.obj
      GeomNode defaultobject (1 geoms) S:(MaterialAttrib TextureAttrib)
None

So, as far as I can tell, everything looks correct, but my panda3d window is blank and does not show/render anything after deserializing my .bam file.

What am I doing wrong?

Thanks!
Dustin

Hi, welcome to the forums!

It looks like you’ve serialized and deserialized your entire scene, including the camera. What’s happening is that when you load the new scene, Panda creates the new camera as it is defined in the file, but Panda is still using the old camera to render the scene, since you’ve not told Panda to render using the new camera.

There are two ways to fix this. The easiest way is to create a scene root with a node under render (to which you parent all your objects) and call writeBamFile on that sub-node of render, so that you don’t include the camera into the serialized scene. When loading the serialized scene, you place the loaded node back under render rather than replace it.

Alternatively, if you need the default camera to be part of the serialized scene, you would need to tell Panda to switch to a different camera after deserialization. This is done by iterating over the display regions on base.win, finding the one that has the default camera (base.cam) assigned to it, and replacing it with the one on your loaded scene (which you can obtain using scene.find('**/+Camera')).

Thanks for the quick reply!

So, this almost gets me there (I do need serialize/deserialize the camera, or at least the camera’s transform). However, the camera controls no longer work, and the directional light that i attached to my camera node is no longer pointing in the same direction as the camera. At least now I can see my geometry and the camera is transformed correctly.

Here is the code that I added, per your suggestion:

camera = self.render.find('**/cam')
for region in self.win.getDisplayRegions():
    if region.camera == self.cam:
        region.setCamera(camera)
        break

I also tried adding a call to self.enableMouse() but that didn’t seem to affect anything.

Btw, when i use the suggested search print(self.render.find('**/+Camera')) then I get back the directional light for some reason:

render/camera/directional

Separate issue: It appears that the python tags I set on my ‘landmark’ instance nodes are either not serialized or I am not deserializing them properly. I would assume that tags would get serialized, but maybe this isn’t the case? Any tips on how to serialize/deserialize custom nodes and/or tag data?

I would not recommend relying on this Panda3D feature. And to implement your own scene format based on a text file, this, of course, increases the loading time. But I think it will take less time than fighting the Panda3D code. Perhaps conservation in the sense that we assume today was not fully laid down or completed when writing this feature.

Yes, there’s more that needs to be changed to point to the new camera. You can just take a look in the ShowBase.py source code to see all the places where the camera is used. You would need at least this to fully replace ShowBase’s camera:

self.cam = self.render.find("**/cam")
self.camNode = self.cam.node()
self.camera = self.cam.parent
self.camLens = self.camNode.getLens()
self.mouse2cam.node().setNode(self.camera.node())

You could alternatively decide to read out only the transform of the camera that is read from the .bam file and apply that to the already existing camera.

Python tags are not serialized; only regular tags are. If you need to have these serialized, you can use something like pickle and write those as a regular tag string.

+Camera searches for anything with the type Camera, which includes DirectionalLight (since a light is a subclass of Camera, for shadow mapping purposes). You may need to adjust your find() to mention the camera by name, or some other mechanism.

I don’t know why @serega-kkz is suggesting that bam serialization cannot be relied upon. I would dismiss the suggestion that the system is unreliable, given that it is heavily used and tested, and has extensive coverage of Panda3D’s memory structures.

Yes, this is what I ended up doing, along with serializing/deserializing only a sub-graph of my scene.

Thanks again for your help.

Note that you suggested the Python - pickle module for serializing tags.

  1. I think it will also handle the rest of the task of serializing the scene. There is no need to explicitly use two serialization systems.

  2. Obviously, there are problems with duplicating nodes when loading, such as the render and camera, this requires additional processing.

It would be ideal to run Panda completely without default nodes for such cases. I mean the lack of a camera and render and so on.

You would still need to solve all those problems with a manually created serialization format, in addition to having to do a lot of extra work writing the serialization and deserialization methods for Panda3D’s scene structures.