Deformable/destructible terrains used as texture

Started by lorcán, November 13, 2008, 20:40:05

Previous topic - Next topic

lorcán

Hi all

I'm making a kind of lemmings game remake using LWJGL. For those who don't know the original game, you can ckeck a description there : http://en.wikipedia.org/wiki/Lemmings_(computer_game)

I'm facing a big problem in rendering a level, since it's using derformable/destructive terrains.

A level "scenery" is made of several layers, in the following order :

1 - animated objects (sprites) in the background : moving water, bubbling lava, marshes... These elements are not destructible (whew!  :P)
2 - a big image (BufferedImage) which represents the overall scene (terrain). This main image is drawn using many .PNG's and rendered once (in level initialization) as a single BufferedImage. This large image actually show the destructible/deformable terrain.
3 - others animated sprites in the "foreground", not destructible

On top of this scene, there are the lemmings creatures walking, climbing, ... (individual sprites)

A lemming can dig tunnels in the soil. in carvern walls and so on... You can guess it : soil, walls, etc, are parts of the large BufferedImage (the second layer). This BufferedImage is converted into a texture bound to a quad. Digging in the terrain means that it affects this image making corresponding pixels transparent (we should see the back sprite objects behind = layer #1). As the texture used for the quad is cached, it won't take note of the image changes.  :(

In addition to digging in the ground, a Lemming can also build stairs... Stairs are also destructibe (another lemming can dig in it like in the terrain). This means I should draw stairs in the BufferedImage (replacing pixels). Same problem here.  :(

What do you think about all this?

Are there methods to manipulate specific pixel in a texture currently being used? Is there any way to alter the buffer instead of clearing the cache and reloading the whole picture into it?

I thought about dividing this big pictures into smaller tiles (several quads instead of just on) and reload only concerned tiles when needed.

Any ideas are welcome  :)

Thanks in advance  ;)

bobjob

Have you thought about using FBO's?

You can just continually minipulate the Image, and then store the changes. Depending on how a projectile will erase the scene you may want to create textures to handle this, draw them to the depth buffer, and redraw the scene(Main Texture) with depth testing on. As for stairs, They would work the other way, draw the scene first then the stairs.

As for collision detection. Thats probably gonna be a head ache. You may need to store a 2 dimesion boolean array to store weather or not a pixel exists.

bobjob


lorcán

Thanks for your fast answer, bobjob.

QuoteHave you thought about using FBO's?
No, I didn't thought about FBO's. I even didn't/don't know what they really are :-X I'm going to have a look at the tutorial for further information and details. Do FBO's also apply to 2D games? (Lemmings is 2D)

QuoteAs for stairs, They would work the other way, draw the scene first then the stairs.
As stairs are also erasable like the scene (partially, only where they are dug/broken), don't you think stairs should become parts of the main scene? Or should I handle them separately?

In the picture attached to this post, you can see : 1) stairs destroyed by a lemming (a lemming has just dug into these stairs) 2) and 3) lemmings digging in the ground. Note that stairs are built by the lemmings as well, they are not present at the initialization of the level.

QuoteAs for collision detection. Thats probably gonna be a head ache. You may need to store a 2 dimesion boolean array to store weather or not a pixel exists.
This is exactly what I intend to do ;) Well, more exactly, I intend to store the state/type of each pixels in an int[][] array. I think this the easiest and most "universal" way to handle a level map, even without having to care about graphics rendering.

Thanks again

Ciardhubh

How about that:

  • Initially load the level scenery into a texture.
  • Draw new scenery objects (stairs) on the texture (glTexSubImage2D).
  • Removing destroyed terrain could be done with Alpha Testing (e.g. glAlphaFunc with GL_GREATER = 0.5) and a texture that is transparent in the area of the explosion (disable blending, write over background).
  • Collision detection could be done by checking if pixels in question are transparent or not. Set viewport to only the area in question, render and use glReadPixels? However glReadPixels is said to be slow.

