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
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
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
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 i'll shut up
Is this still the way to go for run time texture altering