Terrain Painting

Hi to all.



I have been struggling with this for a long time now and need some help?

I am trying to do runtime terrain painting on a splat texture at the same time

update the texture image without having a noticable drop in performance.



Can anyone please direct me the right way?



I basically want to paint on the transparent splat texture!



Thanks

A splatting shader comes to me in mind.

We have done theme tests with this.



Per alpha map you can have 4 different textures. If you want more you need additional alphamaps.

For each texture and alpha you need a textureunit.

The implementation depends on the graphichardware.

If you have old hardware you may need to use multi render passes.

If you have need hardware you can implement a shader with sufficient textureunits.



A shader has the advantage of saving memory.



Is this somethink you think of?

i think what he is asking is how to update the alpha texture used for splatting at run-time(and in realtime)



two ways could be:

  • use the same technique as jmedesktop and get the data buffer from the image backing the texture and rebuild it after update through glubuild2dmipmaps for example
  • the render to texture technique



    on madlion's comments:
  • with shader support you can combine 4 alpha texture into one, putting the textures alpha into each channel of the new texture(rgba), and thus render 4 splats per pass
  • without shader support you have to draw one pass per splat layer



    here is a sample of how i did something similar (probably too slow for your "no noticable drop" criteria)



    sample usage


      DrawableTexture drawableTexture = new DrawableTexture(64);
      drawableTexture.fillTexture(ColorRGBA.red);
      drawableTexture.setPixelColor(32, 32, ColorRGBA.blue);
      drawableTexture.updateTexture();

      Box box = new Box("box", new Vector3f(-100, -100, -100), new Vector3f(100, 100, 100));
      TextureState ts = display.getRenderer().createTextureState();
      ts.setEnabled(true);
      ts.setTexture(drawableTexture.getTexture(), 0);
      box.setRenderState(ts);
      box.updateRenderState();
      rootNode.attachChild(box);

      //and in your update loop, just fiddle with setPixelColor and do updateTexture,
      //or build nicer fill methods that can take a texture as brush input to fill pixels with...



DrawableTexture.java


import com.jme.image.Texture;
import com.jme.renderer.ColorRGBA;
import com.jme.system.JmeException;
import com.jme.util.LoggingSystem;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.Util;
import org.lwjgl.opengl.glu.GLU;

import java.awt.image.BufferedImage;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.logging.Level;

/**
 * MrCoder
 */
public class DrawableTexture {
   private Texture texture;
   private byte[] data;
   private com.jme.image.Image image;
   private BufferedImage awtImage;
   private final int imageSize;

   public DrawableTexture(int imageSize) {
      this.imageSize = imageSize;
      initialize();
   }

   private void initialize() {
      texture = new Texture();

      awtImage = new BufferedImage(imageSize, imageSize, BufferedImage.TYPE_4BYTE_ABGR);
      ByteBuffer scratch = ByteBuffer.allocateDirect(
            4 * imageSize * imageSize).order(
            ByteOrder.nativeOrder());
      data = (byte[]) awtImage.getRaster().getDataElements(0, 0,
            awtImage.getWidth(), awtImage.getHeight(), null);
      scratch.clear();
      scratch.put(data);
      scratch.flip();
      image = new com.jme.image.Image();
      image.setType(com.jme.image.Image.RGBA8888);
      image.setWidth(imageSize);
      image.setHeight(imageSize);
      image.setData(scratch);

      texture.setImage(image);
      texture.setFilter(Texture.FM_LINEAR);
      texture.setMipmapState(Texture.MM_LINEAR_LINEAR);
      texture.setWrap(Texture.WM_ECLAMP_S_ECLAMP_T);
   }

   public void updateTexture() {
      GL11.glBindTexture(GL11.GL_TEXTURE_2D, texture.getTextureId());
      GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);

      awtImage.getRaster().getDataElements(0, 0, awtImage.getWidth(), awtImage.getHeight(), data);
      ByteBuffer scratch = image.getData();
      scratch.clear();
      scratch.put(data);
      scratch.flip();

      ByteBuffer data = image.getData();
      data.rewind();

      GLU.gluBuild2DMipmaps(GL11.GL_TEXTURE_2D, GL11.GL_RGBA8, image.getWidth(),
            image.getHeight(),
            GL11.GL_RGBA,
            GL11.GL_UNSIGNED_BYTE, data);