This would require something that keeps alpha values and does not replace/blend them with the background colour, so you can later draw it over the background and read alpha values. This is just an idea that might be worth looking into if it works. No guarantuee it will work :)

bobjob

Quote from: lorcan on November 14, 2008, 09:18:42
No, I didn't thought about FBO's. I even didn't/don't know what they really are :-X I'm going to have a look at the tutorial for further information and details. Do FBO's also apply to 2D games? (Lemmings is 2D)
Ok the reason i recommended FBO's, because basically you can render to a texture instead of the screen. You can get the same results using PixelBufferObjects but I figured FBO's are easyer to use, as I dont know how much experience you have had.

Quote
As stairs are also erasable like the scene (partially, only where they are dug/broken), don't you think stairs should become parts of the main scene? Or should I handle them separately?

The reason i said draw stairs after the scene, I ment in regards to minipulating the scene, stuff like bombs would be drawn first to the Depth buffer (hopefully you know about depth testing, if not say so its very easy to explain) then the Texture of the scene would be drawn and tested against the bomb texture. If the scene is, say, behind the bomb texture then it wont draw (thus it would have a hole in the texture).
But with stairs it would work the other way, draw the scene first, then just draw the stairs straight on top. Then lastly save the drawn scene to the scene Texture. After that is done you can draw the characters and such, and also test them against the new scene Texture.

If you weren't going to update the bomb/stair effects to the current scene Texture, you would have to store every explosion throughout the whole game and test alot of them against the characters, and it would be alot of processing for nothing. So its best to just update the scene Image, and that is best achieved through: FBO's, PixelBufferObjects, glTexSubImage2D
EDIT: oops you already know that  :P

tell me which you decide I should be able to give you a basic example updating a texture (except PixelBufferObjects havnt ever worked with them). I think your game idea is cool, and shouldnt be too hard to implement. So Im more than happy to help.


lorcán

QuoteOk the reason i recommended FBO's, because basically you can render to a texture instead of the screen. You can get the same results using PixelBufferObjects but I figured FBO's are easyer to use, as I dont know how much experience you have had.

I must admit I must learn what FBO's really are, how they work, how to handle them... I don't know anything about them however what you say seems much interesting. The only things I can do with LWJGL so far is just convert a BufferedImage into a ByteBuffer, use this for a Texture and apply it to a quad.

I've just read articles and source about GL11.glTexSubImage2D. This seems helpful and is rather "easy" to understand... However I still don't know If I can use transparent pixels in the "SubImage" in order to make pixels transparent in the "main" scene Texture (initilally made with glTexImage2D).

Could you teach me more about FBO's (tutorial, demo, code, or so)? Thanks in advance!

QuoteThe reason i said draw stairs after the scene, I ment in regards to minipulating the scene, stuff like bombs would be drawn first to the Depth buffer (hopefully you know about depth testing, if not say so its very easy to explain) then the Texture of the scene would be drawn and tested against the bomb texture. If the scene is, say, behind the bomb texture then it wont draw (thus it would have a hole in the texture).

No indeed, I don't know about depth testing I'd appreciate your explanations :)
What you are stating is fairly easy to understand however it's rather more difficult to convert this in opengl.

QuoteBut with stairs it would work the other way, draw the scene first, then just draw the stairs straight on top. Then lastly save the drawn scene to the scene Texture. After that is done you can draw the characters and such, and also test them against the new scene Texture.

Same opinion :)

QuoteIf you weren't going to update the bomb/stair effects to the current scene Texture, you would have to store every explosion throughout the whole game and test alot of them against the characters, and it would be alot of processing for nothing. So its best to just update the scene Image, and that is best achieved through: FBO's, PixelBufferObjects, glTexSubImage2D

Yes, this is what I intend to do :)
glTexSubImage2D seems rather easy, but as I've just told you, I don't know if can use this to create transparent pixels yet.

Could you give a comparison between FBO's and glTexSubImage2D? Maybe FBO also uses glTexSubImage2D? Don't know, I have to go into FBO's more deeply...

