Inertia's picture

Texture Loaders

Some info regarding the texture loaders:

There's currently 2 different loaders:
a) one using .Net's GDI+ to load uncompressed textures.
b) the other is loading compressed .dds textures in DXT1/3/5 formats with custom logic.

Both loaders will only set texture baselevel and maxlevel, they do not set filtering, wrapping or anisotropy - or touch any other OpenGL state besides the currently bound Texture.

They are loaders only, no texture pool and/or GL.DeleteTextures(). Also they should be considered as "middleware" to get an image from disk into OpenGL, not a solution that is ideal for production/release. So far every single image I've thrown at them had to be flipped upside down, which implies a copy of the Texture. For production/release-ready texture loading you might want to do a simple custom format that just GL.GetTexImage() - from what the OpenTK loaders gave you - and save that out into your custom file format. This isn't very hard to do, and your application will load faster.

What the loaders do return is a boolean for success/failure, the TextureTarget of the loaded image (TextureTarget.Texture1D, -2D, -Cube) and the TextureHandle you can use with GL.BindTexture().

My questions regarding the loaders:

1) Currently the loaders will return a GL_TEXTURE_2D for images that are neither 1D nor cube maps, should GL_TEXTURE_RECTANGLE be considered as an option? (it's primarily interesting for sprite-based games: texture coordinates are not specified in [0f..1f] range and no mipmaps)

2) The GDI+ loader has a boolean parameter to automatically build mipmaps via Glu, should there be a parameter for automatic texture compression aswell? (I'd recommend using .dds if you want compression, because the automatic one only offers GL.Hint() as influence over what the compression result *could* be like)

3) Currently the loaders will catch all exceptions thrown inside their block, and return OpenGL's default Texture handle zero if things went wrong. Should this be configurable to a custom value? (The source engine does that with a black/pink checkerboard texture, if a texture is missing. OpenGL's default is just white)

4) How far should the loaders be wrapped? Just a single LoadTexture() function, which internally figures out the file extension and calls the appropriate loader? Or expose loading .dds and GDI+ images as 2 functions? Or offer all 3 options?

The LoadTexture function would internally look something like this:

bool Success = false;
            switch ( filename.Substring( filename.Length - 4, 4 ) ) // File Extension
            {
            case "exig":
            case "tiff":
            case ".tif":
            case ".bmp":
            case ".gif":
            case ".jpg":
            case ".png":
                Success = ImageGDI.LoadFromDisk( filename, FlipImage, BuildMipMaps, out TextureID, out TexDimension );
                break;
            case ".dds":
                Success = ImageDDS.LoadFromDisk( filename, FlipImage, out TextureID, out TexDimension );
                break;
            default:
                // unrecognized format
                break;
            }
if (Success) set filters/wrapping etc.

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Inertia's picture

1.) also: GDI problem due to unsupported format

I've had a couple of those already, especially with images that weren't 24/32 bpp. Actually I'd just forward whatever GDI+ throws for 1. because we cannot say any more than it's exception does.

2&3) Sounds good, regarding the too large image: Even my old FX5600 supports texture sizes up to 4096^2 (Radeon 9800: 2048^2 - both cards were state of the art early 2004) so it will be quite seldom that this exception appears. Afaik Unreal engine models use 1024^2 textures for the body and 512^2 textures for the head of a character - this is at least true for Unreal Tournament 2004, haven't taken a look at Unreal 3 yet. (just to give an idea of real-use numbers of something that isn't terrain rendering)

Edit:
4.) Any OpenGL error. Could be 'Out of memory' or so.

Inertia's picture

The 'alpha' version of the loaders can be found here.

Recent changes in the loader:

-works with OpenTK 0.9.1
-The number of parameters has been reduced to filename, bindhandle and bindtarget. Everything else is controlled through a static class.
-now throws exceptions rather than reporting success/failure through return values. The naming of the exceptions is just a temporary solution and will probably be renamed to fit the TextureReader concept.
-sets filters, wrapping, texenv to defaults that (hopefully) work on any OpenGL driver capable of texture mapping.

Things one should know:

-Before calling the loader, make sure there's no GL error! Failure to do so will cause the loader to throw exceptions, although loading went fine.
-No wrapmode for R is set.
-After the load function returns, the texture that was loaded is still bound. No other GL state is touched.
-Cubemap support is untested but should work according to spec. It is guaranteed to fail when:

  1. (width != height)
  2. width is not a power of 2
  3. the texture isn't compressed
  4. the cube map does not contain exactly 6 faces
  5. you should set wrapmode to ClampToEdge to avoid visible seams

Possible future improvements:

  • be more paranoid about GL errors leaking from operations previously called before loading textures.
  • Anisotropic filtering parameter
  • the 'skip mipmap levels larger than X' functionality mentioned in previous posts.
Inertia's picture

Cubemaps do work, however flipping them upside down isn't correct yet. Since ATi's CubeMapGen can output the faces already flipped for OpenGL's coordinate system (and I don't know any other tool to build .dds cubemaps), this won't be fixed before the loaders are commited to svn - to avoid having 10 different versions floating around.

Free Image Hosting at www.ImageShack.us

It's a Glu.Sphere, and uses these extremely complicated Shaders to determine cubemap lookup:

Vertex:
 
varying vec3 Normal;
 
void main()
{
  gl_Position = ftransform();
  Normal = gl_Normal;
}
 
Fragment:
 
uniform samplerCube Earth;
varying vec3 Normal;
 
void main()
{ 
  gl_FragColor = textureCube( Earth, Normal.xyz ); 
}
puklaus's picture

Im using dds loader from parallax mapping tutorial and one idea when using 2d is that you should return images width and height maybe in struct or put them static variables. btw opentk is great :)

Inertia's picture

Sorry about the delayed reply, kinda busy atm and had to give this some thought before replying.

Since Texture coordinates for the 1D, 2D, Cube targets are usually in the [0..1] range, the texture size does not really matter and OpenGL allows you to query the width|height and more through GL.GetTexParameter() and GL.GetTexLevelParameter(), so the information is not lost.

I think creating some kind of description object of the texture might be viable for the GL3 version of the loader (because the way I understand it so far, GL3 will abstract textures into 2 objects: 1xState and 1xData), but for GL2.1 there is no clear definition of what has to be included in such a description.

Another problem might be, that what you request is not necessarily what you get - and the description returned by the loader might not be accurate. e.g. when you create a 4-Bit Alpha texture, drivers might pack 2 Texels into 1 Byte, or they might use an 8-Bit Alpha instead of the requested 4-Bit. http://developer.nvidia.com/object/nv_ogl_texture_formats.html

george's picture

Is this loader available anywhere?
The rapidshare link doesn't seem to work anymore.

Inertia's picture

I've uploaded the parallax example again (contains the loader)

http://www.opentk.com/node/394

Not really optimal that files are deleted after 1 month, but my upload to this forum is already exceeded :p