ImageFormat or ByteBuffer ARGB to RGBA magic?

I have a Control which creates a ByteBuffer, from that an Image and from there a Texture2D. In the setSpatial method, I add a MatParamOverride to the spatial to use this texture, and now I have a Control which can dynamicly update the Texture by writing to the ByteBuffer. This works like a charm.

But here comes the trouble: I want to save and load these spatials, together with the Control and the generated texture. Since I need access to the ByteBuffer again after loading, I thought to save the ByteBuffer in the write method. Then in the read method, just read the buffer and recreate the Image, Texture and MatParamOverride accordingly. This seems to work, until I write anything to the ByteBuffer, it now seems to have changed format from ARGB to RGBA or something…

I created a minimal demonstration here: GitHub - smeaggie/ImageTest

In this test case, on first run it will create a savegame with a generated brownish texture. Pressing “T” will call update() on the control, which will put the same brown color on the ByteBuffer, so the cube should NOT change color. Now quit, comment out the first part (lines 37-47) and uncomment the LoadGame calls (lines 49-50). Now the cube has the correct color to begin with, but pressing “T” will change the color. It gets a lot of blue, which hints to me the A and B channels are swapped at least?

A quick solution seems to be to read the ByteBuffer into a temporary buffer and recreate the ByteBuffer using ByteBuffer.allocateDirect() like this:

    @Override()
    public void read(JmeImporter im) throws IOException {
        super.read(im);

        ByteBuffer temp = im.getCapsule(this).readByteBuffer("buffer", null);

        buffer = ByteBuffer.allocateDirect(128 * 128 * 4);
        buffer.position(0);
        temp.position(0);
        buffer.put(temp);

...etc...

So what happens here? Am I abusing the save/load system? The Image/Texture system? Is there a problem with saving/loading ByteBuffers? I’m happy with the workaround but I can’t seem to figure out what went wrong and I really just like to know if this is some error on my part…

For completeness, this is my system info from the SDK:

Product Version: jMonkeyEngine SDK v3.5.2-stable
Java: 11.0.17; OpenJDK 64-Bit Server VM 11.0.17+8
Runtime: OpenJDK Runtime Environment 11.0.17+8
System: Linux version 5.15.0-67-generic running on amd64; UTF-8; en_US (jmonkeyplatform)

[EDIT]:
this might be interesting, found the BufferUtils.createByteBuffer() so I updated the ScreenControl to create the initial Buffer like this:

buffer = BufferUtils.createByteBuffer(128 * 128 * 4);

and now the cube is blueish from the start and never actually brown. Could the answer lay in the difference in endianness? ByteBuffer.allocateDirect() gives BIG_ENDIAN, the BufferUtils.createByteBuffer() creates a LITTLE_ENDIAN buffer. Changing endianness seems a good way to swap color channels somewhere…

Yes, it’s about endianness. They must match or things will be mixed around.

Whether you are abusing the save/load system or not depends entirely on what you are actually doing.

JME’s built in serialization is nearly the worst way possible to save/load game data. It’s only benefit is that JME already implements it… so if you were writing a scene editor or something then there might be a reason to use it.

But if your spatials are “game objects” and you’re using JME’s system to save/load “game state”… you will ultimately have a few different kinds of “bad times”.

1 Like

Yeah I figured I have to set the individual bytes and not try to set all color channels at once using an int. Try that tomorrow, it’s 1:45 at night and I got to sleep. Now I’m curious about the serialization though, why would it be the worst? In my case, almost everything has a custom Control with data, and almost all data must be saved somewhere, so I liked the idea of just serializing the rootNode. Been working on implementing these read/write methods for a few hours and refactoring some code to make it work, but for now I’m quite happy with it. Maybe it’s an edge case, it’s not really a game but more a simulator so there’s more data and computation than actual geometry. I store a lot of data in those Controls sometimes. I’m an experienced java developer but JME newbie so I have to find my way around the APIs and code a bit. Is there another recommended way to save/load game data?

Are there examples of pitfalls I might run into? Like to consider before implementing everything… Thanks for the reply!

First, treating Spatials like game objects is like storing Swing controls in the database. MVC ideas can apply here, too, just like any other application. Presentation → data separation. In that case, “game objects” would be their own thing and just project into Spatials when they need to be visualized.

JME’s serialization has a few down sides. It always requires implementing a special interface for any object that needs to be stored. It may break with any new JME version. It is also storing a TON of stuff that may or may not be relevant to the save state of your game. For example, most games probably don’t want to store every vertex of every piece of geometry… thus making it impossible to ever update the models in a new game version. Even something as simple as deciding you want some piece of geometry under a different node structure means all prior save-games are invalid.

Even Java’s own serialization is better than JMEs… I was never able to convince the team to abandon it, though. It used to be “because we can also save it as XML and look at it…” but I understand that has been broken for some time and at any rate requires maintenance to keep it compatible with the binary form. I remain unconvinced.

Yes that’s an interesting take and you’re probably right. I’ve not thought about it that way, was too busy prototyping other stuff I guess. Good to get this point now, thanks! I’ll re-think my presentation → data separation strategy :slight_smile:

JME allows this sort of “spatial is the game object” prototyping… a lot of the tutorials even encourage it to some degree. For small games it can be ok but eventually limitations are reached and then it’s back to square one.

…so, yeah, it can be a good idea to at least be thinking about it while you continue prototyping.

1 Like