Quotetell me which you decide I should be able to give you a basic example updating a texture (except PixelBufferObjects havnt ever worked with them). I think your game idea is cool, and shouldnt be too hard to implement. So Im more than happy to help.

Thank you very much for your support... I really appreciate it :)

Also thanks for your advice, Ciardhubh ;)

Note : I'm absent this weekend (I'm abroad) ... ;)

wolf_m

Since this is a 2D game, I'd use a special monochrome bitmap for destruction.

Initially, that bitmap is black where the background is supposed to show through (alpha test result).

Destruction alters the destruction map exclusively; it acts on the non-black parts.

You then use the destruction map as an alpha map for making the background shine through at the right spots (black = opacity 0%).

Collision detection also is solved via the destruction map. That way, you already have your array (a monochrome 2D bitmap is in principle a boolean[][]). No duplicates.

That way, the background remains unchanged and everything is really cheap.

Edit: I see that Ciardhubh's solution is rather similar.

lorcán

Thank you very much for your kind help.

Here's is what I'm doing :
1) I first draw the scene to a BufferedImage and fill a 2-dimensional int array. I draw each pixel of the scene coming from a large number of PNG's. This operation is done using AWT's setRGB(...) method while reading each pixel of each PNG. I also store each pixel state/behaviour in the 2-dimensional int array for further collision detection and game mechanics. These 2 operations are admittedly but are executed only once (level initialization).
2) I load the BufferedImage into a texture. Note this BufferedImage contains transparent areas.

Rendering the whole game level to the screen uses these "layers" (from the rear to the front) :
1) animated indestructible background sprites (water, lava, ...) = textured quads
2) main scene = the big scene quad textured with the BufferedImage created earlier
3) animated indestructible foreground sprites (doors, ...) = textured quads
4) the lemmings sprites = textured quads

Since the scene BufferedImage won't be changed after its creation, I can keep it as a backup copy (maybe useful if the player would like to restart the level).

When a lemming destroys the scene or builds stairs, I update the corresponding pixels directly in the texture using glTexSubImage2D(...). I have to process each altered pixels separately since stairs and holes are not always rectangles. I mean there can be transparent pixels around the object in the original image so I have to bypass these transparent pixels otherwise it would erase pixels in the main scene.

To do this, I use such the following method after binding :
private void setPixel (int x, int y, int red, int green, int blue, int alpha) {
    byte[] temp = new byte[]{(byte)red, (byte)green, (byte)blue, (byte)alpha};
    ByteBuffer subImageBuffer = (ByteBuffer) BufferUtils.createByteBuffer(temp.length).put(temp).flip();
    GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, x, y, 1, 1, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, subImageBuffer);
}


I can use such a method to draw new pixels as much as to erase existing ones (drawing stairs and destroying terrain).

What do you think about this?

> wolf_m : could explain me how to create and handle such a destruction (alpha) map? Since my scene texture already has transparent pixels (where there is no terrain), does this alpha map only handles terrain "destruction"? Could you tell and explain me the opengl methodsused for this?

> Ciardhubh :
QuoteRemoving destroyed terrain could be done with Alpha Testing (e.g. glAlphaFunc with GL_GREATER = 0.5) and a texture that is transparent in the area of the explosion (disable blending, write over background)
Do you mean using an additional texture with only "holes" over the current scene texture? Could you also tell me and explain me the opengl syntax to use in your example?

> bobjob : could you go further with your ideas and explanations? (how you handle destruction in the depth buffer, how you draw steps, how to update e texture using FBO's...).

Thank you very much ;)

bobjob

Before I give you an FBO example, here is a depth testing example, notice how openGL can do the "hard part" of removing an image with alpha values.

bobjob

this uses Devil, and lwjgl 1.4... so you should use your own texture code, as well as change the import statement from

import org.lwjgl.opengl.glu.GLU;

to

import org.lwjgl.util.glu.GLU;

