2.5.1 Textures: The basics

Colored, blended and transformed shapes are nice and neccessary for all kinds of graphics but not quite sufficient for "real" game graphics. Let's texture them.

To do this, we first need to generate a texture using image data which we can load from an image file. The shortest way to get a result is using the Load command:

ZweiDe.Texture texTemp = ZweiDe.Texture.Load( ... );

It accepts one to three parameters. What it definitely needs is a filename referring to a source file. ZweiDe uses DevIl for image file loading, so most common formats should be accepted.

Optional parameters are additional texture flags and the noStretch boolean but we'll come back to them later.

After loading a texture, drawing it is just as easy:

ZweiDe.DrawTexture( ... );

Pass the texture object and a position and the rest is done by ZweiDe. Internally, drawing a texture equals drawing a textured rect, so anything you can do with a shape (Coloring, blending, transforming, ...) is also possible when drawing a texture!

It is also possible to fill out a certain area using a texture using

ZweiDe.DrawTextureArea( ... );

This command works similar to DrawRect except it needs you to pass a texture and fill style (tiled or stretched?). Keep in mind that stretching is not always an available operation; this topic is discussed in the chapters "Texture Flags" (->EdgeClamp) and "Stretching"!

If you want a texture to be transformed - for example - around its center instead of its upper left, you'd have to call ZweiDe.SetHandle whenever drawing the texture. You can simplify this by calling a texture objects individual SetHandle method once after its creation; its individual handle will be automatically applied in addition to the global handle.

texTemp.SetHandle( ... );

These are the Texture basics which should be sufficient for most purposes. More advanced texturing topics will be explained in the following chapters.



2.5.2 Textures: Texture Flags

You may have noticed the two yet-unexplained parameters in ZweiDe.Texture.Load specifying texture flags and stretching; this chapters aim is - among others - to make you understand what excactly they are doing.

Let's start with texture flags. Their purpose is to configure a textures general behaviour for example whether or not filtering is enabled when displaying the texture scaled or rotated.


This is how a texture looks rotated and downsized without using any texture filter. It is the case when not using any of the following filtering flags.
Hover the image with the mouse cursor to see how it looks like when using

TexFlag.MipMap


It looks a lot better but in some (especially rotated) cases it may still look "blocky" in progressing transformation. To avoid this, there is an additional smoothing flag.
Hover the image with the mouse cursor to see how it looks like when using

TexFlag.MipMap | TexFlag.Smooth

It also has a slight effect when using it without Mipmapping.

This is how a texture looks rotated and unscaled without using any texture filter. Mipmapping doesn't make any difference here as it works with a series of precalculated half-size textures which are mixed when displaying the texture downscaled. In an un- or upscaled case, these half-size "Mipmaps" are ignored while the smoothing filter is still active in upscaled cases.
Hover the image with the mouse cursor to see how it looks like when using

TexFlag.Smooth


Click on one of the hover-images above to get a more detailed overview image comparing different filtering methods.


The next texture flag is rather unspectacular and of no use in most cases. Though, in some cases, it is absolutely neccessary: The EdgeClamp flag.

When adding

TexFlag.EdgeClamp

to the texture flags, the texture data is assumed to describe a finite image which is not intended to be tiled. When exceeding a textures area, it is normally - without EdgeClamp - "wrapped around" and repeats itsself.
With EdgeClamp, only the textures last pixel is repeated.

So, EdgeClamp is not for tiled textures but it doesn't seem to have an effect on non-tiled textures either.. or does it?

Take a look at the right image. It has been upscaled 2x in a popular graphic program (a) and in ZweiDe using SetScale when drawing the same texture with (b) and without (c) EdgeClamp.
When now looking at the left image - a bigger version of the non-EdgeClamp 2x upscaled image bit - you'll notice the left edge to be grey instead of white; the same occurs with its lower edge: Grey instead of black.

This is because without EdgeClamp, the
texture is assumed to be infinite, repeating when exceeding its area. When smoothing is performed, the nearest pixels are picked out and mixed together. At the non-EdgeClamp textures border or edge some of the nearest pixels are located at the textures opposite border. And that's the point. This is why non-tile textures should always use the EdgeClamp flag.

A little hint: You can globally specify what texture flags are used when leaving out the flags parameter on texture creation using

ZweiDe.Texture.DefaultTexFlags = flags;




