summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPiotr Dziwinski <piotrdz@gmail.com>2012-07-04 00:04:53 +0200
committerPiotr Dziwinski <piotrdz@gmail.com>2012-07-04 00:04:53 +0200
commitf95df35dc58e01b99ffddfc4ad394feaa4460b09 (patch)
tree4e3afe06c73adc767e308991ce4192983fd7b72b /src
parentd9c5a439d09211ec210195709d275596c6c3c9ba (diff)
downloadcolobot-f95df35dc58e01b99ffddfc4ad394feaa4460b09.tar.gz
colobot-f95df35dc58e01b99ffddfc4ad394feaa4460b09.tar.bz2
colobot-f95df35dc58e01b99ffddfc4ad394feaa4460b09.zip
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
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt9
-rw-r--r--src/common/image.cpp221
-rw-r--r--src/common/image.h84
-rw-r--r--src/common/test/CMakeLists.txt6
-rw-r--r--src/common/test/image_test.cpp34
-rw-r--r--src/graphics/common/device.cpp17
-rw-r--r--src/graphics/common/device.h185
-rw-r--r--src/graphics/common/texture.h9
-rw-r--r--src/graphics/opengl/gldevice.cpp449
-rw-r--r--src/graphics/opengl/gldevice.h33
10 files changed, 981 insertions, 66 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 1be7a59..a02e7b9 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -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)
diff --git a/src/common/image.cpp b/src/common/image.cpp
new file mode 100644
index 0000000..78834b4
--- /dev/null
+++ b/src/common/image.cpp
@@ -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;
+}
diff --git a/src/common/image.h b/src/common/image.h
new file mode 100644
index 0000000..4d86d31
--- /dev/null
+++ b/src/common/image.h
@@ -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;
+};
diff --git a/src/common/test/CMakeLists.txt b/src/common/test/CMakeLists.txt
new file mode 100644
index 0000000..680116c
--- /dev/null
+++ b/src/common/test/CMakeLists.txt
@@ -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)
diff --git a/src/common/test/image_test.cpp b/src/common/test/image_test.cpp
new file mode 100644
index 0000000..0ad1ee2
--- /dev/null
+++ b/src/common/test/image_test.cpp
@@ -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;
+}
diff --git a/src/graphics/common/device.cpp b/src/graphics/common/device.cpp
index fcd4318..bb51699 100644
--- a/src/graphics/common/device.cpp
+++ b/src/graphics/common/device.cpp
@@ -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;
+}
diff --git a/src/graphics/common/device.h b/src/graphics/common/device.h
index 961fb6b..3382c9a 100644
--- a/src/graphics/common/device.h
+++ b/src/graphics/common/device.h
@@ -30,6 +30,9 @@
#include <string>
+class CImage;
+
+
namespace Gfx {
/**
@@ -145,6 +148,15 @@ enum CullMode
};
/**
+ \enum ShadeModel
+ \brief Shade model used in rendering */
+enum ShadeModel
+{
+ SHADE_FLAT,
+ SHADE_SMOOTH
+};
+
+/**
\enum FillMode
\brief Polygon fill mode */
enum FillMode
@@ -170,6 +182,147 @@ enum PrimitiveType
};
/**
+ \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
diff --git a/src/graphics/common/texture.h b/src/graphics/common/texture.h
index ab894db..55d5c70 100644
--- a/src/graphics/common/texture.h
+++ b/src/graphics/common/texture.h
@@ -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
diff --git a/src/graphics/opengl/gldevice.cpp b/src/graphics/opengl/gldevice.cpp
index d105a93..c6e91e1 100644
--- a/src/graphics/opengl/gldevice.cpp
+++ b/src/graphics/opengl/gldevice.cpp
@@ -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;
}
diff --git a/src/graphics/opengl/gldevice.h b/src/graphics/opengl/gldevice.h
index 298eb56..aa5dd04 100644
--- a/src/graphics/opengl/gldevice.h
+++ b/src/graphics/opengl/gldevice.h
@@ -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