main things to notice in the code:
the 'z' value of the scene is different from the effect image.
you would draw stairs in the same way as the stencil style image, except you wouldnt change the GL11.glColorMask values (leave them all true).
when drawings are tested against each other which ever pixel is closer to the viewer will pass.


keys:
G: turn on depth testing
H: turn off depth testing

S: explotion mode
H: "stairs" mode


So Basically make sure Depth Testing is always on. draw the objects you would like to add imprints/stairs then draw the scene. And the variable that changes which you are drawing is the colorMask. You should be able to notice this with the key presses

wolf_m

Quote from: lorcán on November 18, 2008, 16:41:38
> wolf_m : could explain me how to create and handle such a destruction (alpha) map?
No, I can't, I personally don't do it and don't want to look into the details right now.
But I guess it's pretty much what you're doing there, except you're starting with white and writing black for alpha found in your BufferedImage into another BufferedImage.
Quote
Since my scene texture already has transparent pixels (where there is no terrain), does this alpha map only handles terrain "destruction"?
And collision detection with destructible stuff, I'd say.
Quote
Could you tell and explain me the opengl methodsused for this?
No, sorry, you'll have to figure the messy details out by yourself.
Buy the Redbook if you haven't done that yet, that's 800+ pages of explanation, can't compete with that..

lorcán

> bobjob : Thank you very much for your explanation and demo sample.

So Basically, if I understand correctly, I should use 3 quads (all fitting the level dimensions)? 2 quads in the rear for destruction/stairs (glColorMask values set to true for the stairs quad, false for the destruction quad). These 2 quads are put at, let say as an example, z -1 and 0. And the third quad in the front for the global scene texture. This third quad (z = 1?) would remain unchanched (as well as it texture) and I should edit the 2 other textures (using GL11.glTexSubImage2D? on both of them). Is this what you expect me to do? Or would I handle destruction and stairs in another way? I understand what your code sample actually does rather clearly but I don't figure out what's at the back of your mind :P Maybe in the next chapter ;)

What do you think about my ideas expressed in the previous post (editing pixels in the main scene texture)?

Thanks again!

bobjob

basically the way i was thinkn would work like this (with FBO extensions)

This would all be drawn not on screen, but on the FBO texture ->

1. Load all Main textures, keep a copy of them all unchanged.

2. generate 2 FBO's with a new texture wrapped to each one, ill attach another example, but i think its best to understand the tutorial as well as go through the lwjgl version of it (the link from before).

3.
before starting draw the main scene texture onto both of the FBOs (as thiese are the textures that will be minipulated during runtime).

4. When minipulating the scene (again this wont make much sence till you go through the tutorial):
you would bind the first FBO.
a. "Cut out" Images would be drawn before the main scene, at a z = 1 (therefore closer to the viewer) with the color mask set to false.
b. the second FBO texture (copy of the scene) would be drawn at z = 0.
c. "stairs" would be drawn after the scene but still with a z = 1 (same 'z' as "cut out" images).

then you would draw the first FBO onto the second. (this is done because if you draw the scene onto the new scene, Im guessing there will be errors, its good to have the "temp" copy refering to step (b)).

Note: The way the stairs are drawn can be different, you could disable depthTesting, and not worry about its z value, but it might be confusing tryn to track weather on not depth testing is currently enabled, so I suggest just do it the way i explained for starters, you can always tweek the code later.

Once you have drawn to the texture the way you would like it using the FBO extension, you can then just draw the new scene as it will be stored into its own texture (either of the FBO wrapped textures, as the should look the same at this step).

Ill attach an FBO example but i still think in order to get a grasp you should go through the tutorial.

As for collision detection, Im guessing that will be per pixel, so you would need to get the value of each indivual pixel you want to test at a time, and check if its alpha value is = 0, or if you want you could make it if alpha < 0.25f (as that should be close enough). The 0.25f would also look cool, cuz you could have them going through semi-transperant tunnels and not getting blocked :D

bobjob

You have actually tempted me to make my own online turn bassed Worms game, as a side project.