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:
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
Feb 17
14:48:16Re: Texture Loaders
posted by InertiaDear Inertia,
since you posted this topic 2 weeks ago nobody cared to reply to it, so I'll reply to you now:
1-4) Just do what you consider the right thing, and also do that for:
5) Should the texture loaders set some defaults filter/wrapmode, to make sure it will draw properly? Like Nearest and NearestMipmapNearest filters and Repeat wrapping?
P.S: next time don't dare to justify your decisions :P
Feb 29
16:24:33Re: Texture Loaders
posted by the Fiddler5) Yes, nearest filtering by default. It sucks to get a white texture and fumble around for 15 minutes before you remember you have to set this first.
4) What's the intended usage? 2 things probably: directly upload to GL and return a texture id, or create a System.Drawing.Bitmap. Probably Load() and LoadBitmap()?
3) Throw the correct exception and leave it up to the user. He can catch and use a checkerboard texture, retry or fail. Silently catching errors is bad imho (*why* did it fail? Malformed file? Missing resource? Didn't have the permission to access the file?)
(yes the permission thing actually happened to me yesterday, I wouldn't have figured without seeing the exception text)
2) This might have to do with (5) too. Maybe pass a few optional flags to Load(), like TextureHint.Compress | TextureHint.Mipmap?
1) a) Can we detect 1d textures from 2d texture in dds? (it is trivial through System.Drawing.Bitmap).
b) What about another hint, like TextureHint.ResizeToPowerOfTwo. I'd prefer the loader to leave size intact, unless the user requests otherwise, *or* the hardware doesn't support GL 2.0 NPOT textures (autoresize through GLU/SGI then).
My 2cc :)
Feb 29
17:03:00Re: Texture Loaders
posted by Inertia1.a) Actually the loader is ditching all mipmap levels smaller than 4x4, because smaller textures have undefined parts in the block. I've not supported 1D texture loading as .dds, because for the same reason: 3/4 of the pixels in the block are undefined, and you have compression artifacts. For 1D textures the memory needs are also the same for uncompressed textures vs. compressed, but you have no quality loss due to compression with a .bmp.
1.b) "automatic" resizing is one of the things i truely disliked about DevIL. That's why the loaders return you errors, so you can try load a different (maybe smaller, maybe 2^n) texture.
2&5) automatic mipmap creation is done through Glu (and implemented in the GDI+ loader). I'll take a look at how to reduce the number of parameters the functions require through enums or static variables :)
3) currently the full exception is printed with Trace.Writeline() so no information for debugging is lost. Forwarding the exceptions is a good idea, so programmers can take actions at runtime.
4) The only intent is to load an image from disk to OpenGL. If you want a Drawing.Bitmap, there's little point in using anything besides .Net's API? Cannot think of a scenario where it would be interesting to have the .dds converted to a Bitmap, because you'd lose the compression while still having it's artifacts.
Thank you very much, any feedback helps alot with the direction where this should be heading.
Feb 29
17:07:40Re: Texture Loaders
posted by the Fiddler4) Makes sense. Only one Load function then :)
One suggestion: provide a Load() overload the takes a System.Drawing.Image, to allow loading resources from memory.
1.b) Agreed. If we allow this, the user should have to explicitly request it.
Feb 29
17:26:57Re: Texture Loaders
posted by Inertia[System.Drawing.Image overload]
Sure, that's not a problem.
1.b) It's also extremely complicated to uncompress the DXT texture, resize and compress it again. There are a couple of compression algorythms for DXT to chose from, and either way the image quality will probably be very bad as the image is compressed twice in the end.
With a Drawing.Image overload you can manually resize the image to whatever you like, before sending it to OpenGL :)
Feb 29
18:06:58Re: Texture Loaders
posted by the Fiddler1.b) I suppose anyone who uses DXT textures, will have thought about possible POT issues anyway ;)
Feb 29
18:54:00Re: Texture Loaders
posted by Inertia6) something I had considered, but not felt the necessity to implement yet: Some restriction to limit the maximum texture taken from the .dds file. Let's assume you have a compressed DXT5 file with the following mipmap levels:
Currently the loader will read all those mipmaps, but not call teximage for 11, 12 and 13 - some .dds authoring tools have come to the same conclusion and don't even create those tiny mipmaps.
But it might be interesting to tell the loader that you want only mipmap 3-10, because the graphics card does not support textures larger than 1024^2 pixels. I don't think it should be the loader's responsibility to figure the max. texture size out and try to be smart, but instead it should offer the programmer some way to set a limit and ignore larger mipmaps.
Feb 29
19:08:30Re: Texture Loaders
posted by the FiddlerAn optional parameter like
int maxTextureSizewould be useful (e.g. for limiting memory usage on low-end machines).Figuring out max texture size is dead easy:
I cannot think of any usecase where not limiting the max mipmap would be useful though. What happens if one tries to upload bigger textures than the ones supported?
Feb 29
19:35:00Re: Texture Loaders
posted by InertiaThe problem isn't figuring out the max. texture size, but rather that the loader tries to be smart, screws up and the user may ponder what went wrong. If you try to load an .bmp image 1027^2 and the limit is set to 1024, the loader would not return anything atm because technically nothing went wrong.
I've tried loading a 800MB texture, GL.GetError returns "invalid enum"
This whole restriction thing might be best for version 2 of the loaders, a static int MaxAllowedTextureSize could be added later on, and the GDI+ loader will manually rescale the image to meet the requirements, while the DDS loader skips larger mipmaps. There's still the problem what to do if the .dds file has no mipmaps and is too large though.
Feb 29
19:51:04Re: Texture Loaders
posted by the FiddlerYeah, no rush to get it out right now. There's room for improvements later on.
We need to see what can go wrong.
We should throw the correct exception on each case. What about: ArgumentNullException, ArgumentException("Invalid filename or image corrupt"), for (1), GraphicsContextException("Need a current GraphicsContext") for (2) and ImageTooLargeException("Max allowed size: {0}. Image size: {1}") for (3)?
Feb 29
20:14:00Re: Texture Loaders
posted by Inertia1.) 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.
May 06
15:03:00Re: Texture Loaders
posted by InertiaThe '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:
Possible future improvements:
May 21
10:35:00Re: Texture Loaders
posted by InertiaCubemaps 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.
It's a Glu.Sphere, and uses these extremely complicated Shaders to determine cubemap lookup:
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 );
}
Jun 17
07:08:50Re: Texture Loaders
posted by puklausIm 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 :)
Jun 23
12:26:59Re: Texture Loaders
posted by InertiaSorry 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