Multitexturing support

- added CImage class for loading/saving images and a simple test for it
- added libpng library to build
- added Gfx::Texture struct
- updated the Gfx::CDevice interface to include new features
- implemented the new features in Gfx::CGLDevice
dev-ui
Piotr Dziwinski 2012-07-04 00:04:53 +02:00
parent d9c5a439d0
commit f95df35dc5
11 changed files with 982 additions and 66 deletions

View File

@ -8,6 +8,7 @@ project(colobot C CXX)
find_package(OpenGL REQUIRED)
find_package(SDL REQUIRED)
find_package(SDL_image REQUIRED)
find_package(PNG REQUIRED)
# TODO: check for SDL version. Should be >= 1.2.10

View File

@ -30,6 +30,7 @@ app/app.cpp
app/main.cpp
app/system.cpp
common/event.cpp
common/image.cpp
common/iman.cpp
# common/metafile.cpp
# common/misc.cpp
@ -150,10 +151,16 @@ set(LIBS
${SDL_LIBRARY}
${SDLIMAGE_LIBRARY}
${OPENGL_LIBRARY}
${PNG_LIBRARIES}
#CBot -- not yet WinAPI-independent
)
include_directories(. ${CMAKE_CURRENT_BINARY_DIR})
include_directories(. ${CMAKE_CURRENT_BINARY_DIR}
${SDL_INCLUDE_DIR}
${SDL_IMAGE_INCLUDE_DIR}
${SDLTTF_INCLUDE_DIR}
${PNG_INCLUDE_DIRS}
)
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/CBot)

221
src/common/image.cpp Normal file
View File

@ -0,0 +1,221 @@
// * This file is part of the COLOBOT source code
// * Copyright (C) 2012, Polish Portal of Colobot (PPC)
// *
// * This program is free software: you can redistribute it and/or modify
// * it under the terms of the GNU General Public License as published by
// * the Free Software Foundation, either version 3 of the License, or
// * (at your option) any later version.
// *
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// * GNU General Public License for more details.
// *
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
// image.cpp
#include "image.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <png.h>
/* <---------------------------------------------------------------> */
/* The following code is from savesurf program by Angelo "Encelo" Theodorou
Source: http://encelo.netsons.org/old/sdl/
The code was refactored and modified slightly to fit the needs.
The copyright information below is kept unchanged. */
/* SaveSurf: an example on how to save a SDLSurface in PNG
Copyright (C) 2006 Angelo "Encelo" Theodorou
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
NOTE:
This program is part of "Mars, Land of No Mercy" SDL examples,
you can find other examples on http://marsnomercy.org
*/
std::string PNG_ERROR = "";
void PNGUserError(png_structp ctx, png_const_charp str)
{
PNG_ERROR = std::string(str);
}
int PNGColortypeFromSurface(SDL_Surface *surface)
{
int colortype = PNG_COLOR_MASK_COLOR; /* grayscale not supported */
if (surface->format->palette)
colortype |= PNG_COLOR_MASK_PALETTE;
else if (surface->format->Amask)
colortype |= PNG_COLOR_MASK_ALPHA;
return colortype;
}
bool PNGSaveSurface(const char *filename, SDL_Surface *surf)
{
FILE *fp;
png_structp png_ptr;
png_infop info_ptr;
int i, colortype;
png_bytep *row_pointers;
PNG_ERROR = "";
/* Opening output file */
fp = fopen(filename, "wb");
if (fp == NULL)
{
PNG_ERROR = std::string("Could not open file '") + std::string(filename) + std::string("' for saving");
return false;
}
/* Initializing png structures and callbacks */
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, PNGUserError, NULL);
if (png_ptr == NULL)
return false;
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL)
{
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
PNG_ERROR = "png_create_info_struct() error!";
return false;
}
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_write_struct(&png_ptr, &info_ptr);
fclose(fp);
return false;
}
png_init_io(png_ptr, fp);
colortype = PNGColortypeFromSurface(surf);
png_set_IHDR(png_ptr, info_ptr, surf->w, surf->h, 8, colortype, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
/* Writing the image */
png_write_info(png_ptr, info_ptr);
png_set_packing(png_ptr);
row_pointers = (png_bytep*) malloc(sizeof(png_bytep)*surf->h);
for (i = 0; i < surf->h; i++)
row_pointers[i] = (png_bytep)(Uint8 *)surf->pixels + i*surf->pitch;
png_write_image(png_ptr, row_pointers);
png_write_end(png_ptr, info_ptr);
/* Cleaning out... */
free(row_pointers);
png_destroy_write_struct(&png_ptr, &info_ptr);
fclose(fp);
return true;
}
/* <---------------------------------------------------------------> */
CImage::CImage()
{
m_data = NULL;
}
CImage::~CImage()
{
Free();
}
bool CImage::IsEmpty()
{
return m_data == NULL;
}
void CImage::Free()
{
if (m_data != NULL)
{
if (m_data->surface != NULL)
{
SDL_FreeSurface(m_data->surface);
m_data->surface = NULL;
}
delete m_data;
m_data = NULL;
}
}
ImageData* CImage::GetData()
{
return m_data;
}
std::string CImage::GetError()
{
return m_error;
}
bool CImage::Load(const std::string& fileName)
{
if (! IsEmpty() )
Free();
m_data = new ImageData();
m_error = "";
m_data->surface = IMG_Load(fileName.c_str());
if (m_data->surface == NULL)
{
delete m_data;
m_data = NULL;
m_error = std::string(IMG_GetError());
return false;
}
return true;
}
bool CImage::SavePNG(const std::string& fileName)
{
if (IsEmpty())
{
m_error = "Empty image!";
return false;
}
m_error = "";
if (! PNGSaveSurface(fileName.c_str(), m_data->surface) )
{
m_error = PNG_ERROR;
return false;
}
return true;
}

