Terrain Texture Layer question

Blitz3D Forums/Blitz3D Programming/Terrain Texture Layer question

John Blackledge(Posted 2011) [#1]
I've looked all through the archives and forums and still can't get my head around multiple layers on terrains, e.g. to create paths.

I'm assuming:
height-map (doesn't count as a layer)
color texture (e.g. main grass texture)
path texture (cobbles stones or something)
alpha texture (to combine the two above)

Could someone please explain
(1) which layer each of those should go on, and
(2) what the appropriate texture filters or texture blends are for each.
Thanks.


Kryzon(Posted 2011) [#2]
It's easier to understand splatting if you look at it by the 'tiling' aspect.

There is a difference in applying a stretching texture to a terrain (stretching it to map the entire terrain at once, looking blurry when viewed up close) and tiling it (several small copies of it are mapped continuously side by side in all directions, joining at the seams, looking like a single big high-detailed texture).

Terrain splatting (the technique for drawing your paths and different textures in the same surface) consists of applying pairs of textures.
These pairs are formed by a tiling texture (grass, dirt, sand), and a stretching texture that masks where the tiled texture will appear.
So they always come in two - one for coloring, the other for masking, with their respective tiling and stretching mappings.

The only problem with splatting is that you need to access the Direct3D texture combiner, a part of the D3D API. You can't do this with Blitz3D natively - if you search for 'texture splatting' posts under my name I think I've posted some links with resources (fastlibs for instance).
That part of the API does the following: "modulate the color of the tiling texture not with its own alpha, but with the alpha channel from the stretching texture right below it".

In more technical terms - understand 'stage' as in layer (quoted from the GameDev article):

Stage 0 passes its alpha value up to stage 1.
Stage 1 uses that alpha value as its own and pairs it with its own color value.


Since the stretching texture is stretched on the terrain covering it entirely at once, so is this alpha-channel. So while the color is coming from small, tiled textures, the alpha used to show or hide them is coming not from each tiled texture but from the big stretched texture, so you have one big alpha-channel to control where you want tiled textures to appear or not (with smooth variations if you wish so).

I hope this helps to understand it more. If you were looking for something more visual and not just textual, I can manage a few explanatory images (although plenty of external resources have them already).

Last edited 2011


John Blackledge(Posted 2011) [#3]
Thanks, Kryzon. Nearly got it, but not quite.
The 'path' texture would be tiled, to get the necessary 'cobblestone' detail. No problem.

Are you saying:
Stage 2 color texture (e.g. main grass texture)
Stage 1 path texture (cobbles stones or something)
Stage 0 alpha texture (to combine the two above)
???

(I'm aware that FastLibs can do this, but their examples always use an alpha'd PNG; and what I want to do is to build this into my own engine so that I can 'draw' onto the alpha texture, then save as a BMP; I don't know how to save as an alpha'd PNG.)


Kryzon(Posted 2011) [#4]
Actually when I say color I mean it's a full texture loaded with LoadTexture(), but just uses the color (flag '1'). It doesn't have alpha.
The alpha textures, on the other hand, only have alpha and don't need any color (no need to have color in something that won't use it).

This makes it:

Stage 0 - Grass texture [tiled]

Stage 1 - Alpha texture [stretched]
Stage 2 - Cobble texture [tiled]

Stage 3 - Alpha texture [stretched]
Stage 4 - Sand texture [tiled]

[...]

Note the Grass texture layer zero doesn't need an alpha texture before it because it's the base texture I want to have for my terrain. If nothing is on top of the terrain, I want it to display a grass tiled texture.
All layers I want splatted - everything that covers small patches of terrain, like paths and hilltops - need an alpha-only texture before them so they are properly faded.

Last edited 2011


Kryzon(Posted 2011) [#5]
Oh, and if you want to save transparent PNG's I'd advise using MarkCW's FreeImage wrapper for B3D.
Then using the GetPixelColor and SetPixelColor functions with the high-color bitmap used for painting, clone the color values but add the alpha channel and then save that as a PNG.


John Blackledge(Posted 2011) [#6]
Woo - got it Kryzon!

You've given me a great formula there.
I'll try it out tomorrow.

Thanks!

("clone the color values but add the alpha channel"
Err... I'll try to manage with BMP's. This is only for my own use.)


John Blackledge(Posted 2011) [#7]
Ah (one minute later), another quick question:
Isn't the TextureBlend setting crucial and different for each one?


John Blackledge(Posted 2011) [#8]
Well I've tried it with all possible TextureBlend settings, and it doesn't work.

Does this mean that we're back to your comment of "You can't do this with Blitz3D natively" and I have to use FastLibs? (which I've bought)
Which means I have to learn to save/load/manipulate alpha'd PNG's from within Blitz code <sigh>.


Yasha(Posted 2011) [#9]
It hasn't been made explicit in the posts above but I think the assumption is that you use more than one terrain object. (EDIT: No, looks like I was way off. Whoops.)

Each terrain uses the same heightmap, and has one alpha texture and one material texture. By placing them all in the same place (possibly over a completely solid base layer), each one is only visible in the areas where that material should be. I presume this is why Kryzon broke up the list of textures into groups of two.

The biggest problem with this method is probably going to be Z-fighting (that's when the Z-buffer loses precision over a long distance and can't decide which polygon should be in front, so they flicker). The only reliable way I know of to get rid of Z-fighting is to either heavily limit draw distance or to use custom mesh terrains (the rules of Blitz meshes seem to be that polygons hand-placed in the same spot on the same mesh are drawn "in order", so in that case you can put the top layer in place last and it won't fight).

The other thing is that this is also not very efficient, involving a lot of overdraw (the part that "can't be done" is I think the classical method with only one mesh). It can definitely be done though, as there are plenty of screenshots of it floating around the forum, so don't give up!

Last edited 2011


Kryzon(Posted 2011) [#10]
Does this mean that we're back to your comment of "You can't do this with Blitz3D natively" and I have to use FastLibs? (which I've bought)
Which means I have to learn to save/load/manipulate alpha'd PNG's from within Blitz code <sigh>.

FastEXT adds new blending modes to TextureBlend(). These blending modes are the ones that were left out from the original B3D set, and are the ones that are necessary to carry out this kind of blending.
You can see them in use in the "FastExt_Example_TextureBlends.bb" example.

("clone the color values but add the alpha channel"
Err... I'll try to manage with BMP's. This is only for my own use.)

If I'm not mistaken, you're trying to do a sort of an interactive way to paint the mask texture, right? I mean, paint directly in your terrain where you want a path to go, for instance - then save this as an image so it can be loaded back.
I think making this functionality from scratch doesn't compensate the hard work you'll have - especially when there are already-made alternatives like Blender 3D (sorry for the Bit.LY link; these forums don't like broken down wiki links like the one it leads to - I hate hidden bit.ly urls).

A small workflow: paint your heightmap in your painting program, build it as a mesh in Blender (just temporarily for size), paint the mask textures in real-time using the Texture Paint feature and then export the painted textures as your mask textures. Sort them out and re-apply in Blitz3D.
Scribbla has done something like this in Modo - thread (plenty of info there).

@Yasha: Yeah, I originally meant a single terrain, multitextured - the separating of layers in pairs was just to make it more clear that we need to think of each "flavor" of splatting texture (sand, dirt, rock) as a pair of tiling and masking textures. I hope I didn't confuse anyone else!
Extra meshes do have their use, however, especially if you want to use more layers than what the user's graphic card allows (like 2, 4 or 8).

Last edited 2011


John Blackledge(Posted 2011) [#11]
Thanks for the link.
I met Scribbla some time ago - really nice guy.

Very, very interesting thread - much to think about.
As you say, yes I really want to paint my paths in realtime, then save.
It might even be that two terrains (top one alpha'd) might be the way to go.

But finally we seem to have defined that Blitz _cannot_ do this in native mode, and the FastLibs author is a genius.


Rroff(Posted 2011) [#12]
Terrain in blitz has a lot of issues, none the less the alpha blending will run into a lot of depth sorting issues with any real use, then theres issues with lighting (some layers won't light correctly).

I'm contemplating a system atm whereby drawing a high res terrain near the player (say 4x 1024x1024 textures) and splatting decals directly to them in 2D from an array in realtime, and another 1024x1024 texture for a low res distance terrain. Not sure if fastimage has the ability to blend alpha'd images into another image properly but would need that functionality - or failing that have to render the textures in 3D (fullbright) with quads for each decal, capture to a texture and render to the terrain.

Last edited 2011


John Blackledge(Posted 2011) [#13]
A quick summary:-

Native Blitz3D cannot do this.
'Insaner' is a fudge that uses a grass/rock combination layer (arithmetic difference, produced with PSP) which nevertheless still has bright yellow pixels punching through. Ok 10 years ago, but not now.

T.ed was my next hope (-even though I wanted to do this in my own engine), but seems unable to import my heightmap without stretching it 100 high. Available height settings seem to have no effect on the y-axis unless T.ed makes it.

L3DT was my next stop, and looked hopeful.
But when I import my 1024x1024 heightmap it always reduces the detail to an unacceptable level. My guess is that it _will_ import 1024x1024 but has an internal working resolution of 512x512.

Back to Fastlibs.
The TextureBlend example does exactly what it says on the tin.
Even though the demo shows multi-textured cubes, the point is proven, and I've managed 4 layers: 1 grass, then stone, cobbles and sand.
As long as the inbetween PNG is sound it really will do the job.

This is the sort of thing I did:


Now all I have to do is search the forums to find a way to convert my (2D or 3D) path drawing into a PNG alpha layer. (-All!)


John Blackledge(Posted 2011) [#14]
Speaking of which:
"Then using the GetPixelColor and SetPixelColor functions with the high-color bitmap used for painting, clone the color values but add the alpha channel and then save that as a PNG."

Duh....
Kryzon - do you have an example?


Rroff(Posted 2011) [#15]
Not to put you off and theres probably more ways to go about it than I've thought of but having been down this road already - your going to run into a lot of issues, using alpha for paths is ok but quite limiting if your terrain is any decent size.

At the kind of resolution showin in my screenshot your either gonna run out of precision after more than about 0.25x0.25mile terrain or end up using a ton of memory.

test1

test2

and even once you solve that and the alpha sorting images you will get random sorting issues if you use alpha blending for vegetation and then unless you use baked lightmaps you have all sorts of lighting issues where one or other layer won't be lit correctly.

There are work arounds but they are all ugly limiting hacks imo.

Last edited 2011


Kryzon(Posted 2011) [#16]
Good thing you posted the summary and code :)

Speaking of which:
"Then using the GetPixelColor and SetPixelColor functions with the high-color bitmap used for painting, clone the color values but add the alpha channel and then save that as a PNG."

Yeah, about that. I don't have experience with FreeImage, but in a theoretical level I would know what to do.
You need to have your painted path as a Blitz3D texture or image, because you'll need to ReadPixelFast() from it. Do whatever you need to do to have it (perhaps you are already drawing to a texture or image, so you're halfway there).
I imagine this painted mask texture of yours is a grayscale image.

The FreeImage wrapper by markcw comes with some utilitary functions, such as a function to convert a Blitz3D image to a FreeImage one.
Here is a slightly modified version [by me] to convert your grayscale, painted path texture to a FreeImage image - it will take the grayscale information and write this as alpha in the FreeImage image. So your painted data becomes alpha (the color for this image is black, but it doesn't matter since you'll only use the alpha of this image later on when doing splatting).

I haven't tested it. You need to have that FreeImage wrapper for it to work properly.

After using this function you can load 'filename$' as a texture with alpha channel and it should come with alpha information for you to use as a mask.

EDIT: Changed the code a bit.

Last edited 2011


John Blackledge(Posted 2011) [#17]
FreeImage_SetPixelColor(dib, ix, height - 1 - iy, (pixel And $FF) Or ($000000)
;The above writes the Blue channel (could be Red or Green too, since it's grayscale) as an Alpha value. The new color will be A,0,0,0 (black color with a varying alpha value).

/\ That appears to be what I need. Thanks, Kryzon.

I can see I still have a lot of work to do:
-Point at the terrain, get position.
-Equivalent memory drawing to texture.
-Save texture with correct values through FreeImage.
-Load it back in.
(Hah! I just remembered a problem: If I load back in the same named file, DX will ignore it ('already got that one') so the saved/loaded texture will have to have an incremented filename for each save/load.

Thanks guys, for all the help!


John Blackledge(Posted 2011) [#18]
Kryzon,

Wrapper and dll in place. Code compiles and runs.
I'm feeding a Blitx texture handle to your code, then

Memory Access Error on:
FreeImage_SetPixelColor(dib, ix, height - 1 - iy, (pixel And $FF) Or ($000000))

(The rest of the code appears to have good variables.)
Any ideas?


John Blackledge(Posted 2011) [#19]
Another little test:

I load a texture (don't edit it), then send it straight out through FiSaveAlpha(texture, filename$), and Paint Shop Pro tells me 'The specified file cannot be identified as a supported type.'

Duh?


Kryzon(Posted 2011) [#20]
Are you running in debug mode? is that all it says?

Oops. Noticed the error. I was considering 'pixel' as an integer value, when it's actually a bank - can you see the same error?

So replace the inner pixel-writing part with this:
Local tempPixel%

LockBuffer [...]
For [...]
   For [...]
      tempPixel = ReadPixelFast(ix, iy, tempBuffer)             ;Read an Int value.
      PokeInt pixel, 0, ((tempPixel And $FF) Or $000000)        ;Poke Int value into bank.
      FreeImage_SetPixelColor(dib, ix, height - 1 - iy, pixel)  ;Send bank to wrapper.
   Next
Next
UnlockBuffer [...]


An additional thing. Did you notice I edited the code in my post above and changed the color masks? take a look:
dib = FreeImage_Allocate(width, height, 32, $00ff0000, $0000ff00, $000000ff) ;The way I think it should be, a full 32 bit value for each.
Make sure you replaced the original line with this too.

In any case, I'll edit the original FiSaveAlpha function with these.

Last edited 2011


John Blackledge(Posted 2011) [#21]
Kryzon,

FreeImage_SetPixelColor(dib, ix, height - 1 - iy, pixel)
Gives runtime error "User lib function not found"

And yet
FreeImage_SetPixelColor%(dib,x,y,value*):"_FreeImage_SetPixelColor@16"
is in the decls.

Again - any ideas??


John Blackledge(Posted 2011) [#22]
Tried a much later version of the dll, 2007.
Still the same error.


Kryzon(Posted 2011) [#23]
Is the keyword being highlighted? I don't see any other errors; I guess it's not on my end.
I've dependency-walked the most up to date DLL and didn't seem to find different function names, so it should work.

Are you sure you have everything userlibbed correctly? placing the .decls and DLL both in the userlibs folder, and the Freeimage.bb Include in your code?
If you're running the executable build of your code make sure you have the DLL in the same folder as the .EXE, or at least in your System32 directory (it's worth placing the DLL in this folder even if you're running directly from the IDE).

Last edited 2011


John Blackledge(Posted 2011) [#24]
It's already executed FreeImage_Allocate before it hits the problem.
Is your dll later that 3, 9, 3, 0 (2007)?


John Blackledge(Posted 2011) [#25]
BTW, if I comment out the FreeImage_SetPixelColor line then
bool = FiSave(dib, filename$) also executes just fine.
And PSP loads the resulting blank PNG too.

I wonder if I could just use
WritePixel x,y,argb,[buffer] command
and copy the blue to the alpha byte.
- Maybe work on that tomorrow.
(If I can remember/work out how to shift the values!)


Kryzon(Posted 2011) [#26]
Yes, it's the latest build (3.15). I don't know what the problem is.


John Blackledge(Posted 2011) [#27]
How does this look:

rgb=ReadPixelFast(x,y) And $FFFFFF
b = rgb And $FF
a = b shl 24
argb = a + rgb
WritePixelFast(x,y,argb)

...but is a dib the same as a texture buffer? (-for the bool = FiSave(dib, filename$) line)

Must go to bed now... brain won't stop!


Kryzon(Posted 2011) [#28]
I don't think you should try another way. The original FiSaveImage() function by markcw uses the same SetPixelColor (and that is presumably tested a lot by himself and others), so it should be finding the function in the first place.

I know. Test the original FiSaveImage() function present in FreeImage.bb (try it out with any random image you have).
If that doesn't work as well, it's the communication between DECLS -> DLL rather than anything in our code.

Last edited 2011


John Blackledge(Posted 2011) [#29]
Don't worry, as I said above

bool = FiSave(dib, filename$) also executes just fine.
And PSP loads the resulting blank PNG too.

But I've noticed that

FiSaveImage(image, filename$)

converts an image to a dib then saves as normal, so that's the next test.


John Blackledge(Posted 2011) [#30]
Got it:
The original freeimage.decls from http://www.blitzbasic.com/codearcs/codearcs.php?code=1732 had multiple .lib "..." lines that had not been commented out, so Blitz was getting confused as to where the dll lived - but only for certain calls!

I have now correctly saved a PNG with alterations that loads into PSP. - Yeehah. Now to refine it.

Last edited 2011


Krischan(Posted 2011) [#31]
John, did you try this?

http://www.blitzbasic.com/codearcs/codearcs.php?code=2620


John Blackledge(Posted 2011) [#32]
(Krischan, I did have a look but it seems to use multiple meshes. What I wanted was a process that I could add to my existing terrain code.)

Having spent the last 24 hours (re)learning bitwise operations, and having copied the blue bytes to alphas, I still got no good results.

So I cut my code right down, so it simply (a) loaded a PNG (which was previously tested as working well with FastLibs multitexturing) and (b) immediately saved it using FastImage.

Low and behold, the resulting PNG (1) could NOT be used with FastLibs (no filtering or 'splatting' produced, and (2) when loaded into PSP displayed as having no transparent layer.
I could of course use PSP's save > Optimise Wizard to set the transparent layer.... but that's the point - one was NOT set within the PNG that FastImage had produced.
Even when I 'forced' the top fourth alpha byte layer - just in case Blitz had got rid of it in LoadImage - the result was the same

Conclusion: FastImage does not produce FastLibs-compatible PNG's. A fourth alpha-layer, yes, but without the alpha 'marker' (that PSP adds) is unusable with FastLibs.

I would love someone to prove me wrong, I really would.
I don't mind if someone says, "You prat, you missed....."

Well, I seem to have two of the leaders in this field, Krischan and Kryzon, reading this thread, and guys I really appreaciate your feedback.


Warner(Posted 2011) [#33]
If you have a white texture with either alpha or masking enabled (flag 2 or 4) on the first texture layer, and you have a non-transparent texture on the second layer, you can use "EntityFX terrain, 2" to apply alpha from tex1 onto tex2.
This code uses 2 terrains:


Last edited 2011


Kryzon(Posted 2011) [#34]
Try this: http://blitzbasic.com/codearcs/codearcs.php?code=1

But change this:
WriteInt f,ReadPixel(x,y,tbuffer)
To this:
col = ReadPixel(x,y,tbuffer)
WriteInt f,((col And $FF) Or $000000) ;Outputs BLUE as alpha and hardcodes RGB as BLACK.
TGA's use alpha as well. Many triple-A games use it. Blitz3D can load them.


John Blackledge(Posted 2011) [#35]
Kryzon thank, but Fastlibs only accepts PNGs as masks for texture layering.

Warner, thank you.

Despite the fact that I said that I only wanted to use 1 terrain, yours has to be one of the simplest and most interesting solutions to this problem that I've come across.

It really does the job, and does away with the need for FastLibs or FastImage.

Can you explain why terrain 1 allows terrain 2 to be visible?
Is there some interaction that I'm not understanding?


Krischan(Posted 2011) [#36]
John, my second Version only uses a single mesh. This was the best try I could get out of B3D (beside the third version with a normal map to increase detail / vertex lighting).


Warner(Posted 2011) [#37]
Is there some interaction that I'm not understanding?

I don't think there is any more interaction that this: terrain1 is rendered first, without alpha, so it writes to the z-buffer. Terrain2 is rendered on top of it, with alpha, without writing to the z-buffer.
What the example mainly shows is, that if you use EntityFX 2(use vertex colors) somehow the alpha information of the first texture layer is combined with the color information of the second texture layer.
I don't know why it does that, it might be an undocumented side effect.


Kryzon(Posted 2011) [#38]
My thoughts on Warner's demo:
From what I can see, both terrains are writing to the Z-Buffer. The only way this would be otherwise is if you would change the EntityOrder (as it disables Z-Buffer writing for the mesh).
The EntityFX 2 implies no difference (such that you can comment that line and still have the same effect).
The decaling effect I believe it's due to the masked texture being in the first layer (zero). If you move all textures of Terrain2 by one index (meaning, the path texture in layer 1 and the rock texture in layer 2), the effect doesn't work anymore.
I guess this works in layer zero because the masking is not only hiding the texture but also the underlying mesh, which leaves completely transparent areas. So if you used a texture with an alpha channel and loaded with flag "2" and placed it in layer zero, it should give smooth transitions which is nice to have available.

My thoughts on Krischan's demo:
I don't agree that it uses a single mesh: from what I can see it's using multiple surfaces (to be a single mesh it would need to be rendered 'in a single draw-call', and each surface requires a different one, even if they belong to the same Mesh entity). It works, however, and shows no flickering at all.
I guess the biggest problem is that by manually building terrain meshes you lose Blitz3D's native terrain LOD feature (fly through a blitz3D terrain and turn on wireframe to see it in action: it always keeps the foreground with more polygons\detail than the background).
You would have to split it in a chunk hierarchy so it behaves similarly to quadtree culling to make it efficient for production uses.

Both demos do not suffer from z-fighting\flimmering\flickering, so they're also both viable paths to go.
If I were dealing with a shader-capable engine I'd go with texture splatting, because you could store different alpha maps in each color of a single "path" texture, and treat each one differently with the pixel shader.

@John: how do you know fastEXT only accepts PNG's for that FE_MODULATE blending?


Warner(Posted 2011) [#39]
You're right. I was confused. On a sidenote: if you use an alpha texture instead of the masked one, the blending doesn't seem to work. It messes up z-ordering.


_PJ_(Posted 2011) [#40]
This is probably not at all related, but I ohope it can be useful, sorry if it's way too 'noobish' for the topic - I'm slowly getting to grips with a lot of this stuff, as I want to do some nice mutltexturing to terrain meshes too.

There seems to be differences to the ordering of RGBa or aRGB depending on file formats and whether working with DX or other libs. Some use Alpha as the high byte, others as the low.
Typically, with heightmaps and BliitzTerrains, Blitz apparently uses the red channel as the height, though I am not sure whether this takes into account whether Red is the Low byte (RGBa).
Just something that may be of interest???


Kryzon(Posted 2011) [#41]
if you use an alpha texture instead of the masked one, the blending doesn't seem to work. It messes up z-ordering.

Hmm, now I'm confused... strange that masking is the only flag that gives that decaling effect. Must be because of Alpha Test, I wouldn't know.


John Blackledge(Posted 2011) [#42]
Malice: I'm pretty sure Blitz uses the red channel for a heightmap, so in that sense it doesn't matter if it's grey or coloured; it's just that it's easier to design with when it's shades of grey.

Kryzon: "how do you know fastEXT only accepts PNG's for that FE_MODULATE blending?"
I have done many tests with different formats and file types (-hoping to manually save my path-drawing efforts out of my engine via FreeImage).
Only PNGs with an alpha channel work (though TGA's might too).
PNG's saved by FreeImage do NOT work.
PNGs saved by Paint Shop Pro (and probably others) do work, if they have an alpha channel set, not just an 'alpha-colour'.
So it just means I have to design my paths as a PSP 'layer' that can be saved separately.