pioneerspacesim/pioneer — pull request #1061 by robn (2012-03-11 11:28:23+00:00)
Title: Integrate textures into the renderer

There's a lot here. I tried to do it incrementally but there was too many interdependencies.

The main thing here is taking the existing `Texture` class and breaking it into three classes:

* `TextureDescriptor`, which describes everything needed to create a new texture - size, format, etc.
* `Texture`, a per-renderer object that will typically map directly to a single renderer-specific texture.
* `TextureBuilder`, a helper class to load images from disk (or SDL surfaces) and create appropriate descriptors and textures from it.

There is a lot of other changes required throughout the code to move to this model. Its all described below.

**Basic usage**

```c++
TextureDescriptor descriptor(format, dataSize, texSize, sampleMode, generateMipmaps);
Texture *texture = renderer->CreateTexture(descriptor);
texture->Update(data, dataSize, imageFormat, imageType);
```

or, via `TextureBuilder`

```c++
TextureBuilder builder(filename, sampleMode, generateMipmaps, potExtend, forceRGBA);
Texture *texture = builder.GetTexture(renderer);
```

Defaults are provided for many values. Its often possible to create a texture from a file with something as simple as:

```c++
Texture *texture = TextureBuilder(filename).GetTexture(renderer);
```

`TextureBuilder` also has some convenience constructors for common texture types used in Pioneer: `Model`, `Billboard` and `UI`, and so:

```c++
Texture *texture = TextureBuild::UI("data/icons/cpanel.png").GetTexture(renderer);
```

**Texture cache**

`Renderer` now provides a very simple texture cache. Its not magical; a created texture must be explicitly placed in the cache and must be explicitly requested. The cache key is a simple pair of strings. A pair has been used to allow one to denote the texture "type" (eg billboard or UI), while the other can hold some other string to uniquely id the texture, typically a filename.

Again, `TextureBuilder` provides a convenience method to easily pull textures from the cache:

```c++
Texture *texture = TextureBuilder(filename).GetOrCreateTexture(renderer, "billboard");
```

**Texture as an abstract type**

After a lot of experimenting I've opted to make `Texture` an abstract type, intended to be subclassed by a per-renderer texture class (eg `TextureGL`). This is in direct opposition to the `RenderInfo` approach used for meshes and surfaces.

It seems to me that the purpose of `Renderable` and `RenderInfo` is to augment a generic renderer object with renderer-specific functionality. I would argue that that is exactly what normal class inheritance is supposed to do too. I think I could have implemented `Texture` using a `TextureRenderInfo` and it would have worked fine, but it makes the code more difficult as its actual functionality then lives inside the renderer instead of the object itself, which makes the renderer larger and makes it less obvious where particular data and functions live. Having `TextureGL` as a separate class has also made it much easier to share it between `RendererLegacy` and `RendererGL2` and also let LMR borrow it for a while.

I'll consider `MeshRenderInfo` and `SurfaceRenderInfo` further and decide if they should also be separate classes, and if not, if `Texture` should change to a `RenderInfo` model for consistency.

**Texture types**

Data formats have been reduced to only `RGB/RGBA` with type `UNSIGNED_INT`. The two places that didn't use this were `Gui::Gradient` (`RGBA`+`FLOAT`) and `TextureFont` (`LUMINANCE_ALPHA`). `Gradient` didn't really need to use `FLOAT` (which my reading indicates is not a valid data type for `RGBA`, though I'm not convinced) and `LUMINANCE_ALPHA` was difficult to support in `TextureGL` (the image type for the initial texture creation was hard to guess) and is deprecated in OpenGL3 anyway. As far as I'm able to drivers will typically convert everything down a small handful of formats anyway, so its unlikely we're losing much here.

**TexturedQuad**

The old `Texture::DrawQuad` method has been replaced by `Gui::TexturedQuad`, a "drawable" that takes a texture and provides a draw method. It's under `Gui::` and not `Graphics::Drawables::` because it knows about the reverse-winding required to draw things for the GUI and so didn't seem generic enough to go there. I don't particularly like it - its bulky and slow on unoptimised builds (though it optimises pretty nicely). `TextureFont` doesn't use it simply because the method call overhead when unoptimised kills the frame rate. But, its useful functionality that needs to live somewhere, so this is it for now. Long term my hope is that the GUI will be flexible enough for "ad-hoc" image drawing, and this sort of function can be taken on by something like `Gui::Image`.

**Supporting changes**

A few other changes have been required to support this work. I believe them to be fairly uncontroversial.

* `TextureCache` is no longer required since the renderer now provides that functionality.
* The GUI now has a renderer, passed to it at initialisation held in `Gui::Screen` with all the other stuff. Necessary since `Image` and `Gradient` now draw textured things through the renderer. Required long-term anyway.
* GUI clipping has now been replaced with scissoring. Shaders do texture drawing in `RendererGL2`, but shaders don't honour the fixed-function clip planes. This resulted in "clipped" textures (most noticable in the commodity market screen) being displayed. Scissor regions however are used with shaders and are the correct technique for masking GUI regions, so I've removed the GUI clip methods and added scissor methods to the renderer and GUI instead.
* As noted before, `TextureFont` uses the renderer directly for drawing instead of using `TexturedQuad`. This is still a temporary measure. Ideally we would pack all the glyphs into a single texture and then build indexed surfaces for rendering. That's out of scope for this work.
* `TextureFont::RenderMarkup` now returns the "current" colour so it can be reapplied for the next text run (usually coming from `Gui::TextLayout`). This used to be held in the GL state, but that can't work any more. This method shouldn't be here - long term, `TextureFont` will be about text rendering, not manipulation. Layout and markup will be fully handled elsewhere.
* LMR continues to do its own drawing. The texture name only is held in the oplist, and are loaded or fetched from the cache the first time they're needed. This maintains the previous functionality, just implemented slightly differently.

**Fixes and cleanups**

Along the way I've fixed and cleaned a few things. All minor, but listed here anyway:

* Most hardcoded texture paths have been moved into constant strings.
* Facegen components are now composited onto a non-alpha surface. This was a bug before but we just got lucky with some internal SDL state which now appears to have been changed.
* `Color` now has `BLACK` and `WHITE` constants. I would expect that over the long-term we'd start to move more "common" colours here.