84
src/common/image.h Normal file
View File

@ -0,0 +1,84 @@
// * This file is part of the COLOBOT source code
// * Copyright (C) 2012, Polish Portal of Colobot (PPC)
// *
// * This program is free software: you can redistribute it and/or modify
// * it under the terms of the GNU General Public License as published by
// * the Free Software Foundation, either version 3 of the License, or
// * (at your option) any later version.
// *
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// * GNU General Public License for more details.
// *
// * You should have received a copy of the GNU General Public License
// * along with this program. If not, see http://www.gnu.org/licenses/.
// image.h
#pragma once
#include <stddef.h>
#include <string>
// Forward declaration without including headers to clutter the code
struct SDL_Surface;
//! Implementation-specific image data
/** Note that the struct has no destructor and the surface
will not be freed at destruction. */
struct ImageData
{
//! SDL surface with image data
SDL_Surface* surface;
ImageData() { surface = NULL; }
};
/**
\class CImage
\brief Image loaded from file
Wrapper around SDL_Image library to load images. Also contains
function for saving images to PNG.
*/
class CImage
{
private:
//! Blocked!
CImage(const CImage &other) {}
//! Blocked!
void operator=(const CImage &other) {}
public:
//! Constructs empty image (with NULL data)
CImage();
//! Destroys image, calling Free()
virtual ~CImage();
//! Frees the allocated image data
void Free();
//! Returns whether the image is empty (has NULL data)
bool IsEmpty();
//! Returns the image data; if empty - returns NULL
ImageData* GetData();
//! Loads an image from the specified file
bool Load(const std::string &fileName);
//! Saves the image to the specified file in PNG format
bool SavePNG(const std::string &fileName);
//! Returns the last error
std::string GetError();
private:
//! Last encountered error
std::string m_error;
//! Image data
ImageData* m_data;
};

View File

@ -0,0 +1,6 @@
cmake_minimum_required(VERSION 2.8)
set(CMAKE_BUILD_TYPE debug)
set(CMAKE_CXX_FLAGS_DEBUG "-Wall -g -O0")
add_executable(image_test ../image.cpp image_test.cpp)

View File

@ -0,0 +1,34 @@
#include "../image.h"
#include <SDL/SDL.h>
#include <stdio.h>
/* For now, just a simple test: loading a file from image
* and saving it to another in PNG. */
int main(int argc, char *argv[])
{
if (argc != 3)
{
printf("Usage: %s in_image out_image\n", argv[0]);
return 0;
}
CImage image;
if (! image.Load(argv[1]))
{
std::string err = image.GetError();
printf("Error loading '%s': %s\n", err.c_str());
return 1;
}
if (! image.SavePNG(argv[2]))
{
std::string err = image.GetError();
printf("Error saving PNG '%s': %s\n", err.c_str());
return 2;
}
return 0;
}

View File

@ -31,3 +31,20 @@ void Gfx::DeviceConfig::LoadDefault()
doubleBuf = true;
noFrame = false;
}
void Gfx::TextureParams::LoadDefault()
{
minFilter = Gfx::TEX_MIN_FILTER_NEAREST;
magFilter = Gfx::TEX_MAG_FILTER_NEAREST;
wrapS = Gfx::TEX_WRAP_REPEAT;
wrapT = Gfx::TEX_WRAP_REPEAT;
colorOperation = Gfx::TEX_MIX_OPER_MODULATE;
colorArg1 = Gfx::TEX_MIX_ARG_CURRENT;
colorArg2 = Gfx::TEX_MIX_ARG_TEXTURE;
alphaOperation = Gfx::TEX_MIX_OPER_MODULATE;
alphaArg1 = Gfx::TEX_MIX_ARG_CURRENT;
alphaArg2 = Gfx::TEX_MIX_ARG_TEXTURE;
}

View File

