Scenegraph to/from XML

The following is output from the AmbientLightingTest example. Object inlining is turned off (looks for shared objects in the graph and produces references to them).

The BaseObjectReference element contains all of the potentially shared objects. Each of them has an objrefid attribute that uniquely identifies the object. Shared objects can reference any other shared object that exists above it in the file. To use a shared object the ObjectReference element is used.


<?xml version="1.0" encoding="UTF-8"?>
<VirtualUniverse>
  <TextureLoader searchpath=".\;..\;"/>
  <BaseObjectReference>
    <PolygonAttributes cullface="0" polygonmode="2" polygonoffset="0.0"
      polygonoffsetfactor="0.0" duplicateonclonetree="false"
      capbits="{}" islive="false" name="" objrefid="0"/>
    <Appearance duplicateonclonetree="false" capbits="{}" islive="false"
      name="" objrefid="1">
      <ObjectReference id="0"/>
    </Appearance>
    <QuadArray duplicateonclonetree="false" capbits="{}" islive="false"
      name="" objrefid="2">
      <CoordinateData setindex="0" setcount="10" totalcount="12"
        commoninitialindex="0" data="-0.099999994,-0.70000005,0.0,-0.099999994,-0.099999994,0.0,-0.70000005,-0.099999994,0.0,-0.70000005"/>
      <CoordinateData setindex="1" setcount="2" totalcount="12"
        commoninitialindex="0" data="-0.70000005,0.0"/>
    </QuadArray>
    <Shape3D passid="0" ignorebounds="false" iscollidable="true"
      boundsautocompute="true" showbounds="false" isoccluder="false"
      pickable="false" renderable="true" capbits="{}" islive="true"
      name="" objrefid="3">
      <ObjectReference id="1"/>
      <ObjectReference id="2"/>
      <BoundingSphere radius="0.42426413" center="-0.40000007,-0.40000007,0.0"/>
    </Shape3D>
    <ColoringAttributes color="1.0,0.0,0.0" shademodel="3"
      duplicateonclonetree="false" capbits="{}" islive="false" name="" objrefid="4"/>
    <PolygonAttributes cullface="0" polygonmode="2" polygonoffset="0.0"
      polygonoffsetfactor="0.0" duplicateonclonetree="false"
      capbits="{}" islive="false" name="" objrefid="5"/>
    <Appearance duplicateonclonetree="false" capbits="{}" islive="false"
      name="" objrefid="6">
      <ObjectReference id="4"/>
      <ObjectReference id="5"/>
    </Appearance>
    <QuadArray duplicateonclonetree="false" capbits="{}" islive="false"
      name="" objrefid="7">
      <CoordinateData setindex="0" setcount="10" totalcount="12"
        commoninitialindex="0" data="0.70000005,-0.70000005,0.0,0.70000005,-0.099999994,0.0,0.099999994,-0.099999994,0.0,0.099999994"/>
      <CoordinateData setindex="1" setcount="2" totalcount="12"
        commoninitialindex="0" data="-0.70000005,0.0"/>
    </QuadArray>

[edit] removed the rest of the xml post to clean up the thread

One note for the future: In order to tell if an object is shared in the graph it is necessary to have acces to the objects handle so methods like:

void getLeftProjection(Transform3D transform3D)

in the View class prohibit the ability to determine if the Transform3D object is shared because the internal object is being coped to the passed object.

[quote]One note for the future: In order to tell if an object is shared in the graph it is necessary to have acces to the objects handle so methods like:

void getLeftProjection(Transform3D transform3D)

in the View class prohibit the ability to determine if the Transform3D object is shared because the internal object is being coped to the passed object.
[/quote]
There are two schools of accessing objects - direct (return pointer to internal object and on set, just change pointer) and java3d-like (perform copy on set and allow only by-value copy getter). In first case, you can check for sharing, in second case, there is no such thing as sharing in fact, because even if you pass same pointer to many objects, data will be copied to internal strucure anyway.

Mess starts when direct setter is used in conjuction with java3d-like getter - and this is only case where IMHO code should be refactored. getLeftProjection is not implemented currently :), so it is not a problem - but it is enough to implement setLeftProjection to copy parameter to avoid problem with sharing.

Help, snif.

The reading/writing from/to xml is working nicely now. I can manually create a graph write it to disc read it in and write it back to disk in another file and both files are identical and all objects are getting displayed.

