Refreshing a texture after in-game modification

Hello J Monkeys!

I use the heightmap of my terrain to calculate what the alphamap looks like, based on slopes and height and stuff.

I was supprised when I saw the image in the folder change when I generate the new alphamap but it would only get applied if I restart my game.

I am guessing that in this function:

matTerrain.setTexture("AlphaMap", Jme.assetManager().loadTexture(path));  

The loadTexture(path) function seems to store the image data somewhere for re-use, correct? How do I tell the asset manager that the image has changed? What is mip-mapping (anything to do with this) ?

I also need this to work for in-game terrain painting.

How does assetManager.removeFromCache() work? How do I get the key of my image?

Regards,

I think that is for when you make one of these

TextureKey key = new TextureKey(textureName, true);

So should solve your problem when used with remove (or is it deleteFromCache?). I think there is also a method to just completely refresh or wipe the assetmanager or something, not sure.

1 Like

Nope, somehow the path is not the texture key? The removeFromCache returns a false boolean each time.

Resetting the whole assetManager seems like a bad idea. How can I find the right key?

public void setAlphaMap(String path){
    boolean keyFound = Jme.assetManager().deleteFromCache(new TextureKey(path, true));
    if(!keyFound) System.out.println("setAlphaMap() - Key ["+path+"] not found in cache!");
    try{
        matTerrain.setTexture("AlphaMap", Jme.assetManager().loadTexture(path));  
        tp.alphamap = path;
    }catch(Exception e){
        tp.alphamap = generateAlphaMap(); //resets the map            
    }                
}

We are on the right track tho, if I clear the whole cache it works… Just need that key :smile:

Edit:

I got it!

2 solutions:

TextureKey tk = new TextureKey(path, true);
tk.setGenerateMips(true); //This was missing

or better:

Jme.assetManager().deleteFromCache(matTerrain.getTextureParam("AlphaMap").getTextureValue().getKey());

Thanks!

Note: if you just want to change a texture at runtime in code then you don’t need to write it to a file to do it. You can, but you don’t need to.

Else make sure you use the same key to load the texture as you did to clear it from the cache. loadTexture(string) builds a texture key internally that may not match the one you created manually. Better to create them the same.

1 Like

Will I still be able to save/modify in asset path (Texture… etc…) once the game is running as compiled exe ?

If you load via a FileLocator, you can modify the file via the normal java api.
If you load via classpath locator, this behaviour is not supported in any way, and will not work with a distributed game anyway.

1 Like

So… Asset manager would be classpath? What do I use for File locator??

Look here 3rd post

1 Like

I’m still curious what you are actually doing in game terms… because if you think the only way to update a texture at runtime is to write a file then know that there are better ways… and that’s almost the slowest possible way imaginable.

Usually, even if you needed to save the texture for later (extremely rare since generally textures are generated from game data) then you’d update the texture and THEN (occasionally) write it back to the disk.

So, repeating, if your game process is currently like:
-game modifies image
-game saves image
-game loads texture
…so user can see texture.

Then know that the only slower way to do that would be to upload it to FTP in the process. :wink:

1 Like

Lol, yes I get you. The saving to disk part is for terrain alphamaps that are generated based on procedurally generated terrain. I save it to disk because I am inside the “Editing” zone in my game, not indented for players.

Now, ingame there will be terrain modification and terrain painting but for that I will save to disk only when player leaves a planet. For multiplayer, I will send alphamap / terrain changes by TCP as they happen but only if a player is on a planet where another user is making modifications.

Does this sound about right?

Yeah, but if it’s the same code generating the alpha map that’s loading the texture then it should be:
generate image → update texture → save image…
Not:
generate image → write to disk → clear cache → reload texture

In the first case, you never have to worry about the cache and you can also decide to save later.

1 Like

Ok, that does seem muchy better. I am having trouble converting Jme3 Image to standard atx image. How do I go from one to the other?

How to I turn Image to texture without using assetManager (asset manager does not work for ram right?)

I can’t find the jme3tools…

Edit: Win for javadoc. What I want is Texture2D right?

Texture2D takes an Image argument

1 Like

Hi Momoko_Fan, thanks!

I made a test painter now but it’s a bit slow for big values. Any ideas on making it faster?