@ -30,6 +30,9 @@
#include <string>
class CImage;
namespace Gfx {
/**
@ -144,6 +147,15 @@ enum CullMode
CULL_CCW
};
/**
\enum ShadeModel
\brief Shade model used in rendering */
enum ShadeModel
{
SHADE_FLAT,
SHADE_SMOOTH
};
/**
\enum FillMode
\brief Polygon fill mode */
@ -169,6 +181,147 @@ enum PrimitiveType
PRIMITIVE_TRIANGLE_STRIP
};
/**
\enum TexMinFilter
\brief Minification texture filter
Corresponds to OpenGL modes but should translate to DirectX too. */
enum TexMinFilter
{
TEX_MIN_FILTER_NEAREST,
TEX_MIN_FILTER_LINEAR,
TEX_MIN_FILTER_NEAREST_MIPMAP_NEAREST,
TEX_MIN_FILTER_LINEAR_MIPMAP_NEAREST,
TEX_MIN_FILTER_NEAREST_MIPMAP_LINEAR,
TEX_MIN_FILTER_LINEAR_MIPMAP_LINEAR
};
/**
\enum TexMagFilter
\brief Magnification texture filter */
enum TexMagFilter
{
TEX_MAG_FILTER_NEAREST,
TEX_MAG_FILTER_LINEAR
};
/**
\enum TexWrapMode
\brief Wrapping mode for texture coords */
enum TexWrapMode
{
TEX_WRAP_CLAMP,
TEX_WRAP_REPEAT
};
/**
\enum TexMixOperation
\brief Multitexture mixing operation
*/
enum TexMixOperation
{
TEX_MIX_OPER_MODULATE,
TEX_MIX_OPER_ADD
};
/**
\enum TexMixArgument
\brief Multitexture mixing argument
*/
enum TexMixArgument
{
TEX_MIX_ARG_CURRENT,
TEX_MIX_ARG_TEXTURE,
TEX_MIX_ARG_DIFFUSE,
TEX_MIX_ARG_FACTOR
};
/**
\enum TextureParams
\brief Parameters for texture creation
*/
struct TextureParams
{
//! Minification filter
Gfx::TexMinFilter minFilter;
//! Magnification filter
Gfx::TexMagFilter magFilter;
//! Wrap S coord mode
Gfx::TexWrapMode wrapS;
//! Wrap T coord mode
Gfx::TexWrapMode wrapT;
//! Mixing operation done on color values
Gfx::TexMixOperation colorOperation;
//! 1st argument of color operations
Gfx::TexMixArgument colorArg1;
//! 2nd argument of color operations
Gfx::TexMixArgument colorArg2;
//! Mixing operation done on alpha values
Gfx::TexMixOperation alphaOperation;
//! 1st argument of alpha operations
Gfx::TexMixArgument alphaArg1;
//! 2nd argument of alpha operations
Gfx::TexMixArgument alphaArg2;
//! Constructor; calls LoadDefault()
TextureParams()
{ LoadDefault(); }
//! Loads the default values
void LoadDefault();
};
/*
Notes for rewriting DirectX code:
>> SetRenderState() translates to many functions depending on param
D3DRENDERSTATE_ALPHABLENDENABLE -> SetRenderState() with RENDER_STATE_BLENDING
D3DRENDERSTATE_ALPHAFUNC -> SetAlphaTestFunc() func
D3DRENDERSTATE_ALPHAREF -> SetAlphaTestFunc() ref
D3DRENDERSTATE_ALPHATESTENABLE -> SetRenderState() with RENDER_STATE_ALPHA_TEST
D3DRENDERSTATE_AMBIENT -> SetGlobalAmbient()
D3DRENDERSTATE_CULLMODE -> SetCullMode()
D3DRENDERSTATE_DESTBLEND -> SetBlendFunc() dest blending func
D3DRENDERSTATE_DITHERENABLE -> SetRenderState() with RENDER_STATE_DITHERING
D3DRENDERSTATE_FILLMODE -> SetFillMode()
D3DRENDERSTATE_FOGCOLOR -> SetFogParams()
D3DRENDERSTATE_FOGENABLE -> SetRenderState() with RENDER_STATE_FOG
D3DRENDERSTATE_FOGEND -> SetFogParams()
D3DRENDERSTATE_FOGSTART -> SetFogParams()
D3DRENDERSTATE_FOGVERTEXMODE -> SetFogParams() fog model
D3DRENDERSTATE_LIGHTING -> SetRenderState() with RENDER_STATE_LIGHTING
D3DRENDERSTATE_SHADEMODE -> SetShadeModel()
D3DRENDERSTATE_SPECULARENABLE -> doesn't matter (always enabled)
D3DRENDERSTATE_SRCBLEND -> SetBlendFunc() src blending func
D3DRENDERSTATE_TEXTUREFACTOR -> SetTextureFactor()
D3DRENDERSTATE_ZBIAS -> SetDepthBias()
D3DRENDERSTATE_ZENABLE -> SetRenderState() with RENDER_STATE_DEPTH_TEST
D3DRENDERSTATE_ZFUNC -> SetDepthTestFunc()
D3DRENDERSTATE_ZWRITEENABLE -> SetRenderState() with RENDER_STATE_DEPTH_WRITE
>> SetTextureStageState() translates to SetTextureParams()
Params from enum in struct TextureParams
D3DTSS_ADDRESS -> Gfx::TexWrapMode wrapS, wrapT
D3DTSS_ALPHAARG1 -> Gfx::TexMixArgument alphaArg1
D3DTSS_ALPHAARG2 -> Gfx::TexMixArgument alphaArg2
D3DTSS_ALPHAOP -> Gfx::TexMixOperation alphaOperation
D3DTSS_COLORARG1 -> Gfx::TexMixArgument colorArg1
D3DTSS_COLORARG2 -> Gfx::TexMixArgument colorArg2
D3DTSS_COLOROP -> Gfx::TexMixOperation colorOperation
D3DTSS_MAGFILTER -> Gfx::TexMagFilter magFilter
D3DTSS_MINFILTER -> Gfx::TexMinFilter minFilter
D3DTSS_TEXCOORDINDEX -> doesn't matter (texture coords are set explicitly by glMultiTexCoordARB*)
Note that D3DTSS_ALPHAOP or D3DTSS_COLOROP set to D3DTOP_DISABLE must translate to disabling the whole texture stage.
In DirectX, you shouldn't mix enabling one and disabling the other.
Also, if previous stage is disabled in DirectX, the later ones are disabled, too. In OpenGL, that is not the case.
*/
/**
\class CDevice
\brief Abstract interface of graphics device
@ -226,20 +379,33 @@ public:
//! Returns the current enable state of light at given index
virtual bool GetLightEnabled(int index) = 0;
// TODO:
// virtual Gfx::Texture* CreateTexture(CImage *image) = 0;
// virtual void DestroyTexture(Gfx::Texture *texture) = 0;
//! Creates a texture from image; the image can be safely removed after that
virtual Gfx::Texture* CreateTexture(CImage *image, bool alpha, bool mipMap) = 0;
//! Deletes a given texture, freeing it from video memory
virtual void DestroyTexture(Gfx::Texture *texture) = 0;
//! Deletes all textures created so far
virtual void DestroyAllTextures() = 0;
//! Returns the maximum number of multitexture units
//! Returns the maximum number of multitexture stages
virtual int GetMaxTextureCount() = 0;
//! Sets the (multi)texture at given index
virtual void SetTexture(int index, Gfx::Texture *texture) = 0;
//! Returns the (multi)texture at given index
virtual Gfx::Texture* GetTexture(int index) = 0;
//! Enables/disables the given texture stage
virtual void SetTextureEnabled(int index, bool enabled) = 0;
//! Returns the current enable state of given texture stage
virtual bool GetTextureEnabled(int index) = 0;
// TODO:
// virtual void GetTextureStageState() = 0;
// virtual void SetTextureStageState() = 0;
//! Sets the current params of texture with given index
virtual void SetTextureParams(int index, const Gfx::TextureParams &params) = 0;
//! Returns the current params of texture with given index
virtual Gfx::TextureParams GetTextureParams(int index) = 0;
//! Sets the texture factor to the given color value
virtual void SetTextureFactor(Gfx::Color &color) = 0;
//! Returns the current texture factor
virtual Gfx::Color GetTextureFactor() = 0;
//! Renders primitive composed of vertices with single texture
virtual void DrawPrimitive(Gfx::PrimitiveType type, Gfx::Vertex *vertices, int vertexCount) = 0;
@ -297,6 +463,11 @@ public:
//! Returns the current cull mode
virtual Gfx::CullMode GetCullMode() = 0;
//! Sets the shade model
virtual void SetShadeModel(Gfx::ShadeModel model) = 0;
//! Returns the current shade model
virtual Gfx::ShadeModel GetShadeModel() = 0;
//! Sets the current fill mode
virtual void SetFillMode(Gfx::FillMode mode) = 0;
//! Returns the current fill mode

View File

@ -20,9 +20,16 @@
namespace Gfx {
/** \struct Texture*/
struct Texture
{
// TODO
//! Whether the texture was loaded
bool valid;
//! Id of the texture in graphics engine
unsigned int id;
Texture()
{ valid = false; id = 0; }
};
}; // namespace Gfx