However I have a problem, textures aren’t working. When I load a graph, for instance the CubeTest, everything appears to be getting created and assigned but the texture isn’t displayed.

To create a texture I do the following:

Create a new Texture2D object using the Texture2D() constructor.

For each ImageComponent sub-element in the XML create a new object using ImageComponent2D(int format, int width, int height, BufferedImage image).

The image I pass in is created by TextureLoader.getScaledImage(BufferedImage origImage, int width, int height, int method) whwre method is set to SCALE_DRAW_BEST.

For I add each of the ImageComponents to the Texture2D object using the setImage(int level, ImageComponent image) method to place the correct scaled image at the correct level.

Finally set all of the attributes of the Texture2D object using the setter methods.

Anyone have any idea where I should look to find the problem?

Obviously I’m doing doing something wrong. Is there a huge difference between doing it this way and using the TextureLoader.loadTexture methods? I need to create the ImageComponent object separately because you can manually set each level, I can’t assume they all use the same image.

[quote]Is there a huge difference between doing it this way and using the TextureLoader.loadTexture methods?
[/quote]
Would be better to look at the source code, so if you put together simple test case, we can check.

I personally use both TextureLoader and own texture creation mechanisms, and both work quite well for me.

Only thing you should take care that you have to use DirectBufferedImage to pass image data to ImageComponent:

// pw and ph are image width and height
BufferedImage src = ...;
BufferedImage dst = DirectBufferedImage.getDirectImageRGB(pw, ph);
Graphics2D g2 = (Graphics2D) dst.getGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2.drawImage(src, 0, 0, pw, ph, null);
g2.dispose();
return dst;

Yuri

Thanks for the info, as it turned out I was using the wrong method to set the texture coordinates on the GeometryContainer objects.

For anyone that is interested the current code is in the following zip file.

http://www.scottshaver2000.com/files/sgxith.zip

The file contains the src and demo directories (no image/sound files). I had to make a number of minor modifications to the Xith code to make this work. All changes are marked with a comment that looks like this:

// TODO SAS

Most of the classes in com.xith3d.test have been modified to write the scene graph out to disk in the current directory. The filename will be that of the test class minus the “Xith3D” prefix. So if you run Xith3DAmbientLightTest.bat from the demo directory you will end up with a file named AmbientLightTest.xml in the demo directory.

For some of the classes I have added a sister class that will read in the xml file, creating a scene graph, write the scene graph out to a second file and then show the loaded graph.

So for Xith3DAmbientLightTest there is a sister class Xith3DAmbientLightReadTest which will produce AmbientLightTest2.xml. I have added batch files in the demo directory for these new classes as well.

There are still some issues I need to deal with but things seem to be working for the most part. I would be insterested to hear from someone with a scene graph that is more complicated than the demo classes.

Have fun.

BTW the code in the zip file is based on the code from the Xith3D_2003-12-23_cvs.tar.gz file.

EDIT Noe that this code is dependant on the Xerces project classes. The old version of Xerces will work and the batch files included in the zip look for it in the third-party directory as xerces.jar.

The new Xerces2 jars can be used if you change the runtest.bat file to include …/third-party/xmlParserAPIs.jar;…/third-party/xml-apis.jar;…/third-party/xercesImpl.jar in the classpath.

These can be downloaded at http://www.apache.org/dist/xml/xerces-j/binaries/Xerces-J-bin.2.6.0.zip.

Warning that download is pretty big but the jar total only about 1 meg.

Any plans to put this to xith-tk?

Yuri

I figured you folks would decide where you want it to go (if you want it at all) after you looked at the code. I don’t have a place I can use CVS from currently as I don’t have a personal inet connection anymore.

even if you don’t put it in the xith-tk you can still list it in the “file sharing area”.

Of course I think it’d be nice to see all code in CVS but like yourself, not everyone has convenient CVS access.

The reason it’s nice to have it there is because it’s a central location where people know where to look.

Will.

Well there isn’t much point in placing it in he file sharing section until someone can make the minor “fixes” I had to make to the code in the Xith project.

can you post a patch to IssueZilla?

Will.

okay the patch/issue has been entered

Although I can’t seem to get it to show up using the query page. :frowning:

Don’t know what went wrong, but you didn’t commit an issue.

Okay I must have screwed up yesterday. It is issue #57.