      Util.checkGLError();
   }

   public void setPixelColor(int x, int y, ColorRGBA color) {
      if(x < 0 || x >= awtImage.getWidth() || y < 0 || y >= awtImage.getHeight()) {
         LoggingSystem.getLogger().log(Level.SEVERE, "Pixel position is out of bounds!");
         throw new JmeException("Pixel position is out of bounds!");
      }

      int a = (int) (color.a * 255.0f);
      int r = (int) (color.r * 255.0f);
      int g = (int) (color.g * 255.0f);
      int b = (int) (color.b * 255.0f);
      awtImage.setRGB(x, y, (a << 24) | (r << 16) | (g << 8) | (b));
   }

   public void fillTexture(ColorRGBA fillColor) {
      for(int i = 0; i < awtImage.getHeight(); i++) {
         for(int j = 0; j < awtImage.getWidth(); j++) {
            int a = (int) (fillColor.a * 255.0f);
            int r = (int) (fillColor.r * 255.0f);
            int g = (int) (fillColor.g * 255.0f);
            int b = (int) (fillColor.b * 255.0f);
            awtImage.setRGB(i, j, (a << 24) | (r << 16) | (g << 8) | (b));
         }
      }
   }

   public Texture getTexture() {
      return texture;
   }

   public BufferedImage getAwtImage() {
      return awtImage;
   }

   public int getImageSize() {
      return imageSize;
   }
}


…but it works fast enough for me to do real-time lightmap generation on a terrain with a moving "sun"

Thank you for the great advice, but my problem is a lot

more simple than that!



I just want to change a Texture's buffered image on every loop and

then update what you see on screen.



I don't want to recreate the texture every loop!



Any ideas?

So the only think that changes is the terrain texture, right?



If you are using a shader the texture will recreated/updated on every loop. The shader is executed every rendering loop. Everytime a vertex, fragment/pixel is redrawn (i'm right?).

So if you are use somethink like MrCoder posted to update a texture imagebuffer the shader will do the rest in realtime.



Is this what you want?



I'm no expert but i think this is the way to do want you want. If i understand what you want.


tGiantLabs said:

Thank you for the great advice, but my problem is a lot
more simple than that!

I just want to change a Texture's buffered image on every loop and
then update what you see on screen.

I don't want to recreate the texture every loop!

Any ideas?



well, that's exactly what the code i posted does...

Not exactly what I want. Lets me try the question again.



How can I update an existing texture which image have changed? My current code is



as follows but doesn't seem to work:



           Texture texture = texturemanager.getTexture();
            texture.setCorrection(Texture.CM_PERSPECTIVE);
            texture.setFilter(Texture.FM_LINEAR);
            texture.setImage(TextureManager.loadImage(textureGenerator.getImageIcon().getImage(), true));
            texture.setMipmapState(Texture.MM_LINEAR_LINEAR);
            texturemanager.setTexture(texture);
            getTextureState().setTexture(texturemanager.getTexture(), 0);
            terrain.updateRenderState();



Suppose to work, right?

i don't understand what you mean either :slight_smile:



are there any other way of getting the texture into opengl than glTexImage2D/gluBuild2DMipmaps etc?



my code doesnt rebuild the texture, it just updates the opengl-bound texture with the new image data…



maybe i'm just too tired today :slight_smile:

Well, you can always replace the entire texture… which seems to be what tGiantLabs wants to do.



However, it looks like you're using the same Texture object instead of making a new one. That's ok actually, as long as you reset the texture id first (to 0).



Of course, the getImageIcon -> getImage -> BufferedImage -> ByteBuffer process isn't exactly fast. Maybe you could take a look if (LWJGL)ImageGraphics can be used in your project, that does all the fancy stuff mrCoder talks about for you…

yeah, that's sort of where i got my code from :wink:

and i hope that it's faster than replacing the entire texture…



snip from lwjglimagegraphics almost exactly like my updateTexture method…


Yes, but I mean he could just use ImageGraphics. It works just like any Graphics2D object (which I assume he's using now), and it tracks dirty regions (so it doesn't have to update all of the texture if you change only a bit). Plus it has a very clever mipmap scheme…

ok ok ok :slight_smile: i'll shut up :wink:

Is this still the way to go for run time texture altering