public void testPaintAlphaMap(){        
    int radius = 100;                
    if(matTerrain != null && matTerrain.getTextureParam("AlphaMap") != null){
        
       Image img = matTerrain.getTextureParam("AlphaMap").getTextureValue().getImage(); 
       ImageRaster raster = ImageRaster.create(img);          
       for(int x = -radius+img.getWidth()/2; x < radius+img.getWidth()/2; x++){
            for(int z = -radius+img.getWidth()/2; z < radius+img.getWidth()/2; z++){                         
                raster.setPixel(x, z, ColorRGBA.Red);                    
            }
        }
        matTerrain.getTextureParam("AlphaMap").setTextureValue(new Texture2D(img));
    }      
                   
}

You don’t really need the last line, since ImageRaster is modifying the image in-place. Its already updated at that point.

Also if the textures you’re using are huge, this will cause the entire texture to be re-uploaded each time, i.e. it cannot detect that you only updated a small portion of it.

1 Like

Yeah, nice that did it!

One more thing, I get following buffer error if I don’t wait long enough before saving to disk. How can I make it so that the buffer error does not happen ?

Jan 17, 2016 6:40:44 PM com.jme3.app.Application handleError
SEVERE: Uncaught exception thrown in Thread[jME3 Main,5,main]
java.nio.BufferUnderflowException
at java.nio.DirectIntBufferU.get(DirectIntBufferU.java:271)
at java.nio.IntBuffer.get(IntBuffer.java:715)
at com.jme3.util.Screenshots.convertScreenShot2(Screenshots.java:50)
at com.jme3.system.JmeDesktopSystem.writeImageFile(JmeDesktopSystem.java:93)
at com.jme3.system.JmeSystem.writeImageFile(JmeSystem.java:137)
at game.env.Terrain.saveAlphaMap(Terrain.java:282)
at game.lemur.TerrainEditorUI$4.execute(TerrainEditorUI.java:97)
at game.lemur.TerrainEditorUI$4.execute(TerrainEditorUI.java:94)
at com.simsilica.lemur.core.CommandMap.runCommands(CommandMap.java:61)
at com.simsilica.lemur.Button$ButtonMouseHandler.click(Button.java:225)
at com.simsilica.lemur.Button$ButtonMouseHandler.mouseButtonEvent(Button.java:246)
at com.simsilica.lemur.event.MouseEventControl.mouseButtonEvent(MouseEventControl.java:122)
at com.simsilica.lemur.event.PickEventSession.buttonEvent(PickEventSession.java:447)
at com.simsilica.lemur.event.MouseAppState.dispatch(MouseAppState.java:175)
at com.simsilica.lemur.event.MouseAppState$MouseObserver.onMouseButtonEvent(MouseAppState.java:189)
at com.jme3.input.InputManager.processQueue(InputManager.java:832)
at com.jme3.input.InputManager.update(InputManager.java:908)
at com.jme3.app.Application.update(Application.java:690)
at com.jme3.app.SimpleApplication.update(SimpleApplication.java:234)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:152)
at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:192)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:233)
at java.lang.Thread.run(Thread.java:745)

This is the saving code:

public void saveAlphaMap(){
    if(matTerrain == null || matTerrain.getTextureParam("AlphaMap") == null){
        return;
    }
    String dest = System.getProperty("user.dir") + SaveLoad.ALPHAMAPS_FOLDER; 
    ByteBuffer bb = matTerrain.getTextureParam("AlphaMap").getTextureValue().getImage().getData(0);        
    try {      
        File outputfile = new File(dest+tp.name+"_am.png");
        JmeSystem.writeImageFile(new FileOutputStream(outputfile), "png", bb, tp.size+1, tp.size+1); 
        System.out.println("Filename: "+outputfile.getPath());
    
    } catch (IOException e) {
        e.printStackTrace();
    }         
}
1 Like

You need to make sure the buffer is in a state for reading… and you probably want to get in the practice of grabbing a read-only view of it:
http://docs.oracle.com/javase/7/docs/api/java/nio/ByteBuffer.html#asReadOnlyBuffer()

But at least you will want to rewind the buffer before saving else it may already be at the end.

2 Likes