2.5.3 Textures: Anisotropic filtering

We've now completed the texture flags. Now's time for anisotropy!
You may have heard of anisotropic filtering. It is a technique that is used to prevent filtered textures drawn distorted or in a perspective being blurry. You can activate it using

texTemp.AnisotropicLevel = level;

while "level" is a value from 1.0 (no anisotropic filtering) to ZweiDe.Texture.GetMaxAnisoLeve(). You may also set a default value using

ZweiDe.Texture.DefaultAnisoLevel = level;

You can take a look at how anisotropic filtering looks like combined with different texture flags in the following list. Hover one of the images with your mouse cursor to see the same with 16x anisotropic filtering and click on one of the images to open a more detailed overview:

TexFlag.None

compared to

TexFlag.Smooth | TexFlag.MipMap


TexFlag.Smooth | TexFlag.MipMap

compared to the same with anisotropic filtering.

TexFlag.None

compared to

TexFlag.Smooth | TexFlag.MipMap

with anisotropic filtering.
As you can see, the results with anisotropic filtering are better than the ones without. Though, it only has an effect when drawing distorted textures and should remain deactivated if a texture is intended to be drawn undistorted (rotated, scaled using DrawTexture) as it consumes render time.



2.5.4 Textures: Stretching

Enough with anisotropic filtering, let's take a look at the third parameter of Texture.Load: The noStretch-boolean. So, what is "stretching" anyway?

To explain this, we need to go down to the OpenGL level of ZweiDe. Most video cards - and so OpenGL - only support textures which have dimensions that are a power of two e.g. a width of 2^i and a height of 2^j while i and j are non-negative integers.

Of course ZweiDe also supports any other texture dimensions, but to be honest: Non-power-of-two textures are simulated while internally still being power-of-two.

Basically, there are two techniques to simulate being a power-of-two-texture in ZweiDe:

1. Generating a power-of-two-texture but using only the part that is needed to contain the non-power-of-two data filling out the rest with specific color.

2. Generating a power-of-two-texture and stretching the non-power-of-two data in order to fit the textures dimensions.

The noStretch parameter is how you choose between the two versions. So, what's the difference? Take a look at the following comparison:
The left image shows how a 100x100 grid texture is displayed when loaded unstretched.

The right image shows how it looks like when trying to tile it. A rather strange "tiling" is the logic result.
The left image shows the same texture when loaded stretched. Apparantly, it looks a little blurry - which is no surprise because it has been upscaled to fit the power-of-two-texture and then downscaled again to fit its original dimensions when being displayed.

The right image shows the tiling experiment. It looks just like a tiling should look like.

Conclusion: Only use stretched textures if you don't need pixel perfection or it is absolutely neccessary to tile a non-power-of-two-texture.



2.5.5 Textures: Texturing a shape

Tired of DrawTexture and DrawTextureArea? You want to texture whatever shape you like? In this case, this is your chapter.
Texturing a shape isn't harder than by-vertex-coloring - in fact, it works similar using the same stack technique.

When texturing a shape, each vertex needs a UV coordinate; it describes a percentual position on the texture image that will be located at the current vertex during rendering. Imagine rendering a textured rect that shall look like as if we'd drawn the texture using DrawTexture. Its upper left vertex should be textured with the textures upper left, its upper right vertex should be textured with the textures upper right, etc. etc.
In UV coordinates: The rects upper left will get a UV of [0.0, 0.0], its upper right will get a UV of [1.0, 0.0], its lower rights UV will be [1.0, 1.0] and so on.

A UV coordinate of [1.0, 1.0] represents the actual textures lower right. However, if you have loaded a non-power-of-two texture without stretching it to power-of-two dimensions, [1.0, 1.0] exceeds the boundaries of your actually loaded image as this only covers a part of the internal texture area; while [1.0, 1.0] refers to the power-of-two container texture. What UV coordinate you need to target your image datas lower right is saved in the texture objects UVRatio property. This is also used in DrawTexture and DrawTextureArea in order to calculate the vertices UV coordinates.

Specifying a shapes vertices UV-coordinates works similar to specifying their color when using by-vertex coloring: You add all your UV-coordinates to a "first in first out" stack and when the shape is drawn they are automatically used in the same order you've put them to the stack.

ZweiDe.AddUV( .. );