View File

@ -16,6 +16,7 @@
// gldevice.cpp
#include "common/image.h"
#include "graphics/opengl/gldevice.h"
#include <GL/gl.h>
@ -30,21 +31,13 @@ namespace Gfx {
struct GLDevicePrivate
{
void (APIENTRY* glMultiTexCoord1fARB)(GLenum target, GLfloat s);
void (APIENTRY* glMultiTexCoord2fARB)(GLenum target, GLfloat s, GLfloat t);
void (APIENTRY* glMultiTexCoord3fARB)(GLenum target, GLfloat s, GLfloat t, GLfloat r);
void (APIENTRY* glMultiTexCoord4fARB)(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q);
void (APIENTRY* glActiveTextureARB)(GLenum texture);
void (APIENTRY* glClientActiveTextureARB)(GLenum texture);
GLDevicePrivate()
{
glMultiTexCoord1fARB = NULL;
glMultiTexCoord2fARB = NULL;
glMultiTexCoord3fARB = NULL;
glMultiTexCoord4fARB = NULL;
glActiveTextureARB = NULL;
glClientActiveTextureARB = NULL;
}
};
@ -71,6 +64,7 @@ Gfx::CGLDevice::CGLDevice()
{
m_private = new Gfx::GLDevicePrivate();
m_wasInit = false;
m_texturing = false;
}
@ -92,18 +86,10 @@ std::string Gfx::CGLDevice::GetError()
bool Gfx::CGLDevice::Create()
{
m_wasInit = true;
// TODO: move to functions?
glShadeModel(GL_SMOOTH);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
/* First check for extensions
These should be available in standard OpenGL 1.3
But every distribution is different
So we're loading them dynamically through SDL_GL_GetProcAddress() */
std::string extensions = std::string( (char*) glGetString(GL_EXTENSIONS));
@ -119,37 +105,58 @@ bool Gfx::CGLDevice::Create()
return false;
}
m_private->glMultiTexCoord2fARB = (PFNGLMULTITEXCOORD2FARBPROC) SDL_GL_GetProcAddress("glMultiTexCoord2fARB");
m_private->glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC) SDL_GL_GetProcAddress("glActiveTextureARB");
if ((m_private->glMultiTexCoord2fARB == NULL) || (m_private->glActiveTextureARB == NULL))
{
m_error = "Could not load extension functions, even though they seem supported";
return false;
}
m_wasInit = true;
// This is mostly done in all modern hardware by default
// DirectX doesn't even allow the option to turn off perspective correction anymore
// So turn it on permanently
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
// Set just to be sure
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
m_lights = std::vector<Gfx::Light>(GL_MAX_LIGHTS, Gfx::Light());
m_lightsEnabled = std::vector<bool> (GL_MAX_LIGHTS, false);
int maxTextures = 0;
glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &maxTextures);
m_textures = std::vector<Gfx::Texture*>(maxTextures, NULL);
m_lights = std::vector<Gfx::Light>(GL_MAX_LIGHTS, Gfx::Light());
m_lightsEnabled = std::vector<bool>(GL_MAX_LIGHTS, false);
m_private->glMultiTexCoord1fARB = (PFNGLMULTITEXCOORD1FARBPROC) SDL_GL_GetProcAddress("glMultiTexCoord1fARB");
m_private->glMultiTexCoord2fARB = (PFNGLMULTITEXCOORD2FARBPROC) SDL_GL_GetProcAddress("glMultiTexCoord2fARB");
m_private->glMultiTexCoord3fARB = (PFNGLMULTITEXCOORD3FARBPROC) SDL_GL_GetProcAddress("glMultiTexCoord3fARB");
m_private->glMultiTexCoord4fARB = (PFNGLMULTITEXCOORD4FARBPROC) SDL_GL_GetProcAddress("glMultiTexCoord4fARB");
m_private->glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC) SDL_GL_GetProcAddress("glActiveTextureARB");
m_private->glClientActiveTextureARB = (PFNGLCLIENTACTIVETEXTUREARBPROC) SDL_GL_GetProcAddress("glClientActiveTextureARB");
m_textures = std::vector<Gfx::Texture*> (maxTextures, NULL);
m_texturesEnabled = std::vector<bool> (maxTextures, false);
m_texturesParams = std::vector<Gfx::TextureParams>(maxTextures, Gfx::TextureParams());
return true;
}
void Gfx::CGLDevice::Destroy()
{
m_private->glMultiTexCoord1fARB = NULL;
m_private->glMultiTexCoord2fARB = NULL;
m_private->glMultiTexCoord3fARB = NULL;
m_private->glMultiTexCoord4fARB = NULL;
m_private->glActiveTextureARB = NULL;
m_private->glClientActiveTextureARB = NULL;
// Delete the remaining textures
std::set<Gfx::Texture*>::iterator it;
for (it = m_allTextures.begin(); it != m_allTextures.end(); ++it)
delete *it;
m_allTextures.clear();
// Should not be strictly necessary, but just in case
DestroyAllTextures();
m_lights.clear();
m_lightsEnabled.clear();
m_textures.clear();
m_texturesEnabled.clear();
m_texturesParams.clear();
m_wasInit = false;
}
@ -177,7 +184,7 @@ void Gfx::CGLDevice::Clear()
void Gfx::CGLDevice::SetTransform(Gfx::TransformType type, const Math::Matrix &matrix)
{
if (type == Gfx::TRANSFORM_WORLD)
if (type == Gfx::TRANSFORM_WORLD)
{
m_worldMat = matrix;
m_modelviewMat = Math::MultiplyMatrices(m_worldMat, m_viewMat);
@ -205,7 +212,7 @@ void Gfx::CGLDevice::SetTransform(Gfx::TransformType type, const Math::Matrix &m
const Math::Matrix& Gfx::CGLDevice::GetTransform(Gfx::TransformType type)
{
if (type == Gfx::TRANSFORM_WORLD)
if (type == Gfx::TRANSFORM_WORLD)
return m_worldMat;
else if (type == Gfx::TRANSFORM_VIEW)
return m_viewMat;
@ -219,7 +226,7 @@ const Math::Matrix& Gfx::CGLDevice::GetTransform(Gfx::TransformType type)
void Gfx::CGLDevice::MultiplyTransform(Gfx::TransformType type, const Math::Matrix &matrix)
{
if (type == Gfx::TRANSFORM_WORLD)
if (type == Gfx::TRANSFORM_WORLD)
{
m_worldMat = Math::MultiplyMatrices(m_worldMat, matrix);
m_modelviewMat = Math::MultiplyMatrices(m_worldMat, m_viewMat);
@ -306,7 +313,7 @@ const Gfx::Light& Gfx::CGLDevice::GetLight(int index)
void Gfx::CGLDevice::SetLightEnabled(int index, bool enabled)
{
assert(index >= 0);
assert(index < (int)m_lightsEnabled.size());
assert(index < (int)m_lights.size());
m_lightsEnabled[index] = enabled;
@ -321,11 +328,102 @@ bool Gfx::CGLDevice::GetLightEnabled(int index)
return m_lightsEnabled[index];
}
Gfx::Texture* Gfx::CGLDevice::CreateTexture(CImage *image, bool alpha, bool mipMap)
{
Gfx::Texture *result = new Gfx::Texture();
// Texturing must be enabled, so enable 1st texture stage
m_private->glActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &result->id);
glBindTexture(GL_TEXTURE_2D, result->id);
GLenum sourceFormat = 0;
if (alpha)
sourceFormat = GL_RGBA;
else
sourceFormat = GL_RGB;
ImageData *data = image->GetData();
if (data == NULL)
return NULL;
if (mipMap)
{
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, data->surface->w,
data->surface->h, sourceFormat, GL_UNSIGNED_BYTE,
data->surface->pixels);
}
else
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, data->surface->w, data->surface->h,
0, sourceFormat, GL_UNSIGNED_BYTE, data->surface->pixels);
}
// Restore previous setup of 1st texture stage
RestoreTextureStage(0);
return result;
}
void Gfx::CGLDevice::DestroyTexture(Gfx::Texture *texture)
{
std::set<Gfx::Texture*>::iterator it = m_allTextures.find(texture);
if (it != m_allTextures.end())
m_allTextures.erase(it);
glDeleteTextures(1, &texture->id);
}
void Gfx::CGLDevice::DestroyAllTextures()
{
std::set<Gfx::Texture*> allCopy = m_allTextures;
std::set<Gfx::Texture*>::iterator it;
for (it = allCopy.begin(); it != allCopy.end(); ++it)
{
DestroyTexture(*it);
delete *it;
}
}
int Gfx::CGLDevice::GetMaxTextureCount()
{
return m_textures.size();
}
/**
If \a texture is \c NULL or invalid, unbinds the given texture.
If valid, binds the texture and enables the given texture stage.
The setting is remembered, even if texturing is disabled at the moment. */
void Gfx::CGLDevice::SetTexture(int index, Gfx::Texture *texture)
{
assert(index >= 0);
assert(index < (int)m_textures.size());
// Enable the given texture stage
m_private->glActiveTextureARB(GL_TEXTURE0_ARB + index);
glEnable(GL_TEXTURE_2D);
if ((texture == NULL) || (! texture->valid))
{
glBindTexture(GL_TEXTURE_2D, 0); // unbind texture
m_textures[index] = NULL; // remember the changes
}
else
{
glBindTexture(GL_TEXTURE_2D, texture->id); // bind the texture
m_textures[index] = texture; // remember the changes
SetTextureParams(index, m_texturesParams[index]); // texture params need to be re-set for the new texture
}
// Disable the stage if it is set so
if ( (! m_texturing) || (! m_texturesEnabled[index]) )
glDisable(GL_TEXTURE_2D);
}
/**
Returns the previously assigned texture or \c NULL if the given stage is not enabled. */
Gfx::Texture* Gfx::CGLDevice::GetTexture(int index)
{
assert(index >= 0);
@ -334,14 +432,224 @@ Gfx::Texture* Gfx::CGLDevice::GetTexture(int index)
return m_textures[index];
}
void Gfx::CGLDevice::SetTexture(int index, Gfx::Texture *texture)
void Gfx::CGLDevice::SetTextureEnabled(int index, bool enabled)
{
assert(index >= 0);
assert(index < (int)m_textures.size());
m_textures[index] = texture;
m_texturesEnabled[index] = enabled;
// TODO
m_private->glActiveTextureARB(GL_TEXTURE0_ARB + index);
if (enabled)
glEnable(GL_TEXTURE_2D);
else
glDisable(GL_TEXTURE_2D);
}
bool Gfx::CGLDevice::GetTextureEnabled(int index)
{
assert(index >= 0);
assert(index < (int)m_textures.size());
return m_texturesEnabled[index];
}
/**
Sets the texture parameters for the given texture stage.
If the given texture was not set (bound) yet, nothing happens.
The settings are remembered, even if texturing is disabled at the moment. */
void Gfx::CGLDevice::SetTextureParams(int index, const Gfx::TextureParams &params)
{
assert(index >= 0);
assert(index < (int)m_textures.size());
// Remember the settings
m_texturesParams[index] = params;
// Enable the given stage
m_private->glActiveTextureARB(GL_TEXTURE0_ARB + index);
glEnable(GL_TEXTURE_2D);
GLint minF = 0;
if (params.minFilter == Gfx::TEX_MIN_FILTER_NEAREST) minF = GL_NEAREST;
else if (params.minFilter == Gfx::TEX_MIN_FILTER_LINEAR) minF = GL_LINEAR;
else if (params.minFilter == Gfx::TEX_MIN_FILTER_NEAREST_MIPMAP_NEAREST) minF = GL_NEAREST_MIPMAP_NEAREST;
else if (params.minFilter == Gfx::TEX_MIN_FILTER_LINEAR_MIPMAP_NEAREST) minF = GL_LINEAR_MIPMAP_NEAREST;
else if (params.minFilter == Gfx::TEX_MIN_FILTER_NEAREST_MIPMAP_LINEAR) minF = GL_NEAREST_MIPMAP_LINEAR;
else if (params.minFilter == Gfx::TEX_MIN_FILTER_LINEAR_MIPMAP_LINEAR) minF = GL_LINEAR_MIPMAP_LINEAR;
else assert(false);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minF);
GLint magF = 0;
if (params.magFilter == Gfx::TEX_MAG_FILTER_NEAREST) magF = GL_NEAREST;
else if (params.magFilter == Gfx::TEX_MAG_FILTER_LINEAR) magF = GL_LINEAR;
else assert(false);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magF);
if (params.wrapS == Gfx::TEX_WRAP_CLAMP)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
else if (params.wrapS == Gfx::TEX_WRAP_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
else assert(false);
if (params.wrapT == Gfx::TEX_WRAP_CLAMP)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
else if (params.wrapT == Gfx::TEX_WRAP_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
else assert(false);
// Selection of operation and arguments
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
// Color operation
if (params.colorOperation == Gfx::TEX_MIX_OPER_MODULATE)
glTexEnvi(GL_TEXTURE_2D, GL_COMBINE_RGB, GL_MODULATE);
else if (params.colorOperation == Gfx::TEX_MIX_OPER_ADD)
glTexEnvi(GL_TEXTURE_2D, GL_COMBINE_RGB, GL_ADD);
else assert(false);
// Color arg1
if (params.colorArg1 == Gfx::TEX_MIX_ARG_CURRENT)
{
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS); // that's right - stupid D3D enum values
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
}
else if (params.colorArg1 == Gfx::TEX_MIX_ARG_TEXTURE)
{
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
}
else if (params.colorArg1 == Gfx::TEX_MIX_ARG_DIFFUSE)
{
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PRIMARY_COLOR); // here as well
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
}
else assert(false);
// Color arg2
if (params.colorArg2 == Gfx::TEX_MIX_ARG_CURRENT)
{
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
}
else if (params.colorArg2 == Gfx::TEX_MIX_ARG_TEXTURE)
{
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
}
else if (params.colorArg2 == Gfx::TEX_MIX_ARG_DIFFUSE)
{
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
}
else assert(false);
// Alpha operation
if (params.alphaOperation == Gfx::TEX_MIX_OPER_MODULATE)
glTexEnvi(GL_TEXTURE_2D, GL_COMBINE_ALPHA, GL_MODULATE);
else if (params.alphaOperation == Gfx::TEX_MIX_OPER_ADD)
glTexEnvi(GL_TEXTURE_2D, GL_COMBINE_ALPHA, GL_ADD);
else assert(false);
// Alpha arg1
if (params.alphaArg1 == Gfx::TEX_MIX_ARG_CURRENT)
{
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
}
else if (params.alphaArg1 == Gfx::TEX_MIX_ARG_TEXTURE)
{
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
}
else if (params.alphaArg1 == Gfx::TEX_MIX_ARG_DIFFUSE)
{
glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
}
else assert(false);
// Alpha arg2
if (params.alphaArg2 == Gfx::TEX_MIX_ARG_CURRENT)
{
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_PREVIOUS);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
}
else if (params.alphaArg2 == Gfx::TEX_MIX_ARG_TEXTURE)
{
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
}
else if (params.alphaArg2 == Gfx::TEX_MIX_ARG_DIFFUSE)
{
glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_PRIMARY_COLOR);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
}
else assert(false);
// Disable the stage if it is set so
if ( (! m_texturing) || (! m_texturesEnabled[index]) )
glDisable(GL_TEXTURE_2D);
}
Gfx::TextureParams Gfx::CGLDevice::GetTextureParams(int index)
{
assert(index >= 0);
assert(index < (int)m_textures.size());
return m_texturesParams[index];
}
void Gfx::CGLDevice::SetTextureFactor(Gfx::Color &color)
{
// Needs to be set for all texture stages
for (int index = 0; index < (int)m_textures.size(); ++index)
{
// Activate stage
m_private->glActiveTextureARB(GL_TEXTURE0_ARB + index);
glEnable(GL_TEXTURE_2D);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color.Array());
// Disable the stage if it is set so
if ( (! m_texturing) || (! m_texturesEnabled[index]) )
glDisable(GL_TEXTURE_2D);
}
}
Gfx::Color Gfx::CGLDevice::GetTextureFactor()
{
// Get from 1st stage (should be the same for all stages)
m_private->glActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
GLfloat color[4] = { 0.0f };
glGetTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
// Disable the 1st stage if it is set so
if ( (! m_texturing) || (! m_texturesEnabled[0]) )
glDisable(GL_TEXTURE_2D);
return Gfx::Color(color[0], color[1], color[2], color[3]);
}
void Gfx::CGLDevice::RestoreTextureStage(int index)
{
// Ensure that we're working with the right stage
m_private->glActiveTextureARB(GL_TEXTURE0_ARB + index);
glEnable(GL_TEXTURE_2D);
if (m_textures[index] != NULL)
glBindTexture(GL_TEXTURE_2D, m_textures[index]->id); // bind to the previous texture
else
glBindTexture(GL_TEXTURE_2D, 0); // unbind
// Disable the stage if it is set so
if ( (! m_texturing) || (! m_texturesEnabled[index]) )
glDisable(GL_TEXTURE_2D);
}
void Gfx::CGLDevice::DrawPrimitive(Gfx::PrimitiveType type, Vertex *vertices, int vertexCount)
@ -374,7 +682,7 @@ void Gfx::CGLDevice::DrawPrimitive(Gfx::PrimitiveType type, Gfx::VertexCol *vert
for (int i = 0; i < vertexCount; ++i)
{
// TODO: specular?
// TODO: specular through EXT_separate_specular_color?
glColor4fv((GLfloat*)vertices[i].color.Array());
glTexCoord2fv((GLfloat*)vertices[i].texCoord.Array());
glVertex3fv((GLfloat*)vertices[i].coord.Array());
@ -395,8 +703,8 @@ void Gfx::CGLDevice::DrawPrimitive(Gfx::PrimitiveType type, VertexTex2 *vertices
for (int i = 0; i < vertexCount; ++i)
{
glNormal3fv((GLfloat*) vertices[i].normal.Array());
// TODO glMultiTexCoord2fARB(GL_TEXTURE0_ARB, vertices[i].texCoord.x, vertices[i].texCoord.y);
// TODO glMultiTexCoord2fARB(GL_TEXTURE1_ARB, vertices[i].texCoord2.x, vertices[i].texCoord2.y);
m_private->glMultiTexCoord2fARB(GL_TEXTURE0_ARB, vertices[i].texCoord.x, vertices[i].texCoord.y);
m_private->glMultiTexCoord2fARB(GL_TEXTURE1_ARB, vertices[i].texCoord2.x, vertices[i].texCoord2.y);
glVertex3fv((GLfloat*) vertices[i].coord.Array());
}
@ -412,14 +720,26 @@ void Gfx::CGLDevice::SetRenderState(Gfx::RenderState state, bool enabled)
}
else if (state == RENDER_STATE_TEXTURING)
{
m_texturing = enabled;
if (enabled)
{
glEnable(GL_TEXTURE_2D);
// TODO multitexture
// All enabled multitexture stages have to be enabled
for (int index = 0; index < (int)m_textures.size(); ++index)
{
m_private->glActiveTextureARB(GL_TEXTURE0_ARB + index);
if (m_texturesEnabled[index])
glEnable(GL_TEXTURE_2D);
}
}
else
{
glDisable(GL_TEXTURE_2D);
// All multitexture stages have to be disabled
for (int index = 0; index < (int)m_textures.size(); ++index)
{
m_private->glActiveTextureARB(GL_TEXTURE0_ARB + index);
glDisable(GL_TEXTURE_2D);
}
}
return;
}
@ -445,12 +765,14 @@ void Gfx::CGLDevice::SetRenderState(Gfx::RenderState state, bool enabled)
bool Gfx::CGLDevice::GetRenderState(Gfx::RenderState state)
{
if (state == RENDER_STATE_TEXTURING)
return m_texturing;
GLenum flag = 0;
switch (state)
{
case Gfx::RENDER_STATE_DEPTH_WRITE: flag = GL_DEPTH_WRITEMASK; break;
case Gfx::RENDER_STATE_TEXTURING: flag = GL_TEXTURE_2D; break;
case Gfx::RENDER_STATE_LIGHTING: flag = GL_DEPTH_WRITEMASK; break;
case Gfx::RENDER_STATE_BLENDING: flag = GL_BLEND; break;
case Gfx::RENDER_STATE_FOG: flag = GL_FOG; break;
@ -602,7 +924,7 @@ void Gfx::CGLDevice::SetClearColor(Gfx::Color color)
Gfx::Color Gfx::CGLDevice::GetClearColor()
{
float color[4] = { 0.0f };
GLfloat color[4] = { 0.0f };
glGetFloatv(GL_COLOR_CLEAR_VALUE, color);
return Gfx::Color(color[0], color[1], color[2], color[3]);
}
@ -614,7 +936,7 @@ void Gfx::CGLDevice::SetGlobalAmbient(Gfx::Color color)
Gfx::Color Gfx::CGLDevice::GetGlobalAmbient()
{
float color[4] = { 0.0f };
GLfloat color[4] = { 0.0f };
glGetFloatv(GL_LIGHT_MODEL_AMBIENT, color);
return Gfx::Color(color[0], color[1], color[2], color[3]);
}
@ -662,6 +984,23 @@ Gfx::CullMode Gfx::CGLDevice::GetCullMode()
return Gfx::CULL_CW;
}
void Gfx::CGLDevice::SetShadeModel(Gfx::ShadeModel model)
{
if (model == Gfx::SHADE_FLAT) glShadeModel(GL_FLAT);
else if (model == Gfx::SHADE_SMOOTH) glShadeModel(GL_SMOOTH);
else assert(false);
}
Gfx::ShadeModel Gfx::CGLDevice::GetShadeModel()
{
GLenum flag = 0;
glGetIntegerv(GL_SHADE_MODEL, (GLint*)&flag);
if (flag == GL_FLAT) return Gfx::SHADE_FLAT;
else if (flag == GL_SMOOTH) return Gfx::SHADE_SMOOTH;
else assert(false);
return Gfx::SHADE_FLAT;
}
void Gfx::CGLDevice::SetFillMode(Gfx::FillMode mode)
{
if (mode == Gfx::FILL_POINT) glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
@ -677,6 +1016,6 @@ Gfx::FillMode Gfx::CGLDevice::GetFillMode()
if (flag == GL_POINT) return Gfx::FILL_POINT;
else if (flag == GL_LINE) return Gfx::FILL_LINES;
else if (flag == GL_FILL) return Gfx::FILL_FILL;
else assert(false);
else assert(false);
return Gfx::FILL_POINT;
}

View File

@ -97,9 +97,21 @@ public:
virtual void SetLightEnabled(int index, bool enabled);
virtual bool GetLightEnabled(int index);
virtual Gfx::Texture* CreateTexture(CImage *image, bool alpha, bool mipMap);
virtual void DestroyTexture(Gfx::Texture *texture);
virtual void DestroyAllTextures();
virtual int GetMaxTextureCount();
virtual void SetTexture(int index, Gfx::Texture *texture);
virtual void SetTexture(int index, Gfx::Texture *texture);
virtual Gfx::Texture* GetTexture(int index);
virtual void SetTextureEnabled(int index, bool enabled);
virtual bool GetTextureEnabled(int index);
virtual void SetTextureParams(int index, const Gfx::TextureParams &params);
virtual Gfx::TextureParams GetTextureParams(int index);
virtual void SetTextureFactor(Gfx::Color &color);
virtual Gfx::Color GetTextureFactor();
virtual void DrawPrimitive(Gfx::PrimitiveType type, Vertex *vertices, int vertexCount);
virtual void DrawPrimitive(Gfx::PrimitiveType type, Gfx::VertexCol *vertices, int vertexCount);
@ -133,6 +145,9 @@ public:
virtual void SetCullMode(Gfx::CullMode mode);
virtual Gfx::CullMode GetCullMode();
virtual void SetShadeModel(Gfx::ShadeModel model);
virtual Gfx::ShadeModel GetShadeModel();
virtual void SetFillMode(Gfx::FillMode mode) ;
virtual Gfx::FillMode GetFillMode();
@ -143,6 +158,7 @@ private:
bool m_wasInit;
//! Last encountered error
std::string m_error;
//! Current world matrix
Math::Matrix m_worldMat;
//! Current view matrix
@ -151,16 +167,29 @@ private:
Math::Matrix m_modelviewMat;
//! Current projection matrix
Math::Matrix m_projectionMat;
//! The current material
Gfx::Material m_material;
//! Current lights
std::vector<Gfx::Light> m_lights;
//! Current lights enable status
std::vector<bool> m_lightsEnabled;
//! Current textures
//! Whether texturing is enabled in general
bool m_texturing;
//! Current textures; \c NULL value means unassigned
std::vector<Gfx::Texture*> m_textures;
//! Current texture stages enable status
std::vector<bool> m_texturesEnabled;
//! Current texture params
std::vector<Gfx::TextureParams> m_texturesParams;
//! Set of all created textures
std::set<Gfx::Texture*> m_allTextures;
//! Restores the state of given texture stage to the previously saved settings
void RestoreTextureStage(int index);
};
}; // namespace Gfx