I added some comments to proposed patch in IssueZilla. Please comment especially on point 2, point 1 is not so important, but you should know that compatibility of serialized versions for point 1 (billboards) will be very probably broken in future. The rest is OK.

Yuri

Well I don’t appear to have the ability to modify my own issues so I’ll post here.

As for number one I can just leave the Billboard objects out of the graph I suppose. I have already assumed that the classes will change making them incompatible with the XML reader/writer at times. So if we can go ahead and add the methods I need for now I can deal with the changes when they occur.

As for number two I’d be happy to do it some other way if you can tell me how else I can make sure I get a filename based on an ImageComponent.

I realize that there are other methods of generating the texture data. Right now if I don’t find a file name in the IC I use the methods

byte[] getByteData() <- 2D
byte[] getImage(int index) <- 3D

to get the image data and write it into the XML file. However I belive this to be unreasonable in situations where the data was not generated but was loaded from an image file. I only need the file name not the path.

I also understand that people may not use the TextureLoader to load the images and they may create the ImageComponent object by hand. If this is the case and they want the XML to not contain huge blocks of image data they simply have to follow one rule: set the filename in the ImageCompoent objects by calling setFileName().

As for “adding storage-specific functionaloty/data to rendering classes”, well it’s meta-data, it doesn’t effect the render process in any way, it needs to be there unless another method of finding the file name for an IC object can be found.

Another possibility would be to tackle the problem in a more general way. I know we have the setUserData method in the SceneGraphObject class. However there is no way to effectively output this as XML since it could be anything.

What about adding another set of methods to SceneGraphObject:

public void putMetaData(String key, String value)
public String getMetaData(String key)
public Iterator getAllMetaDataKeys()

Keys that are prefixed with “XITH3D” are keys added by the system. In the case of the file name a key XITH3DImageFileName, would be added to ImageComponents via the TextureLoader class automatically.

Keys without the above prefix are assumed to have specific meaning to a programmers application and the system never deals with them.

Again this has no impact on the renderer and allows me to store information in the XML output that may or may not have meaning to the Xith3D system as a whole.

Any thoughts?

[quote]I have already assumed that the classes will change making them incompatible with the XML reader/writer at times. So if we can go ahead and add the methods I need for now I can deal with the changes when they occur.
[/quote]
This is what I thought to do. I just wanted to make a clear statement about possible future incompatibility.

[quote]As for “adding storage-specific functionaloty/data to rendering classes”, well it’s meta-data, it doesn’t effect the render process in any way, it needs to be there unless another method of finding the file name for an IC object can be found.
[/quote]
For Metadata, Java3D assumed to use userData. In this specific case the major concern is that if we will add all the possible metadata fields as fields in core classes, they will become enormously big very very soon, and every time it will be “one more very small addition”.

Instead of this I will suggest to introduce either alternate mechanism for associating metadata with the scenegraph, or adding an option to TextureLoader to allow identifying textures loaded by TextureLoader and retrieving their name. We anyway need more sophisticated mechanism for annotating scenegraph with application-specific data to add, for example, physical properties, which may differ from one physics engine to another.

We can extend userData support by adding named user data, that will be stored in internal hashMap, so any other application will be able to associate its own data with given node.

It is generally not possible to store metadata as a part of serialized scenegraph until they have their own well-defined persistence support, so we will not break XML serialization anyway.

The bottom line: I just don’t think that placing workaround for specific problem is a good idea, because of more generic solution seems for me easy to implement.

It would be good to have an option to say to texture loader to place its annotations in userData (or metaData) or not, and I see it disabled by default.

Yuri

Okay so you think that extending the user data mechanism is the way to go here then? I wonder if it may make sense to add the meta data methods above AND extend the user data methods to allow for named objects.

The meta data methods I have suggested would be used ONLY for system generated information then. The user data remains for program specific related data only. The separation of the two types of data makes sense to me (although maybe not to anyone else :slight_smile: ).

This allows serialization code to have a general rule: USER DATA IS NOT SERAILIZED (or requires special handling), SYSTEM METADATA IS SERIALIZED.

Also I’m thinking there may be a way for me to allow the user to help me understand how to serialize/deserialize the user data by passing in an object to the reader/writer that implements an interface such as

public String userDataToString(Object userData);
public Object userDataToObject(String userData);

Then when writing if I come across a non-null userData object I can simply pass it to the implmenting class and let it give me back a string to place in the XML. Reverse for the read process.