summaryrefslogtreecommitdiffstats
path: root/src/graphics/opengl
diff options
context:
space:
mode:
Diffstat (limited to 'src/graphics/opengl')
-rw-r--r--src/graphics/opengl/README.txt4
-rw-r--r--src/graphics/opengl/gldevice.cpp1192
-rw-r--r--src/graphics/opengl/gldevice.h176
-rw-r--r--src/graphics/opengl/glengine.cpp21
-rw-r--r--src/graphics/opengl/glengine.h32
-rw-r--r--src/graphics/opengl/test/CMakeLists.txt87
-rw-r--r--src/graphics/opengl/test/README.txt9
-rw-r--r--src/graphics/opengl/test/light_test.cpp437
-rw-r--r--src/graphics/opengl/test/model_test.cpp377
-rw-r--r--src/graphics/opengl/test/tex1.pngbin0 -> 151263 bytes
-rw-r--r--src/graphics/opengl/test/tex2.pngbin0 -> 57503 bytes
-rw-r--r--src/graphics/opengl/test/texture_test.cpp193
-rw-r--r--src/graphics/opengl/test/transform_test.cpp339
13 files changed, 2809 insertions, 58 deletions
diff --git a/src/graphics/opengl/README.txt b/src/graphics/opengl/README.txt
index 11aba8d..0aba0ed 100644
--- a/src/graphics/opengl/README.txt
+++ b/src/graphics/opengl/README.txt
@@ -2,5 +2,5 @@ src/graphics/opengl
OpenGL engine implementation
-Contains the concreate implementation using OpenGL of functions
-of grahpics engine in graphics/common.
+Contains the concrete implementation using OpenGL of abstract CDevice class
+from src/graphics/core
diff --git a/src/graphics/opengl/gldevice.cpp b/src/graphics/opengl/gldevice.cpp
index 7938e62..3a255f4 100644
--- a/src/graphics/opengl/gldevice.cpp
+++ b/src/graphics/opengl/gldevice.cpp
@@ -18,4 +18,1194 @@
#include "graphics/opengl/gldevice.h"
-// TODO
+#include "common/config.h"
+#include "common/image.h"
+#include "math/geometry.h"
+
+
+#if defined(USE_GLEW)
+
+// When using GLEW, only glew.h is needed
+#include <GL/glew.h>
+
+#else
+
+// Should define prototypes of used extensions as OpenGL functions
+#define GL_GLEXT_PROTOTYPES
+
+#include <GL/gl.h>
+#include <GL/glu.h>
+#include <GL/glext.h>
+
+#endif // if defined(GLEW)
+
+#include <SDL/SDL.h>
+
+#include <cassert>
+
+
+
+void Gfx::GLDeviceConfig::LoadDefault()
+{
+ Gfx::DeviceConfig::LoadDefault();
+
+ hardwareAccel = true;
+
+ redSize = 8;
+ blueSize = 8;
+ greenSize = 8;
+ alphaSize = 8;
+ depthSize = 24;
+}
+
+
+
+
+Gfx::CGLDevice::CGLDevice(const Gfx::GLDeviceConfig &config)
+{
+ m_config = config;
+ m_wasInit = false;
+ m_lighting = false;
+ m_texturing = false;
+}
+
+
+Gfx::CGLDevice::~CGLDevice()
+{
+}
+
+bool Gfx::CGLDevice::GetWasInit()
+{
+ return m_wasInit;
+}
+
+std::string Gfx::CGLDevice::GetError()
+{
+ return m_error;
+}
+
+bool Gfx::CGLDevice::Create()
+{
+#if defined(USE_GLEW)
+ static bool glewInited = false;
+
+ if (!glewInited)
+ {
+ glewInited = true;
+
+ if (glewInit() != GLEW_OK)
+ {
+ m_error = "GLEW initialization failed";
+ return false;
+ }
+
+ if ( (! GLEW_ARB_multitexture) || (! GLEW_EXT_texture_env_combine) || (! GLEW_EXT_secondary_color) )
+ {
+ m_error = "GLEW reports required extensions not supported";
+ return false;
+ }
+ }
+#endif
+
+ /* NOTE: when not using GLEW, extension testing is not performed, as it is assumed that
+ glext.h is up-to-date and the OpenGL shared library has the required functions present. */
+
+ 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);
+
+ // To use separate specular color in drawing primitives
+ glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
+
+ // To avoid problems with scaling & lighting
+ glEnable(GL_RESCALE_NORMAL);
+
+ // Set just to be sure
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ glViewport(0, 0, m_config.size.w, m_config.size.h);
+
+
+ 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, &maxTextures);
+
+ m_currentTextures = std::vector<Gfx::Texture> (maxTextures, Gfx::Texture());
+ m_texturesEnabled = std::vector<bool> (maxTextures, false);
+ m_textureStageParams = std::vector<Gfx::TextureStageParams>(maxTextures, Gfx::TextureStageParams());
+
+ return true;
+}
+
+void Gfx::CGLDevice::Destroy()
+{
+ // Delete the remaining textures
+ // Should not be strictly necessary, but just in case
+ DestroyAllTextures();
+
+ m_lights.clear();
+ m_lightsEnabled.clear();
+
+ m_currentTextures.clear();
+ m_texturesEnabled.clear();
+ m_textureStageParams.clear();
+
+ m_wasInit = false;
+}
+
+void Gfx::CGLDevice::ConfigChanged(const Gfx::GLDeviceConfig& newConfig)
+{
+ m_config = newConfig;
+
+ // Reset state
+ m_lighting = false;
+ m_texturing = false;
+ Destroy();
+ Create();
+}
+
+void Gfx::CGLDevice::BeginScene()
+{
+ Clear();
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadMatrixf(m_projectionMat.Array());
+
+ UpdateModelviewMatrix();
+}
+
+void Gfx::CGLDevice::EndScene()
+{
+ glFlush();
+}
+
+void Gfx::CGLDevice::Clear()
+{
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+}
+
+void Gfx::CGLDevice::SetTransform(Gfx::TransformType type, const Math::Matrix &matrix)
+{
+ if (type == Gfx::TRANSFORM_WORLD)
+ {
+ m_worldMat = matrix;
+ UpdateModelviewMatrix();
+ }
+ else if (type == Gfx::TRANSFORM_VIEW)
+ {
+ m_viewMat = matrix;
+ UpdateModelviewMatrix();
+ }
+ else if (type == Gfx::TRANSFORM_PROJECTION)
+ {
+ m_projectionMat = matrix;
+ glMatrixMode(GL_PROJECTION);
+ glLoadMatrixf(m_projectionMat.Array());
+ }
+ else
+ {
+ assert(false);
+ }
+}
+
+const Math::Matrix& Gfx::CGLDevice::GetTransform(Gfx::TransformType type)
+{
+ if (type == Gfx::TRANSFORM_WORLD)
+ return m_worldMat;
+ else if (type == Gfx::TRANSFORM_VIEW)
+ return m_viewMat;
+ else if (type == Gfx::TRANSFORM_PROJECTION)
+ return m_projectionMat;
+ else
+ assert(false);
+
+ return m_worldMat; // to avoid warning
+}
+
+void Gfx::CGLDevice::MultiplyTransform(Gfx::TransformType type, const Math::Matrix &matrix)
+{
+ if (type == Gfx::TRANSFORM_WORLD)
+ {
+ m_worldMat = Math::MultiplyMatrices(m_worldMat, matrix);
+ UpdateModelviewMatrix();
+ }
+ else if (type == Gfx::TRANSFORM_VIEW)
+ {
+ m_viewMat = Math::MultiplyMatrices(m_viewMat, matrix);
+ UpdateModelviewMatrix();
+ }
+ else if (type == Gfx::TRANSFORM_PROJECTION)
+ {
+ m_projectionMat = Math::MultiplyMatrices(m_projectionMat, matrix);
+ glMatrixMode(GL_PROJECTION);
+ glLoadMatrixf(m_projectionMat.Array());
+ }
+ else
+ {
+ assert(false);
+ }
+}
+
+void Gfx::CGLDevice::UpdateModelviewMatrix()
+{
+ m_modelviewMat = Math::MultiplyMatrices(m_viewMat, m_worldMat);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glScalef(1.0f, 1.0f, -1.0f);
+ glMultMatrixf(m_modelviewMat.Array());
+
+ if (m_lighting)
+ {
+ for (int index = 0; index < static_cast<int>( m_lights.size() ); ++index)
+ UpdateLightPosition(index);
+ }
+}
+
+void Gfx::CGLDevice::SetMaterial(const Gfx::Material &material)
+{
+ m_material = material;
+
+ glMaterialfv(GL_FRONT, GL_AMBIENT, m_material.ambient.Array());
+ glMaterialfv(GL_FRONT, GL_DIFFUSE, m_material.diffuse.Array());
+ glMaterialfv(GL_FRONT, GL_SPECULAR, m_material.specular.Array());
+}
+
+const Gfx::Material& Gfx::CGLDevice::GetMaterial()
+{
+ return m_material;
+}
+
+int Gfx::CGLDevice::GetMaxLightCount()
+{
+ return m_lights.size();
+}
+
+void Gfx::CGLDevice::SetLight(int index, const Gfx::Light &light)
+{
+ assert(index >= 0);
+ assert(index < static_cast<int>( m_lights.size() ));
+
+ m_lights[index] = light;
+
+ // Indexing from GL_LIGHT0 should always work
+ glLightfv(GL_LIGHT0 + index, GL_AMBIENT, const_cast<GLfloat*>(light.ambient.Array()));
+ glLightfv(GL_LIGHT0 + index, GL_DIFFUSE, const_cast<GLfloat*>(light.diffuse.Array()));
+ glLightfv(GL_LIGHT0 + index, GL_SPECULAR, const_cast<GLfloat*>(light.specular.Array()));
+
+ glLightf(GL_LIGHT0 + index, GL_CONSTANT_ATTENUATION, light.attenuation0);
+ glLightf(GL_LIGHT0 + index, GL_LINEAR_ATTENUATION, light.attenuation1);
+ glLightf(GL_LIGHT0 + index, GL_QUADRATIC_ATTENUATION, light.attenuation2);
+
+ if (light.type == Gfx::LIGHT_SPOT)
+ {
+ glLightf(GL_LIGHT0 + index, GL_SPOT_CUTOFF, light.spotAngle);
+ glLightf(GL_LIGHT0 + index, GL_SPOT_EXPONENT, light.spotIntensity);
+ }
+ else
+ {
+ glLightf(GL_LIGHT0 + index, GL_SPOT_CUTOFF, 180.0f);
+ }
+
+ UpdateLightPosition(index);
+}
+
+void Gfx::CGLDevice::UpdateLightPosition(int index)
+{
+ assert(index >= 0);
+ assert(index < static_cast<int>( m_lights.size() ));
+
+ if ((! m_lighting) || (! m_lightsEnabled[index]))
+ return;
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+
+ glLoadIdentity();
+ glScalef(1.0f, 1.0f, -1.0f);
+ glMultMatrixf(m_viewMat.Array());
+
+ if (m_lights[index].type == LIGHT_DIRECTIONAL)
+ {
+ GLfloat position[4] = { m_lights[index].direction.x, m_lights[index].direction.y, m_lights[index].direction.z, 0.0f };
+ glLightfv(GL_LIGHT0 + index, GL_POSITION, position);
+ }
+ else
+ {
+ GLfloat position[4] = { m_lights[index].position.x, m_lights[index].position.y, m_lights[index].position.z, 1.0f };
+ glLightfv(GL_LIGHT0 + index, GL_POSITION, position);
+ }
+
+ if (m_lights[index].type == Gfx::LIGHT_SPOT)
+ {
+ GLfloat direction[4] = { m_lights[index].direction.x, m_lights[index].direction.y, m_lights[index].direction.z, 0.0f };
+ glLightfv(GL_LIGHT0 + index, GL_SPOT_DIRECTION, direction);
+ }
+
+ glPopMatrix();
+}
+
+const Gfx::Light& Gfx::CGLDevice::GetLight(int index)
+{
+ assert(index >= 0);
+ assert(index < static_cast<int>( m_lights.size() ));
+
+ return m_lights[index];
+}
+
+void Gfx::CGLDevice::SetLightEnabled(int index, bool enabled)
+{
+ assert(index >= 0);
+ assert(index < static_cast<int>( m_lights.size() ));
+
+ m_lightsEnabled[index] = enabled;
+
+ glEnable(GL_LIGHT0 + index);
+}
+
+bool Gfx::CGLDevice::GetLightEnabled(int index)
+{
+ assert(index >= 0);
+ assert(index < static_cast<int>( m_lights.size() ));
+
+ return m_lightsEnabled[index];
+}
+
+/** If image is invalid, returns invalid texture.
+ Otherwise, returns pointer to new Gfx::Texture struct.
+ This struct must not be deleted in other way than through DeleteTexture() */
+Gfx::Texture Gfx::CGLDevice::CreateTexture(CImage *image, const Gfx::TextureCreateParams &params)
+{
+ Gfx::Texture result;
+
+ ImageData *data = image->GetData();
+ if (data == NULL)
+ {
+ m_error = "Invalid texture data";
+ return result; // invalid texture
+ }
+
+ result.valid = true;
+ result.size.w = data->surface->w;
+ result.size.h = data->surface->h;
+
+ // Use & enable 1st texture stage
+ glActiveTexture(GL_TEXTURE0);
+ glEnable(GL_TEXTURE_2D);
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ glGenTextures(1, &result.id);
+ glBindTexture(GL_TEXTURE_2D, result.id);
+
+ // Set params
+
+ 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.mipmap)
+ glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
+ else
+ glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);
+
+ GLenum sourceFormat = 0;
+ if (params.format == Gfx::TEX_IMG_RGB)
+ {
+ sourceFormat = GL_RGB;
+ result.alpha = false;
+ }
+ else if (params.format == Gfx::TEX_IMG_BGR)
+ {
+ sourceFormat = GL_BGR;
+ result.alpha = false;
+ }
+ else if (params.format == Gfx::TEX_IMG_RGBA)
+ {
+ sourceFormat = GL_RGBA;
+ result.alpha = true;
+ }
+ else if (params.format == Gfx::TEX_IMG_BGRA)
+ {
+ sourceFormat = GL_BGRA;
+ result.alpha = true;
+ }
+ else
+ assert(false);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, data->surface->w, data->surface->h,
+ 0, sourceFormat, GL_UNSIGNED_BYTE, data->surface->pixels);
+
+
+ // Restore the previous state of 1st stage
+ if (m_currentTextures[0].valid)
+ glBindTexture(GL_TEXTURE_2D, m_currentTextures[0].id);
+ else
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ if ( (! m_texturing) || (! m_texturesEnabled[0]) )
+ glDisable(GL_TEXTURE_2D);
+
+ return result;
+}
+
+void Gfx::CGLDevice::DestroyTexture(const Gfx::Texture &texture)
+{
+ std::set<Gfx::Texture>::iterator it = m_allTextures.find(texture);
+ if (it != m_allTextures.end())
+ m_allTextures.erase(it);
+
+ // Unbind the texture if in use anywhere
+ for (int index = 0; index < static_cast<int>( m_currentTextures.size() ); ++index)
+ {
+ if (m_currentTextures[index] == texture)
+ SetTexture(index, Gfx::Texture()); // set to invalid texture
+ }
+
+ 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);
+}
+
+int Gfx::CGLDevice::GetMaxTextureCount()
+{
+ return m_currentTextures.size();
+}
+
+/**
+ If \a texture is 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, const Gfx::Texture &texture)
+{
+ assert(index >= 0);
+ assert(index < static_cast<int>( m_currentTextures.size() ));
+
+ // Enable the given texture stage
+ glActiveTexture(GL_TEXTURE0 + index);
+ glEnable(GL_TEXTURE_2D);
+
+ m_currentTextures[index] = texture; // remember the change
+
+ if (! texture.valid)
+ {
+ glBindTexture(GL_TEXTURE_2D, 0); // unbind texture
+ }
+ else
+ {
+ glBindTexture(GL_TEXTURE_2D, texture.id); // bind the texture
+ SetTextureStageParams(index, m_textureStageParams[index]); // texture stage 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 invalid texture if the given stage is not enabled. */
+Gfx::Texture Gfx::CGLDevice::GetTexture(int index)
+{
+ assert(index >= 0);
+ assert(index < static_cast<int>( m_currentTextures.size() ));
+
+ return m_currentTextures[index];
+}
+
+void Gfx::CGLDevice::SetTextureEnabled(int index, bool enabled)
+{
+ assert(index >= 0);
+ assert(index < static_cast<int>( m_currentTextures.size() ));
+
+ m_texturesEnabled[index] = enabled;
+
+ glActiveTexture(GL_TEXTURE0 + index);
+ if (enabled)
+ glEnable(GL_TEXTURE_2D);
+ else
+ glDisable(GL_TEXTURE_2D);
+}
+
+bool Gfx::CGLDevice::GetTextureEnabled(int index)
+{
+ assert(index >= 0);
+ assert(index < static_cast<int>( m_currentTextures.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::SetTextureStageParams(int index, const Gfx::TextureStageParams &params)
+{
+ assert(index >= 0);
+ assert(index < static_cast<int>( m_currentTextures.size() ));
+
+ // Remember the settings
+ m_textureStageParams[index] = params;
+
+ // Don't actually do anything if texture not set
+ if (! m_currentTextures[index].valid)
+ return;
+
+ // Enable the given stage
+ glActiveTexture(GL_TEXTURE0 + index);
+ glEnable(GL_TEXTURE_2D);
+
+ glBindTexture(GL_TEXTURE_2D, m_currentTextures[index].id);
+
+ // To save some trouble
+ if ( (params.colorOperation == Gfx::TEX_MIX_OPER_DEFAULT) &&
+ (params.alphaOperation == Gfx::TEX_MIX_OPER_DEFAULT) )
+ {
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ goto after_tex_operations;
+ }
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+
+ // Only these modes of getting color & alpha are used
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
+
+ // Color operation
+
+ if (params.colorOperation == Gfx::TEX_MIX_OPER_DEFAULT)
+ {
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE);
+ goto after_tex_color;
+ }
+ else if (params.colorOperation == Gfx::TEX_MIX_OPER_REPLACE)
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
+ else if (params.colorOperation == Gfx::TEX_MIX_OPER_MODULATE)
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
+ else if (params.colorOperation == Gfx::TEX_MIX_OPER_ADD)
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD);
+ else if (params.colorOperation == Gfx::TEX_MIX_OPER_SUBTRACT)
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_SUBTRACT);
+ else assert(false);
+
+ // Color arg1
+ if (params.colorArg1 == Gfx::TEX_MIX_ARG_TEXTURE)
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
+ else if (params.colorArg1 == Gfx::TEX_MIX_ARG_COMPUTED_COLOR)
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
+ else if (params.colorArg1 == Gfx::TEX_MIX_ARG_SRC_COLOR)
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR);
+ else if (params.colorArg1 == Gfx::TEX_MIX_ARG_FACTOR)
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_CONSTANT);
+ else assert(false);
+
+ // Color arg2
+ if (params.colorArg2 == Gfx::TEX_MIX_ARG_TEXTURE)
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_TEXTURE);
+ else if (params.colorArg2 == Gfx::TEX_MIX_ARG_COMPUTED_COLOR)
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS);
+ else if (params.colorArg2 == Gfx::TEX_MIX_ARG_SRC_COLOR)
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);
+ else if (params.colorArg2 == Gfx::TEX_MIX_ARG_FACTOR)
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);
+ else assert(false);
+
+
+after_tex_color:
+
+ // Alpha operation
+ if (params.alphaOperation == Gfx::TEX_MIX_OPER_DEFAULT)
+ {
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_TEXTURE);
+ goto after_tex_operations;
+ }
+ else if (params.colorOperation == Gfx::TEX_MIX_OPER_REPLACE)
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
+ else if (params.alphaOperation == Gfx::TEX_MIX_OPER_MODULATE)
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
+ else if (params.alphaOperation == Gfx::TEX_MIX_OPER_ADD)
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_ADD);
+ else if (params.alphaOperation == Gfx::TEX_MIX_OPER_SUBTRACT)
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_SUBTRACT);
+ else assert(false);
+
+ // Alpha arg1
+ if (params.alphaArg1 == Gfx::TEX_MIX_ARG_TEXTURE)
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_TEXTURE);
+ else if (params.alphaArg1 == Gfx::TEX_MIX_ARG_COMPUTED_COLOR)
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS);
+ else if (params.alphaArg1 == Gfx::TEX_MIX_ARG_SRC_COLOR)
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR);
+ else if (params.alphaArg1 == Gfx::TEX_MIX_ARG_FACTOR)
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_CONSTANT);
+ else assert(false);
+
+ // Alpha arg2
+ if (params.alphaArg2 == Gfx::TEX_MIX_ARG_TEXTURE)
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_TEXTURE);
+ else if (params.alphaArg2 == Gfx::TEX_MIX_ARG_COMPUTED_COLOR)
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PREVIOUS);
+ else if (params.alphaArg2 == Gfx::TEX_MIX_ARG_SRC_COLOR)
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_PRIMARY_COLOR);
+ else if (params.alphaArg2 == Gfx::TEX_MIX_ARG_FACTOR)
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_CONSTANT);
+ else assert(false);
+
+
+after_tex_operations:
+
+ 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);
+
+ // Disable the stage if it is set so
+ if ( (! m_texturing) || (! m_texturesEnabled[index]) )
+ glDisable(GL_TEXTURE_2D);
+}
+
+Gfx::TextureStageParams Gfx::CGLDevice::GetTextureStageParams(int index)
+{
+ assert(index >= 0);
+ assert(index < static_cast<int>( m_currentTextures.size() ));
+
+ return m_textureStageParams[index];
+}
+
+void Gfx::CGLDevice::SetTextureFactor(const Gfx::Color &color)
+{
+ // Needs to be set for all texture stages
+ for (int index = 0; index < static_cast<int>( m_currentTextures.size() ); ++index)
+ {
+ // Activate stage
+ glActiveTexture(GL_TEXTURE0 + 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)
+ glActiveTexture(GL_TEXTURE0);
+ 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]);
+}
+
+GLenum TranslateGfxPrimitive(Gfx::PrimitiveType type)
+{
+ GLenum flag = 0;
+ switch (type)
+ {
+ case Gfx::PRIMITIVE_POINTS: flag = GL_POINTS; break;
+ case Gfx::PRIMITIVE_LINES: flag = GL_LINES; break;
+ case Gfx::PRIMITIVE_LINE_STRIP: flag = GL_LINE_STRIP; break;
+ case Gfx::PRIMITIVE_TRIANGLES: flag = GL_TRIANGLES; break;
+ case Gfx::PRIMITIVE_TRIANGLE_STRIP: flag = GL_TRIANGLE_STRIP; break;
+ default: assert(false); break;
+ }
+ return flag;
+}
+
+void Gfx::CGLDevice::DrawPrimitive(Gfx::PrimitiveType type, const Vertex *vertices, int vertexCount)
+{
+ glBegin(TranslateGfxPrimitive(type));
+
+ glColor3f(1.0f, 1.0f, 1.0f);
+
+ for (int i = 0; i < vertexCount; ++i)
+ {
+ glNormal3fv(const_cast<GLfloat*>(vertices[i].normal.Array()));
+ glMultiTexCoord2fv(GL_TEXTURE0, const_cast<GLfloat*>(vertices[i].texCoord.Array()));
+ glVertex3fv(const_cast<GLfloat*>(vertices[i].coord.Array()));
+ }
+
+ glEnd();
+}
+
+void Gfx::CGLDevice::DrawPrimitive(Gfx::PrimitiveType type, const Gfx::VertexCol *vertices, int vertexCount)
+{
+ glBegin(TranslateGfxPrimitive(type));
+
+ for (int i = 0; i < vertexCount; ++i)
+ {
+ glColor4fv(const_cast<GLfloat*>(vertices[i].color.Array()));
+ glSecondaryColor3fv(const_cast<GLfloat*>(vertices[i].specular.Array()));
+ glMultiTexCoord2fv(GL_TEXTURE0, const_cast<GLfloat*>(vertices[i].texCoord.Array()));
+ glVertex3fv(const_cast<GLfloat*>(vertices[i].coord.Array()));
+ }
+
+ glEnd();
+}
+
+void Gfx::CGLDevice::DrawPrimitive(Gfx::PrimitiveType type, const VertexTex2 *vertices, int vertexCount)
+{
+ glBegin(TranslateGfxPrimitive(type));
+
+ glColor3f(1.0f, 1.0f, 1.0f);
+
+ for (int i = 0; i < vertexCount; ++i)
+ {
+ glNormal3fv(const_cast<GLfloat*>(vertices[i].normal.Array()));
+ glMultiTexCoord2fv(GL_TEXTURE0, const_cast<GLfloat*>(vertices[i].texCoord.Array()));
+ glMultiTexCoord2fv(GL_TEXTURE1, const_cast<GLfloat*>(vertices[i].texCoord.Array()));
+ glVertex3fv(const_cast<GLfloat*>(vertices[i].coord.Array()));
+ }
+
+ glEnd();
+}
+
+bool InPlane(Math::Vector normal, float originPlane, Math::Vector center, float radius)
+{
+ float distance = (originPlane + Math::DotProduct(normal, center)) / normal.Length();
+
+ if (distance < -radius)
+ return true;
+
+ return false;
+}
+
+/*
+ The implementation of ComputeSphereVisibility is taken from libwine's device.c
+ Copyright of the WINE team, licensed under GNU LGPL v 2.1
+ */
+
+// TODO: testing
+int Gfx::CGLDevice::ComputeSphereVisibility(const Math::Vector &center, float radius)
+{
+ Math::Matrix m;
+ m.LoadIdentity();
+ m = Math::MultiplyMatrices(m, m_worldMat);
+ m = Math::MultiplyMatrices(m, m_viewMat);
+ m = Math::MultiplyMatrices(m, m_projectionMat);
+
+ Math::Vector vec[6];
+ float originPlane[6];
+
+ // Left plane
+ vec[0].x = m.Get(4, 1) + m.Get(1, 1);
+ vec[0].y = m.Get(4, 2) + m.Get(1, 2);
+ vec[0].z = m.Get(4, 3) + m.Get(1, 3);
+ originPlane[0] = m.Get(4, 4) + m.Get(1, 4);
+
+ // Right plane
+ vec[1].x = m.Get(4, 1) - m.Get(1, 1);
+ vec[1].y = m.Get(4, 2) - m.Get(1, 2);
+ vec[1].z = m.Get(4, 3) - m.Get(1, 3);
+ originPlane[1] = m.Get(4, 4) - m.Get(1, 4);
+
+ // Top plane
+ vec[2].x = m.Get(4, 1) - m.Get(2, 1);
+ vec[2].y = m.Get(4, 2) - m.Get(2, 2);
+ vec[2].z = m.Get(4, 3) - m.Get(2, 3);
+ originPlane[2] = m.Get(4, 4) - m.Get(2, 4);
+
+ // Bottom plane
+ vec[3].x = m.Get(4, 1) + m.Get(2, 1);
+ vec[3].y = m.Get(4, 2) + m.Get(2, 2);
+ vec[3].z = m.Get(4, 3) + m.Get(2, 3);
+ originPlane[3] = m.Get(4, 4) + m.Get(2, 4);
+
+ // Front plane
+ vec[4].x = m.Get(3, 1);
+ vec[4].y = m.Get(3, 2);
+ vec[4].z = m.Get(3, 3);
+ originPlane[4] = m.Get(3, 4);
+
+ // Back plane
+ vec[5].x = m.Get(4, 1) - m.Get(3, 1);
+ vec[5].y = m.Get(4, 2) - m.Get(3, 2);
+ vec[5].z = m.Get(4, 3) - m.Get(3, 3);
+ originPlane[5] = m.Get(4, 4) - m.Get(3, 4);
+
+ int result = 0;
+
+ if (InPlane(vec[0], originPlane[0], center, radius))
+ result |= Gfx::INTERSECT_PLANE_LEFT;
+ if (InPlane(vec[1], originPlane[1], center, radius))
+ result |= Gfx::INTERSECT_PLANE_RIGHT;
+ if (InPlane(vec[2], originPlane[2], center, radius))
+ result |= Gfx::INTERSECT_PLANE_TOP;
+ if (InPlane(vec[3], originPlane[3], center, radius))
+ result |= Gfx::INTERSECT_PLANE_BOTTOM;
+ if (InPlane(vec[4], originPlane[4], center, radius))
+ result |= Gfx::INTERSECT_PLANE_FRONT;
+ if (InPlane(vec[5], originPlane[5], center, radius))
+ result |= Gfx::INTERSECT_PLANE_BACK;
+
+ return result;
+}
+
+void Gfx::CGLDevice::SetRenderState(Gfx::RenderState state, bool enabled)
+{
+ if (state == Gfx::RENDER_STATE_DEPTH_WRITE)
+ {
+ glDepthMask(enabled ? GL_TRUE : GL_FALSE);
+ return;
+ }
+ else if (state == Gfx::RENDER_STATE_LIGHTING)
+ {
+ m_lighting = enabled;
+
+ if (enabled)
+ glEnable(GL_LIGHTING);
+ else
+ glDisable(GL_LIGHTING);
+
+ if (enabled)
+ {
+ for (int index = 0; index < static_cast<int>( m_lights.size() ); ++index)
+ UpdateLightPosition(index);
+ }
+
+ return;
+ }
+ else if (state == Gfx::RENDER_STATE_TEXTURING)
+ {
+ m_texturing = enabled;
+
+ // Enable/disable stages with new setting
+ for (int index = 0; index < static_cast<int>( m_currentTextures.size() ); ++index)
+ {
+ glActiveTexture(GL_TEXTURE0 + index);
+ if (m_texturing && m_texturesEnabled[index])
+ glEnable(GL_TEXTURE_2D);
+ else
+ glDisable(GL_TEXTURE_2D);
+ }
+
+ return;
+ }
+
+ GLenum flag = 0;
+
+ switch (state)
+ {
+ case Gfx::RENDER_STATE_BLENDING: flag = GL_BLEND; break;
+ case Gfx::RENDER_STATE_FOG: flag = GL_FOG; break;
+ case Gfx::RENDER_STATE_DEPTH_TEST: flag = GL_DEPTH_TEST; break;
+ case Gfx::RENDER_STATE_ALPHA_TEST: flag = GL_ALPHA_TEST; break;
+ case Gfx::RENDER_STATE_CULLING: flag = GL_CULL_FACE; break;
+ case Gfx::RENDER_STATE_DITHERING: flag = GL_DITHER; break;
+ default: assert(false); break;
+ }
+
+ if (enabled)
+ glEnable(flag);
+ else
+ glDisable(flag);
+}
+
+bool Gfx::CGLDevice::GetRenderState(Gfx::RenderState state)
+{
+ if (state == Gfx::RENDER_STATE_LIGHTING)
+ return m_lighting;
+
+ if (state == Gfx::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_BLENDING: flag = GL_BLEND; break;
+ case Gfx::RENDER_STATE_FOG: flag = GL_FOG; break;
+ case Gfx::RENDER_STATE_DEPTH_TEST: flag = GL_DEPTH_TEST; break;
+ case Gfx::RENDER_STATE_ALPHA_TEST: flag = GL_ALPHA_TEST; break;
+ case Gfx::RENDER_STATE_CULLING: flag = GL_CULL_FACE; break;
+ case Gfx::RENDER_STATE_DITHERING: flag = GL_DITHER; break;
+ default: assert(false); break;
+ }
+
+ GLboolean result = GL_FALSE;
+ glGetBooleanv(flag, &result);
+
+ return result == GL_TRUE;
+}
+
+Gfx::CompFunc TranslateGLCompFunc(GLenum flag)
+{
+ switch (flag)
+ {
+ case GL_NEVER: return Gfx::COMP_FUNC_NEVER;
+ case GL_LESS: return Gfx::COMP_FUNC_LESS;
+ case GL_EQUAL: return Gfx::COMP_FUNC_EQUAL;
+ case GL_NOTEQUAL: return Gfx::COMP_FUNC_NOTEQUAL;
+ case GL_LEQUAL: return Gfx::COMP_FUNC_LEQUAL;
+ case GL_GREATER: return Gfx::COMP_FUNC_GREATER;
+ case GL_GEQUAL: return Gfx::COMP_FUNC_GEQUAL;
+ case GL_ALWAYS: return Gfx::COMP_FUNC_ALWAYS;
+ default: assert(false); break;
+ }
+ return Gfx::COMP_FUNC_NEVER;
+}
+
+GLenum TranslateGfxCompFunc(Gfx::CompFunc func)
+{
+ switch (func)
+ {
+ case Gfx::COMP_FUNC_NEVER: return GL_NEVER;
+ case Gfx::COMP_FUNC_LESS: return GL_LESS;
+ case Gfx::COMP_FUNC_EQUAL: return GL_EQUAL;
+ case Gfx::COMP_FUNC_NOTEQUAL: return GL_NOTEQUAL;
+ case Gfx::COMP_FUNC_LEQUAL: return GL_LEQUAL;
+ case Gfx::COMP_FUNC_GREATER: return GL_GREATER;
+ case Gfx::COMP_FUNC_GEQUAL: return GL_GEQUAL;
+ case Gfx::COMP_FUNC_ALWAYS: return GL_ALWAYS;
+ default: assert(false); break;
+ }
+ return 0;
+}
+
+void Gfx::CGLDevice::SetDepthTestFunc(Gfx::CompFunc func)
+{
+ glDepthFunc(TranslateGfxCompFunc(func));
+}
+
+Gfx::CompFunc Gfx::CGLDevice::GetDepthTestFunc()
+{
+ GLint flag = 0;
+ glGetIntegerv(GL_DEPTH_FUNC, &flag);
+ return TranslateGLCompFunc(static_cast<GLenum>(flag));
+}
+
+void Gfx::CGLDevice::SetDepthBias(float factor)
+{
+ glPolygonOffset(factor, 0.0f);
+}
+
+float Gfx::CGLDevice::GetDepthBias()
+{
+ GLfloat result = 0.0f;
+ glGetFloatv(GL_POLYGON_OFFSET_FACTOR, &result);
+ return result;
+}
+
+void Gfx::CGLDevice::SetAlphaTestFunc(Gfx::CompFunc func, float refValue)
+{
+ glAlphaFunc(TranslateGfxCompFunc(func), refValue);
+}
+
+void Gfx::CGLDevice::GetAlphaTestFunc(Gfx::CompFunc &func, float &refValue)
+{
+ GLint flag = 0;
+ glGetIntegerv(GL_ALPHA_TEST_FUNC, &flag);
+ func = TranslateGLCompFunc(static_cast<GLenum>(flag));
+
+ glGetFloatv(GL_ALPHA_TEST_REF, static_cast<GLfloat*>(&refValue));
+}
+
+Gfx::BlendFunc TranslateGLBlendFunc(GLenum flag)
+{
+ switch (flag)
+ {
+ case GL_ZERO: return Gfx::BLEND_ZERO;
+ case GL_ONE: return Gfx::BLEND_ONE;
+ case GL_SRC_COLOR: return Gfx::BLEND_SRC_COLOR;
+ case GL_ONE_MINUS_SRC_COLOR: return Gfx::BLEND_INV_SRC_COLOR;
+ case GL_DST_COLOR: return Gfx::BLEND_DST_COLOR;
+ case GL_ONE_MINUS_DST_COLOR: return Gfx::BLEND_INV_DST_COLOR;
+ case GL_SRC_ALPHA: return Gfx::BLEND_SRC_ALPHA;
+ case GL_ONE_MINUS_SRC_ALPHA: return Gfx::BLEND_INV_SRC_ALPHA;
+ case GL_DST_ALPHA: return Gfx::BLEND_DST_ALPHA;
+ case GL_ONE_MINUS_DST_ALPHA: return Gfx::BLEND_INV_DST_ALPHA;
+ case GL_SRC_ALPHA_SATURATE: return Gfx::BLEND_SRC_ALPHA_SATURATE;
+ default: assert(false); break;
+ }
+
+ return Gfx::BLEND_ZERO;
+}
+
+GLenum TranslateGfxBlendFunc(Gfx::BlendFunc func)
+{
+ switch (func)
+ {
+ case Gfx::BLEND_ZERO: return GL_ZERO;
+ case Gfx::BLEND_ONE: return GL_ONE;
+ case Gfx::BLEND_SRC_COLOR: return GL_SRC_COLOR;
+ case Gfx::BLEND_INV_SRC_COLOR: return GL_ONE_MINUS_SRC_COLOR;
+ case Gfx::BLEND_DST_COLOR: return GL_DST_COLOR;
+ case Gfx::BLEND_INV_DST_COLOR: return GL_ONE_MINUS_DST_COLOR;
+ case Gfx::BLEND_SRC_ALPHA: return GL_SRC_ALPHA;
+ case Gfx::BLEND_INV_SRC_ALPHA: return GL_ONE_MINUS_SRC_ALPHA;
+ case Gfx::BLEND_DST_ALPHA: return GL_DST_ALPHA;
+ case Gfx::BLEND_INV_DST_ALPHA: return GL_ONE_MINUS_DST_ALPHA;
+ case Gfx::BLEND_SRC_ALPHA_SATURATE: return GL_SRC_ALPHA_SATURATE;
+ default: assert(false); break;
+ }
+ return 0;
+}
+
+void Gfx::CGLDevice::SetBlendFunc(Gfx::BlendFunc srcBlend, Gfx::BlendFunc dstBlend)
+{
+ glBlendFunc(TranslateGfxBlendFunc(srcBlend), TranslateGfxBlendFunc(dstBlend));
+}
+
+void Gfx::CGLDevice::GetBlendFunc(Gfx::BlendFunc &srcBlend, Gfx::BlendFunc &dstBlend)
+{
+ GLint srcFlag = 0;
+ glGetIntegerv(GL_ALPHA_TEST_FUNC, &srcFlag);
+ srcBlend = TranslateGLBlendFunc(static_cast<GLenum>(srcFlag));
+
+ GLint dstFlag = 0;
+ glGetIntegerv(GL_ALPHA_TEST_FUNC, &dstFlag);
+ dstBlend = TranslateGLBlendFunc(static_cast<GLenum>(dstFlag));
+}
+
+void Gfx::CGLDevice::SetClearColor(const Gfx::Color &color)
+{
+ glClearColor(color.r, color.g, color.b, color.a);
+}
+
+Gfx::Color Gfx::CGLDevice::GetClearColor()
+{
+ GLfloat color[4] = { 0.0f };
+ glGetFloatv(GL_COLOR_CLEAR_VALUE, color);
+ return Gfx::Color(color[0], color[1], color[2], color[3]);
+}
+
+void Gfx::CGLDevice::SetGlobalAmbient(const Gfx::Color &color)
+{
+ glLightModelfv(GL_LIGHT_MODEL_AMBIENT, color.Array());
+}
+
+Gfx::Color Gfx::CGLDevice::GetGlobalAmbient()
+{
+ GLfloat color[4] = { 0.0f };
+ glGetFloatv(GL_LIGHT_MODEL_AMBIENT, color);
+ return Gfx::Color(color[0], color[1], color[2], color[3]);
+}
+
+void Gfx::CGLDevice::SetFogParams(Gfx::FogMode mode, const Gfx::Color &color, float start, float end, float density)
+{
+ if (mode == Gfx::FOG_LINEAR) glFogi(GL_FOG_MODE, GL_LINEAR);
+ else if (mode == Gfx::FOG_EXP) glFogi(GL_FOG_MODE, GL_EXP);
+ else if (mode == Gfx::FOG_EXP2) glFogi(GL_FOG_MODE, GL_EXP2);
+ else assert(false);
+
+ glFogf(GL_FOG_START, start);
+ glFogf(GL_FOG_END, end);
+ glFogf(GL_FOG_DENSITY, density);
+}
+
+void Gfx::CGLDevice::GetFogParams(Gfx::FogMode &mode, Gfx::Color &color, float &start, float &end, float &density)
+{
+ GLint flag = 0;
+ glGetIntegerv(GL_FOG_MODE, &flag);
+ if (flag == GL_LINEAR) mode = Gfx::FOG_LINEAR;
+ else if (flag == GL_EXP) mode = Gfx::FOG_EXP;
+ else if (flag == GL_EXP2) mode = Gfx::FOG_EXP2;
+ else assert(false);
+
+ glGetFloatv(GL_FOG_START, static_cast<GLfloat*>(&start));
+ glGetFloatv(GL_FOG_END, static_cast<GLfloat*>(&end));
+ glGetFloatv(GL_FOG_DENSITY, static_cast<GLfloat*>(&density));
+}
+
+void Gfx::CGLDevice::SetCullMode(Gfx::CullMode mode)
+{
+ if (mode == Gfx::CULL_CW) glCullFace(GL_CW);
+ else if (mode == Gfx::CULL_CCW) glCullFace(GL_CCW);
+ else assert(false);
+}
+
+Gfx::CullMode Gfx::CGLDevice::GetCullMode()
+{
+ GLint flag = 0;
+ glGetIntegerv(GL_CULL_FACE, &flag);
+ if (flag == GL_CW) return Gfx::CULL_CW;
+ else if (flag == GL_CCW) return Gfx::CULL_CCW;
+ else assert(false);
+ 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()
+{
+ GLint flag = 0;
+ glGetIntegerv(GL_SHADE_MODEL, &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);
+ else if (mode == Gfx::FILL_LINES) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ else if (mode == Gfx::FILL_FILL) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ else assert(false);
+}
+
+Gfx::FillMode Gfx::CGLDevice::GetFillMode()
+{
+ GLint flag = 0;
+ glGetIntegerv(GL_POLYGON_MODE, &flag);
+ 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);
+ return Gfx::FILL_POINT;
+}
diff --git a/src/graphics/opengl/gldevice.h b/src/graphics/opengl/gldevice.h
index a2fb4a0..1864000 100644
--- a/src/graphics/opengl/gldevice.h
+++ b/src/graphics/opengl/gldevice.h
@@ -19,13 +19,185 @@
#pragma once
-#include "graphics/common/device.h"
+#include "graphics/core/device.h"
+
+#include <string>
+#include <vector>
+#include <set>
+
namespace Gfx {
+/**
+ \struct GLDeviceConfig
+ \brief Additional config with OpenGL-specific settings */
+struct GLDeviceConfig : public DeviceConfig
+{
+ //! Size of red channel in bits
+ int redSize;
+ //! Size of green channel in bits
+ int greenSize;
+ //! Size of blue channel in bits
+ int blueSize;
+ //! Size of alpha channel in bits
+ int alphaSize;
+ //! Color depth in bits
+ int depthSize;
+
+ //! Force hardware acceleration (video mode set will fail on lack of hw accel)
+ bool hardwareAccel;
+
+ //! Constructor calls LoadDefaults()
+ GLDeviceConfig() { LoadDefault(); }
+
+ //! Loads the default values
+ void LoadDefault();
+};
+
+struct GLDevicePrivate;
+
+/**
+ \class CGLDevice
+ \brief Implementation of CDevice interface in OpenGL
+
+ Provides the concrete implementation of 3D device in OpenGL.
+
+ This class should be initialized (by calling Initialize() ) only after
+ setting the video mode by CApplication, once the OpenGL context is defined.
+ Because of that, CGLDeviceConfig is outside the CDevice class and must be set
+ in CApplication.
+*/
class CGLDevice : public Gfx::CDevice
{
- // TODO
+public:
+ CGLDevice(const Gfx::GLDeviceConfig &config);
+ virtual ~CGLDevice();
+
+ virtual bool GetWasInit();
+ virtual std::string GetError();
+
+ virtual bool Create();
+ virtual void Destroy();
+
+ void ConfigChanged(const Gfx::GLDeviceConfig &newConfig);
+
+ virtual void BeginScene();
+ virtual void EndScene();
+
+ virtual void Clear();
+
+ virtual void SetTransform(Gfx::TransformType type, const Math::Matrix &matrix);
+ virtual const Math::Matrix& GetTransform(Gfx::TransformType type);
+ virtual void MultiplyTransform(Gfx::TransformType type, const Math::Matrix &matrix);
+
+ virtual void SetMaterial(const Gfx::Material &material);
+ virtual const Gfx::Material& GetMaterial();
+
+ virtual int GetMaxLightCount();
+ virtual void SetLight(int index, const Gfx::Light &light);
+ virtual const Gfx::Light& GetLight(int index);
+ virtual void SetLightEnabled(int index, bool enabled);
+ virtual bool GetLightEnabled(int index);
+
+ virtual Gfx::Texture CreateTexture(CImage *image, const Gfx::TextureCreateParams &params);
+ virtual void DestroyTexture(const Gfx::Texture &texture);
+ virtual void DestroyAllTextures();
+
+ virtual int GetMaxTextureCount();
+ virtual void SetTexture(int index, const Gfx::Texture &texture);
+ virtual Gfx::Texture GetTexture(int index);
+ virtual void SetTextureEnabled(int index, bool enabled);
+ virtual bool GetTextureEnabled(int index);
+
+ virtual void SetTextureStageParams(int index, const Gfx::TextureStageParams &params);
+ virtual Gfx::TextureStageParams GetTextureStageParams(int index);
+
+ virtual void SetTextureFactor(const Gfx::Color &color);
+ virtual Gfx::Color GetTextureFactor();
+
+ virtual void DrawPrimitive(Gfx::PrimitiveType type, const Gfx::Vertex *vertices, int vertexCount);
+ virtual void DrawPrimitive(Gfx::PrimitiveType type, const Gfx::VertexCol *vertices, int vertexCount);
+ virtual void DrawPrimitive(Gfx::PrimitiveType type, const Gfx::VertexTex2 *vertices, int vertexCount);
+
+ virtual int ComputeSphereVisibility(const Math::Vector &center, float radius);
+
+ virtual void SetRenderState(Gfx::RenderState state, bool enabled);
+ virtual bool GetRenderState(Gfx::RenderState state);
+
+ virtual void SetDepthTestFunc(Gfx::CompFunc func);
+ virtual Gfx::CompFunc GetDepthTestFunc();
+
+ virtual void SetDepthBias(float factor);
+ virtual float GetDepthBias();
+
+ virtual void SetAlphaTestFunc(Gfx::CompFunc func, float refValue);
+ virtual void GetAlphaTestFunc(Gfx::CompFunc &func, float &refValue);
+
+ virtual void SetBlendFunc(Gfx::BlendFunc srcBlend, Gfx::BlendFunc dstBlend);
+ virtual void GetBlendFunc(Gfx::BlendFunc &srcBlend, Gfx::BlendFunc &dstBlend);
+
+ virtual void SetClearColor(const Gfx::Color &color);
+ virtual Gfx::Color GetClearColor();
+
+ virtual void SetGlobalAmbient(const Gfx::Color &color);
+ virtual Gfx::Color GetGlobalAmbient();
+
+ virtual void SetFogParams(Gfx::FogMode mode, const Gfx::Color &color, float start, float end, float density);
+ virtual void GetFogParams(Gfx::FogMode &mode, Gfx::Color &color, float &start, float &end, float &density);
+
+ 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();
+
+private:
+ //! Updates internal modelview matrix
+ void UpdateModelviewMatrix();
+ //! Updates position for given light based on transformation matrices
+ void UpdateLightPosition(int index);
+
+private:
+ //! Current config
+ Gfx::GLDeviceConfig m_config;
+ //! Was initialized?
+ bool m_wasInit;
+ //! Last encountered error
+ std::string m_error;
+
+ //! Current world matrix
+ Math::Matrix m_worldMat;
+ //! Current view matrix
+ Math::Matrix m_viewMat;
+ //! OpenGL modelview matrix = world matrix * view matrix
+ Math::Matrix m_modelviewMat;
+ //! Current projection matrix
+ Math::Matrix m_projectionMat;
+
+ //! The current material
+ Gfx::Material m_material;
+
+ //! Whether lighting is enabled
+ bool m_lighting;
+ //! Current lights
+ std::vector<Gfx::Light> m_lights;
+ //! Current lights enable status
+ std::vector<bool> m_lightsEnabled;
+
+ //! Whether texturing is enabled in general
+ bool m_texturing;
+ //! Current textures; \c NULL value means unassigned
+ std::vector<Gfx::Texture> m_currentTextures;
+ //! Current texture stages enable status
+ std::vector<bool> m_texturesEnabled;
+ //! Current texture params
+ std::vector<Gfx::TextureStageParams> m_textureStageParams;
+
+ //! Set of all created textures
+ std::set<Gfx::Texture> m_allTextures;
};
}; // namespace Gfx
diff --git a/src/graphics/opengl/glengine.cpp b/src/graphics/opengl/glengine.cpp
deleted file mode 100644
index 9aab348..0000000
--- a/src/graphics/opengl/glengine.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-// * 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/.
-
-// glengine.h
-
-#include "graphics/opengl/glengine.h"
-
-// TODO \ No newline at end of file
diff --git a/src/graphics/opengl/glengine.h b/src/graphics/opengl/glengine.h
deleted file mode 100644
index fa67bfe..0000000
--- a/src/graphics/opengl/glengine.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// * 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/.
-
-// glengine.h
-
-#pragma once
-
-
-#include "graphics/common/engine.h"
-
-namespace Gfx
-{
-
-class CGLEngine : public Gfx::CEngine
-{
- // TODO
-};
-
-};
diff --git a/src/graphics/opengl/test/CMakeLists.txt b/src/graphics/opengl/test/CMakeLists.txt
new file mode 100644
index 0000000..8ed7364
--- /dev/null
+++ b/src/graphics/opengl/test/CMakeLists.txt
@@ -0,0 +1,87 @@
+cmake_minimum_required(VERSION 2.8)
+
+find_package(OpenGL REQUIRED)
+find_package(SDL REQUIRED)
+find_package(SDL_image REQUIRED)
+find_package(PNG REQUIRED)
+
+set(CMAKE_BUILD_TYPE debug)
+set(CMAKE_CXX_FLAGS_DEBUG "-Wall -g -O0")
+
+set(ADD_LIBS "")
+
+if (${CMAKE_SYSTEM_NAME} MATCHES "Windows")
+ set(PLATFORM_WINDOWS 1)
+ set(PLATFORM_LINUX 0)
+ set(PLATFORM_OTHER 0)
+elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
+ set(PLATFORM_WINDOWS 0)
+ set(PLATFORM_LINUX 1)
+ set(PLATFORM_OTHER 0)
+ set(ADD_LIBS "-lrt")
+else()
+ set(PLATFORM_WINDOWS 0)
+ set(PLATFORM_LINUX 0)
+ set(PLATFORM_OTHER 1)
+endif()
+
+configure_file(../../../common/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/common/config.h)
+
+
+set(TEXTURE_SOURCES
+../gldevice.cpp
+../../../common/logger.cpp
+../../../common/image.cpp
+texture_test.cpp
+)
+
+set(MODEL_SOURCES
+../gldevice.cpp
+../../engine/modelfile.cpp
+../../../common/logger.cpp
+../../../common/image.cpp
+../../../common/iman.cpp
+../../../common/stringutils.cpp
+../../../app/system.cpp
+model_test.cpp
+)
+
+set(TRANSFORM_SOURCES
+../gldevice.cpp
+../../../common/logger.cpp
+../../../common/image.cpp
+../../../common/iman.cpp
+../../../app/system.cpp
+transform_test.cpp
+)
+
+set(LIGHT_SOURCES
+../gldevice.cpp
+../../../common/logger.cpp
+../../../common/image.cpp
+../../../common/iman.cpp
+../../../app/system.cpp
+light_test.cpp
+)
+
+include_directories(../../../ ${CMAKE_CURRENT_BINARY_DIR})
+
+set(LIBS
+${SDL_LIBRARY}
+${SDLIMAGE_LIBRARY}
+${OPENGL_LIBRARY}
+${PNG_LIBRARIES}
+${ADD_LIBS}
+)
+
+add_executable(texture_test ${TEXTURE_SOURCES})
+target_link_libraries(texture_test ${LIBS})
+
+add_executable(model_test ${MODEL_SOURCES})
+target_link_libraries(model_test ${LIBS})
+
+add_executable(transform_test ${TRANSFORM_SOURCES})
+target_link_libraries(transform_test ${LIBS})
+
+add_executable(light_test ${LIGHT_SOURCES})
+target_link_libraries(light_test ${LIBS})
diff --git a/src/graphics/opengl/test/README.txt b/src/graphics/opengl/test/README.txt
new file mode 100644
index 0000000..c618415
--- /dev/null
+++ b/src/graphics/opengl/test/README.txt
@@ -0,0 +1,9 @@
+Test programs for OpenGL engine:
+ - texture_test -> multitexturing test with 2 textures (included as files: ./tex1.png, ./tex2.png)
+ - model_test -> simple model viewer to test model loading
+ usage: ./model_test {dxf|mod} model_file
+ second argument is the loaded format (DXF or Colobot .mod files)
+ requires ./tex folder (or symlink) with Colobot textures
+ viewer is controlled from keyboard - the bindings can be found in code
+ - transform_test -> simple "walk around" test for world & view transformations
+ - light test -> test for lighting
diff --git a/src/graphics/opengl/test/light_test.cpp b/src/graphics/opengl/test/light_test.cpp
new file mode 100644
index 0000000..80fa911
--- /dev/null
+++ b/src/graphics/opengl/test/light_test.cpp
@@ -0,0 +1,437 @@
+#include "app/system.h"
+#include "common/logger.h"
+#include "common/image.h"
+#include "common/iman.h"
+#include "graphics/opengl/gldevice.h"
+#include "math/geometry.h"
+
+#include <SDL/SDL.h>
+#include <SDL/SDL_image.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <map>
+
+enum KeySlots
+{
+ K_Forward,
+ K_Back,
+ K_Left,
+ K_Right,
+ K_Up,
+ K_Down,
+ K_Count
+};
+bool KEYMAP[K_Count] = { false };
+
+Math::Point MOUSE_POS_BASE;
+
+Math::Vector TRANSLATION(0.0f, 2.0f, 0.0f);
+Math::Vector ROTATION, ROTATION_BASE;
+
+float CUBE_ORBIT = 0.0f;
+
+const int FRAME_DELAY = 5000;
+
+SystemTimeStamp *PREV_TIME = NULL, *CURR_TIME = NULL;
+
+void Init(Gfx::CGLDevice *device)
+{
+ device->SetRenderState(Gfx::RENDER_STATE_DEPTH_TEST, true);
+ device->SetShadeModel(Gfx::SHADE_SMOOTH);
+}
+
+void Render(Gfx::CGLDevice *device)
+{
+ device->BeginScene();
+
+ /* Unlit part of scene */
+
+ device->SetRenderState(Gfx::RENDER_STATE_LIGHTING, false);
+ device->SetRenderState(Gfx::RENDER_STATE_CULLING, false); // Double-sided drawing
+
+ Math::Matrix persp;
+ Math::LoadProjectionMatrix(persp, Math::PI / 4.0f, (800.0f) / (600.0f), 0.1f, 100.0f);
+ device->SetTransform(Gfx::TRANSFORM_PROJECTION, persp);
+
+
+ Math::Matrix viewMat;
+ Math::Matrix mat;
+
+ viewMat.LoadIdentity();
+
+ Math::LoadRotationXMatrix(mat, -ROTATION.x);
+ viewMat = Math::MultiplyMatrices(viewMat, mat);
+
+ Math::LoadRotationYMatrix(mat, -ROTATION.y);
+ viewMat = Math::MultiplyMatrices(viewMat, mat);
+
+ Math::LoadTranslationMatrix(mat, -TRANSLATION);
+ viewMat = Math::MultiplyMatrices(viewMat, mat);
+
+ device->SetTransform(Gfx::TRANSFORM_VIEW, viewMat);
+
+ Math::Matrix worldMat;
+ worldMat.LoadIdentity();
+ device->SetTransform(Gfx::TRANSFORM_WORLD, worldMat);
+
+ Gfx::VertexCol line[2] = { Gfx::VertexCol() };
+
+ for (int x = -40; x <= 40; ++x)
+ {
+ line[0].color = Gfx::Color(0.7f + x / 120.0f, 0.0f, 0.0f);
+ line[0].coord.z = -40;
+ line[0].coord.x = x;
+ line[1].color = Gfx::Color(0.7f + x / 120.0f, 0.0f, 0.0f);
+ line[1].coord.z = 40;
+ line[1].coord.x = x;
+ device->DrawPrimitive(Gfx::PRIMITIVE_LINES, line, 2);
+ }
+
+ for (int z = -40; z <= 40; ++z)
+ {
+ line[0].color = Gfx::Color(0.0f, 0.7f + z / 120.0f, 0.0f);
+ line[0].coord.z = z;
+ line[0].coord.x = -40;
+ line[1].color = Gfx::Color(0.0f, 0.7f + z / 120.0f, 0.0f);
+ line[1].coord.z = z;
+ line[1].coord.x = 40;
+ device->DrawPrimitive(Gfx::PRIMITIVE_LINES, line, 2);
+ }
+
+
+ Gfx::VertexCol quad[6] = { Gfx::VertexCol() };
+
+ quad[0].coord = Math::Vector(-1.0f, -1.0f, 0.0f);
+ quad[1].coord = Math::Vector( 1.0f, -1.0f, 0.0f);
+ quad[2].coord = Math::Vector(-1.0f, 1.0f, 0.0f);
+ quad[3].coord = Math::Vector( 1.0f, 1.0f, 0.0f);
+
+ for (int i = 0; i < 6; ++i)
+ quad[i].color = Gfx::Color(1.0f, 1.0f, 0.0f);
+
+ Math::LoadTranslationMatrix(worldMat, Math::Vector(40.0f, 2.0f, 40.0f));
+ device->SetTransform(Gfx::TRANSFORM_WORLD, worldMat);
+
+ device->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLE_STRIP, quad, 4);
+
+ for (int i = 0; i < 6; ++i)
+ quad[i].color = Gfx::Color(0.0f, 1.0f, 1.0f);
+
+ Math::LoadTranslationMatrix(worldMat, Math::Vector(-40.0f, 2.0f, -40.0f));
+ device->SetTransform(Gfx::TRANSFORM_WORLD, worldMat);
+
+ device->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLE_STRIP, quad, 4);
+
+ for (int i = 0; i < 6; ++i)
+ quad[i].color = Gfx::Color(1.0f, 0.0f, 1.0f);
+
+ Math::LoadTranslationMatrix(worldMat, Math::Vector(10.0f, 4.5f, 5.0f));
+ device->SetTransform(Gfx::TRANSFORM_WORLD, worldMat);
+
+ device->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLE_STRIP, quad, 4);
+
+ /* Moving lit cube */
+ device->SetRenderState(Gfx::RENDER_STATE_LIGHTING, true);
+ device->SetRenderState(Gfx::RENDER_STATE_CULLING, true); // Culling (CCW faces)
+
+ device->SetGlobalAmbient(Gfx::Color(0.4f, 0.4f, 0.4f));
+
+ Gfx::Light light1;
+ light1.type = Gfx::LIGHT_POINT;
+ light1.position = Math::Vector(10.0f, 4.5f, 5.0f);
+ light1.ambient = Gfx::Color(0.2f, 0.2f, 0.2f);
+ light1.diffuse = Gfx::Color(1.0f, 0.1f, 0.1f);
+ light1.specular = Gfx::Color(0.0f, 0.0f, 0.0f);
+ device->SetLight(0, light1);
+ device->SetLightEnabled(0, true);
+
+ /*Gfx::Light light2;
+ device->SetLight(1, light2);
+ device->SetLightEnabled(1, true);*/
+
+ Gfx::Material material;
+ material.ambient = Gfx::Color(0.3f, 0.3f, 0.3f);
+ material.diffuse = Gfx::Color(0.8f, 0.7f, 0.6f);
+ material.specular = Gfx::Color(0.0f, 0.0f, 0.0f);
+ device->SetMaterial(material);
+
+ const Gfx::Vertex cube[6][4] =
+ {
+ {
+ // Front
+ Gfx::Vertex(Math::Vector(-1.0f, -1.0f, -1.0f), Math::Vector( 0.0f, 0.0f, -1.0f)),
+ Gfx::Vertex(Math::Vector( 1.0f, -1.0f, -1.0f), Math::Vector( 0.0f, 0.0f, -1.0f)),
+ Gfx::Vertex(Math::Vector(-1.0f, 1.0f, -1.0f), Math::Vector( 0.0f, 0.0f, -1.0f)),
+ Gfx::Vertex(Math::Vector( 1.0f, 1.0f, -1.0f), Math::Vector( 0.0f, 0.0f, -1.0f))
+ },
+
+ {
+ // Back
+ Gfx::Vertex(Math::Vector( 1.0f, -1.0f, 1.0f), Math::Vector( 0.0f, 0.0f, 1.0f)),
+ Gfx::Vertex(Math::Vector(-1.0f, -1.0f, 1.0f), Math::Vector( 0.0f, 0.0f, 1.0f)),
+ Gfx::Vertex(Math::Vector( 1.0f, 1.0f, 1.0f), Math::Vector( 0.0f, 0.0f, 1.0f)),
+ Gfx::Vertex(Math::Vector(-1.0f, 1.0f, 1.0f), Math::Vector( 0.0f, 0.0f, 1.0f))
+ },
+
+ {
+ // Top
+ Gfx::Vertex(Math::Vector(-1.0f, 1.0f, -1.0f), Math::Vector( 0.0f, 1.0f, 0.0f)),
+ Gfx::Vertex(Math::Vector( 1.0f, 1.0f, -1.0f), Math::Vector( 0.0f, 1.0f, 0.0f)),
+ Gfx::Vertex(Math::Vector(-1.0f, 1.0f, 1.0f), Math::Vector( 0.0f, 1.0f, 0.0f)),
+ Gfx::Vertex(Math::Vector( 1.0f, 1.0f, 1.0f), Math::Vector( 0.0f, 1.0f, 0.0f))
+ },
+
+ {
+ // Bottom
+ Gfx::Vertex(Math::Vector(-1.0f, -1.0f, 1.0f), Math::Vector( 0.0f, -1.0f, 0.0f)),
+ Gfx::Vertex(Math::Vector( 1.0f, -1.0f, 1.0f), Math::Vector( 0.0f, -1.0f, 0.0f)),
+ Gfx::Vertex(Math::Vector(-1.0f, -1.0f, -1.0f), Math::Vector( 0.0f, -1.0f, 0.0f)),
+ Gfx::Vertex(Math::Vector( 1.0f, -1.0f, -1.0f), Math::Vector( 0.0f, -1.0f, 0.0f))
+ },
+
+ {
+ // Left
+ Gfx::Vertex(Math::Vector(-1.0f, -1.0f, 1.0f), Math::Vector(-1.0f, 0.0f, 0.0f)),
+ Gfx::Vertex(Math::Vector(-1.0f, -1.0f, -1.0f), Math::Vector(-1.0f, 0.0f, 0.0f)),
+ Gfx::Vertex(Math::Vector(-1.0f, 1.0f, 1.0f), Math::Vector(-1.0f, 0.0f, 0.0f)),
+ Gfx::Vertex(Math::Vector(-1.0f, 1.0f, -1.0f), Math::Vector(-1.0f, 0.0f, 0.0f))
+ },
+
+ {
+ // Right
+ Gfx::Vertex(Math::Vector( 1.0f, -1.0f, -1.0f), Math::Vector( 1.0f, 0.0f, 0.0f)),
+ Gfx::Vertex(Math::Vector( 1.0f, -1.0f, 1.0f), Math::Vector( 1.0f, 0.0f, 0.0f)),
+ Gfx::Vertex(Math::Vector( 1.0f, 1.0f, -1.0f), Math::Vector( 1.0f, 0.0f, 0.0f)),
+ Gfx::Vertex(Math::Vector( 1.0f, 1.0f, 1.0f), Math::Vector( 1.0f, 0.0f, 0.0f))
+ }
+ };
+
+ Math::Matrix cubeTrans;
+ Math::LoadTranslationMatrix(cubeTrans, Math::Vector(10.0f, 2.0f, 5.0f));
+ Math::Matrix cubeRot;
+ Math::LoadRotationMatrix(cubeRot, Math::Vector(0.0f, 1.0f, 0.0f), CUBE_ORBIT);
+ Math::Matrix cubeRotInv;
+ Math::LoadRotationMatrix(cubeRotInv, Math::Vector(0.0f, 1.0f, 0.0f), -CUBE_ORBIT);
+ Math::Matrix cubeTransRad;
+ Math::LoadTranslationMatrix(cubeTransRad, Math::Vector(0.0f, 0.0f, 6.0f));
+ worldMat = Math::MultiplyMatrices(cubeTransRad, cubeRotInv);
+ worldMat = Math::MultiplyMatrices(cubeRot, worldMat);
+ worldMat = Math::MultiplyMatrices(cubeTrans, worldMat);
+ device->SetTransform(Gfx::TRANSFORM_WORLD, worldMat);
+
+ for (int i = 0; i < 6; ++i)
+ device->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLE_STRIP, cube[i], 4);
+
+ device->EndScene();
+}
+
+void Update()
+{
+ const float TRANS_SPEED = 6.0f; // units / sec
+
+ GetCurrentTimeStamp(CURR_TIME);
+ float timeDiff = TimeStampDiff(PREV_TIME, CURR_TIME, STU_SEC);
+ CopyTimeStamp(PREV_TIME, CURR_TIME);
+
+ CUBE_ORBIT += timeDiff * (Math::PI / 4.0f);
+
+ Math::Vector incTrans;
+
+ if (KEYMAP[K_Forward])
+ incTrans.z = +TRANS_SPEED * timeDiff;
+ if (KEYMAP[K_Back])
+ incTrans.z = -TRANS_SPEED * timeDiff;
+ if (KEYMAP[K_Right])
+ incTrans.x = +TRANS_SPEED * timeDiff;
+ if (KEYMAP[K_Left])
+ incTrans.x = -TRANS_SPEED * timeDiff;
+ if (KEYMAP[K_Up])
+ incTrans.y = +TRANS_SPEED * timeDiff;
+ if (KEYMAP[K_Down])
+ incTrans.y = -TRANS_SPEED * timeDiff;
+
+ Math::Point rotTrans = Math::RotatePoint(-ROTATION.y, Math::Point(incTrans.x, incTrans.z));
+ incTrans.x = rotTrans.x;
+ incTrans.z = rotTrans.y;
+ TRANSLATION += incTrans;
+}
+
+void KeyboardDown(SDLKey key)
+{
+ switch (key)
+ {
+ case SDLK_w:
+ KEYMAP[K_Forward] = true;
+ break;
+ case SDLK_s:
+ KEYMAP[K_Back] = true;
+ break;
+ case SDLK_d:
+ KEYMAP[K_Right] = true;
+ break;
+ case SDLK_a:
+ KEYMAP[K_Left] = true;
+ break;
+ case SDLK_z:
+ KEYMAP[K_Down] = true;
+ break;
+ case SDLK_x:
+ KEYMAP[K_Up] = true;
+ break;
+ default:
+ break;
+ }
+}
+
+void KeyboardUp(SDLKey key)
+{
+ switch (key)
+ {
+ case SDLK_w:
+ KEYMAP[K_Forward] = false;
+ break;
+ case SDLK_s:
+ KEYMAP[K_Back] = false;
+ break;
+ case SDLK_d:
+ KEYMAP[K_Right] = false;
+ break;
+ case SDLK_a:
+ KEYMAP[K_Left] = false;
+ break;
+ case SDLK_z:
+ KEYMAP[K_Down] = false;
+ break;
+ case SDLK_x:
+ KEYMAP[K_Up] = false;
+ break;
+ default:
+ break;
+ }
+}
+
+void MouseMove(int x, int y)
+{
+ Math::Point currentPos((float)x, (float)y);
+
+ static bool first = true;
+ if (first || (x < 10) || (y < 10) || (x > 790) || (y > 590))
+ {
+ SDL_WarpMouse(400, 300);
+ MOUSE_POS_BASE.x = 400;
+ MOUSE_POS_BASE.y = 300;
+ ROTATION_BASE = ROTATION;
+ first = false;
+ return;
+ }
+
+ ROTATION.y = ROTATION_BASE.y + ((float) (x - MOUSE_POS_BASE.x) / 800.0f) * Math::PI;
+ ROTATION.x = ROTATION_BASE.x + ((float) (y - MOUSE_POS_BASE.y) / 600.0f) * Math::PI;
+}
+
+int main(int argc, char *argv[])
+{
+ CLogger logger;
+
+ PREV_TIME = CreateTimeStamp();
+ CURR_TIME = CreateTimeStamp();
+
+ GetCurrentTimeStamp(PREV_TIME);
+ GetCurrentTimeStamp(CURR_TIME);
+
+ CInstanceManager iMan;
+
+ // Without any error checking, for simplicity
+
+ SDL_Init(SDL_INIT_VIDEO);
+
+ IMG_Init(IMG_INIT_PNG);
+
+ const SDL_VideoInfo *videoInfo = SDL_GetVideoInfo();
+
+ Uint32 videoFlags = SDL_OPENGL | SDL_GL_DOUBLEBUFFER | SDL_HWPALETTE;
+
+ if (videoInfo->hw_available)
+ videoFlags |= SDL_HWSURFACE;
+ else
+ videoFlags |= SDL_SWSURFACE;
+
+ if (videoInfo->blit_hw)
+ videoFlags |= SDL_HWACCEL;
+
+
+ SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
+
+ SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 8);
+
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+
+ SDL_Surface *surface = SDL_SetVideoMode(800, 600, 32, videoFlags);
+
+
+ SDL_WM_SetCaption("Light Test", "Light Test");
+
+ //SDL_WM_GrabInput(SDL_GRAB_ON);
+ SDL_ShowCursor(SDL_DISABLE);
+
+ Gfx::CGLDevice *device = new Gfx::CGLDevice();
+ device->Create();
+
+ Init(device);
+
+ bool done = false;
+ while (! done)
+ {
+ Render(device);
+ Update();
+
+ SDL_GL_SwapBuffers();
+
+ SDL_Event event;
+ while (SDL_PollEvent(&event))
+ {
+ if (event.type == SDL_QUIT)
+ {
+ break;
+ done = true;
+ }
+ else if (event.type == SDL_KEYDOWN)
+ {
+ if (event.key.keysym.sym == SDLK_q)
+ {
+ done = true;
+ break;
+ }
+ else
+ KeyboardDown(event.key.keysym.sym);
+ }
+ else if (event.type == SDL_KEYUP)
+ KeyboardUp(event.key.keysym.sym);
+ else if (event.type == SDL_MOUSEMOTION)
+ MouseMove(event.motion.x, event.motion.y);
+ }
+
+ usleep(FRAME_DELAY);
+ }
+
+ //SDL_WM_GrabInput(SDL_GRAB_OFF);
+ SDL_ShowCursor(SDL_ENABLE);
+
+ device->Destroy();
+ delete device;
+
+ SDL_FreeSurface(surface);
+
+ IMG_Quit();
+
+ SDL_Quit();
+
+ DestroyTimeStamp(PREV_TIME);
+ DestroyTimeStamp(CURR_TIME);
+
+ return 0;
+}
diff --git a/src/graphics/opengl/test/model_test.cpp b/src/graphics/opengl/test/model_test.cpp
new file mode 100644
index 0000000..3e8efe6
--- /dev/null
+++ b/src/graphics/opengl/test/model_test.cpp
@@ -0,0 +1,377 @@
+#include "app/system.h"
+#include "common/logger.h"
+#include "common/image.h"
+#include "common/iman.h"
+#include "graphics/engine/modelfile.h"
+#include "graphics/opengl/gldevice.h"
+#include "math/geometry.h"
+
+#include <SDL/SDL.h>
+#include <SDL/SDL_image.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <map>
+
+enum KeySlots
+{
+ K_RotXUp,
+ K_RotXDown,
+ K_RotYLeft,
+ K_RotYRight,
+ K_Forward,
+ K_Back,
+ K_Left,
+ K_Right,
+ K_Up,
+ K_Down,
+ K_Count
+};
+bool KEYMAP[K_Count] = { false };
+
+Math::Vector TRANSLATION(0.0f, 0.0f, 30.0f);
+Math::Vector ROTATION;
+
+const int FRAME_DELAY = 5000;
+
+std::map<std::string, Gfx::Texture> TEXS;
+
+SystemTimeStamp *PREV_TIME = NULL, *CURR_TIME = NULL;
+
+Gfx::Texture GetTexture(const std::string &name)
+{
+ std::map<std::string, Gfx::Texture>::iterator it = TEXS.find(name);
+ if (it == TEXS.end())
+ return Gfx::Texture();
+
+ return (*it).second;
+}
+
+void LoadTexture(Gfx::CGLDevice *device, const std::string &name)
+{
+ if (name.empty())
+ return;
+
+ Gfx::Texture tex = GetTexture(name);
+
+ if (tex.valid)
+ return;
+
+ CImage img;
+ if (! img.Load(std::string("tex/") + name))
+ {
+ std::string err = img.GetError();
+ GetLogger()->Error("Texture not loaded, error: %s!\n", err.c_str());
+ }
+ else
+ {
+ Gfx::TextureCreateParams texCreateParams;
+ texCreateParams.mipmap = true;
+ if (img.GetData()->surface->format->Amask == 0)
+ texCreateParams.format = Gfx::TEX_IMG_BGR;
+ else
+ texCreateParams.format = Gfx::TEX_IMG_BGRA;
+ texCreateParams.minFilter = Gfx::TEX_MIN_FILTER_LINEAR_MIPMAP_LINEAR;
+ texCreateParams.magFilter = Gfx::TEX_MAG_FILTER_LINEAR;
+
+ tex = device->CreateTexture(&img, texCreateParams);
+ }
+
+ TEXS[name] = tex;
+}
+
+void Init(Gfx::CGLDevice *device, Gfx::CModelFile *model)
+{
+ std::vector<Gfx::ModelTriangle> &triangles = model->GetTriangles();
+
+ for (int i = 0; i < (int) triangles.size(); ++i)
+ {
+ LoadTexture(device, triangles[i].tex1Name);
+ LoadTexture(device, triangles[i].tex2Name);
+ }
+
+ device->SetRenderState(Gfx::RENDER_STATE_TEXTURING, true);
+ device->SetRenderState(Gfx::RENDER_STATE_LIGHTING, true);
+ device->SetRenderState(Gfx::RENDER_STATE_DEPTH_TEST, true);
+ device->SetShadeModel(Gfx::SHADE_SMOOTH);
+
+ Gfx::Light light;
+ light.type = Gfx::LIGHT_DIRECTIONAL;
+ light.ambient = Gfx::Color(0.4f, 0.4f, 0.4f, 0.0f);
+ light.diffuse = Gfx::Color(0.8f, 0.8f, 0.8f, 0.0f);
+ light.specular = Gfx::Color(0.2f, 0.2f, 0.2f, 0.0f);
+ light.position = Math::Vector(0.0f, 0.0f, -1.0f);
+ light.direction = Math::Vector(0.0f, 0.0f, 1.0f);
+
+ device->SetGlobalAmbient(Gfx::Color(0.5f, 0.5f, 0.5f, 0.0f));
+ device->SetLight(0, light);
+ device->SetLightEnabled(0, true);
+}
+
+void Render(Gfx::CGLDevice *device, Gfx::CModelFile *modelFile)
+{
+ device->BeginScene();
+
+ Math::Matrix persp;
+ Math::LoadProjectionMatrix(persp, Math::PI / 4.0f, (800.0f) / (600.0f), 0.1f, 100.0f);
+ device->SetTransform(Gfx::TRANSFORM_PROJECTION, persp);
+
+ Math::Matrix id;
+ id.LoadIdentity();
+ device->SetTransform(Gfx::TRANSFORM_WORLD, id);
+
+ Math::Matrix viewMat;
+ Math::LoadTranslationMatrix(viewMat, TRANSLATION);
+ Math::Matrix rot;
+ Math::LoadRotationXZYMatrix(rot, ROTATION);
+ viewMat = Math::MultiplyMatrices(viewMat, rot);
+ device->SetTransform(Gfx::TRANSFORM_VIEW, viewMat);
+
+ std::vector<Gfx::ModelTriangle> &triangles = modelFile->GetTriangles();
+
+ Gfx::VertexTex2 tri[3];
+
+ for (int i = 0; i < (int) triangles.size(); ++i)
+ {
+ device->SetTexture(0, GetTexture(triangles[i].tex1Name));
+ device->SetTexture(1, GetTexture(triangles[i].tex2Name));
+ device->SetTextureEnabled(0, true);
+ device->SetTextureEnabled(1, true);
+
+ device->SetMaterial(triangles[i].material);
+
+ tri[0] = triangles[i].p1;
+ tri[1] = triangles[i].p2;
+ tri[2] = triangles[i].p3;
+
+ device->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLES, tri, 3);
+ }
+
+ device->EndScene();
+}
+
+void Update()
+{
+ const float ROT_SPEED = 80.0f * Math::DEG_TO_RAD; // rad / sec
+ const float TRANS_SPEED = 3.0f; // units / sec
+
+ GetCurrentTimeStamp(CURR_TIME);
+ float timeDiff = TimeStampDiff(PREV_TIME, CURR_TIME, STU_SEC);
+ CopyTimeStamp(PREV_TIME, CURR_TIME);
+
+ if (KEYMAP[K_RotYLeft])
+ ROTATION.y -= ROT_SPEED * timeDiff;
+ if (KEYMAP[K_RotYRight])
+ ROTATION.y += ROT_SPEED * timeDiff;
+ if (KEYMAP[K_RotXDown])
+ ROTATION.x -= ROT_SPEED * timeDiff;
+ if (KEYMAP[K_RotXUp])
+ ROTATION.x += ROT_SPEED * timeDiff;
+
+ if (KEYMAP[K_Forward])
+ TRANSLATION.z -= TRANS_SPEED * timeDiff;
+ if (KEYMAP[K_Back])
+ TRANSLATION.z += TRANS_SPEED * timeDiff;
+ if (KEYMAP[K_Left])
+ TRANSLATION.x += TRANS_SPEED * timeDiff;
+ if (KEYMAP[K_Right])
+ TRANSLATION.x -= TRANS_SPEED * timeDiff;
+ if (KEYMAP[K_Up])
+ TRANSLATION.y += TRANS_SPEED * timeDiff;
+ if (KEYMAP[K_Down])
+ TRANSLATION.y -= TRANS_SPEED * timeDiff;
+}
+
+void KeyboardDown(SDLKey key)
+{
+ switch (key)
+ {
+ case SDLK_LEFT:
+ KEYMAP[K_RotYLeft] = true;
+ break;
+ case SDLK_RIGHT:
+ KEYMAP[K_RotYRight] = true;
+ break;
+ case SDLK_UP:
+ KEYMAP[K_RotXUp] = true;
+ break;
+ case SDLK_DOWN:
+ KEYMAP[K_RotXDown] = true;
+ break;
+ case SDLK_w:
+ KEYMAP[K_Forward] = true;
+ break;
+ case SDLK_s:
+ KEYMAP[K_Back] = true;
+ break;
+ case SDLK_a:
+ KEYMAP[K_Left] = true;
+ break;
+ case SDLK_d:
+ KEYMAP[K_Right] = true;
+ break;
+ case SDLK_z:
+ KEYMAP[K_Down] = true;
+ break;
+ case SDLK_x:
+ KEYMAP[K_Up] = true;
+ break;
+ default:
+ break;
+ }
+}
+
+void KeyboardUp(SDLKey key)
+{
+ switch (key)
+ {
+ case SDLK_LEFT:
+ KEYMAP[K_RotYLeft] = false;
+ break;
+ case SDLK_RIGHT:
+ KEYMAP[K_RotYRight] = false;
+ break;
+ case SDLK_UP:
+ KEYMAP[K_RotXUp] = false;
+ break;
+ case SDLK_DOWN:
+ KEYMAP[K_RotXDown] = false;
+ break;
+ case SDLK_w:
+ KEYMAP[K_Forward] = false;
+ break;
+ case SDLK_s:
+ KEYMAP[K_Back] = false;
+ break;
+ case SDLK_a:
+ KEYMAP[K_Left] = false;
+ break;
+ case SDLK_d:
+ KEYMAP[K_Right] = false;
+ break;
+ case SDLK_z:
+ KEYMAP[K_Down] = false;
+ break;
+ case SDLK_x:
+ KEYMAP[K_Up] = false;
+ break;
+ default:
+ break;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ CLogger logger;
+
+ PREV_TIME = CreateTimeStamp();
+ CURR_TIME = CreateTimeStamp();
+
+ GetCurrentTimeStamp(PREV_TIME);
+ GetCurrentTimeStamp(CURR_TIME);
+
+ if (argc != 3)
+ {
+ std::cerr << "Usage: " << argv[0] << "{mod|dxf} model_file" << std::endl;
+ return 1;
+ }
+
+ CInstanceManager iMan;
+
+ Gfx::CModelFile *modelFile = new Gfx::CModelFile(&iMan);
+ if (std::string(argv[1]) == "mod")
+ {
+ if (! modelFile->ReadModel(argv[2], false, false))
+ {
+ std::cerr << "Error reading MOD: " << modelFile->GetError() << std::endl;
+ return 1;
+ }
+ }
+ else if (std::string(argv[1]) == "dxf")
+ {
+ if (! modelFile->ReadDXF(argv[2], 0.0f, 0.0f))
+ {
+ std::cerr << "Error reading DXF: " << modelFile->GetError() << std::endl;
+ return 1;
+ }
+ }
+ else
+ {
+ std::cerr << "Usage: " << argv[0] << "{mod|dxf} model_file" << std::endl;
+ return 1;
+ }
+
+ // Without any error checking, for simplicity
+
+ SDL_Init(SDL_INIT_VIDEO);
+
+ IMG_Init(IMG_INIT_PNG);
+
+ const SDL_VideoInfo *videoInfo = SDL_GetVideoInfo();
+
+ Uint32 videoFlags = SDL_OPENGL | SDL_GL_DOUBLEBUFFER | SDL_HWPALETTE;
+
+ if (videoInfo->hw_available)
+ videoFlags |= SDL_HWSURFACE;
+ else
+ videoFlags |= SDL_SWSURFACE;
+
+ if (videoInfo->blit_hw)
+ videoFlags |= SDL_HWACCEL;
+
+
+ SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
+
+ SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 8);
+
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+
+ SDL_Surface *surface = SDL_SetVideoMode(800, 600, 32, videoFlags);
+
+
+ SDL_WM_SetCaption("Model Test", "Model Test");
+
+ Gfx::CGLDevice *device = new Gfx::CGLDevice();
+ device->Create();
+
+ Init(device, modelFile);
+
+ bool done = false;
+ while (! done)
+ {
+ Render(device, modelFile);
+ Update();
+
+ SDL_GL_SwapBuffers();
+
+ SDL_Event event;
+ SDL_PollEvent(&event);
+ if (event.type == SDL_QUIT)
+ done = true;
+ else if (event.type == SDL_KEYDOWN)
+ KeyboardDown(event.key.keysym.sym);
+ else if (event.type == SDL_KEYUP)
+ KeyboardUp(event.key.keysym.sym);
+
+ usleep(FRAME_DELAY);
+ }
+
+ delete modelFile;
+
+ device->Destroy();
+ delete device;
+
+ SDL_FreeSurface(surface);
+
+ IMG_Quit();
+
+ SDL_Quit();
+
+ DestroyTimeStamp(PREV_TIME);
+ DestroyTimeStamp(CURR_TIME);
+
+ return 0;
+}
diff --git a/src/graphics/opengl/test/tex1.png b/src/graphics/opengl/test/tex1.png
new file mode 100644
index 0000000..46c68a0
--- /dev/null
+++ b/src/graphics/opengl/test/tex1.png
Binary files differ
diff --git a/src/graphics/opengl/test/tex2.png b/src/graphics/opengl/test/tex2.png
new file mode 100644
index 0000000..ebdae0d
--- /dev/null
+++ b/src/graphics/opengl/test/tex2.png
Binary files differ
diff --git a/src/graphics/opengl/test/texture_test.cpp b/src/graphics/opengl/test/texture_test.cpp
new file mode 100644
index 0000000..c3c568b
--- /dev/null
+++ b/src/graphics/opengl/test/texture_test.cpp
@@ -0,0 +1,193 @@
+#include "common/logger.h"
+#include "common/image.h"
+#include "graphics/opengl/gldevice.h"
+#include "math/geometry.h"
+
+#include <SDL/SDL.h>
+#include <SDL/SDL_image.h>
+#include <unistd.h>
+
+
+void Init(Gfx::CGLDevice *device)
+{
+ device->SetShadeModel(Gfx::SHADE_SMOOTH);
+
+ device->SetRenderState(Gfx::RENDER_STATE_DEPTH_TEST, false);
+ device->SetRenderState(Gfx::RENDER_STATE_TEXTURING, true);
+
+ device->SetTextureEnabled(0, true);
+ device->SetTextureEnabled(1, true);
+
+ CImage img1;
+ if (! img1.Load("tex1.png"))
+ {
+ std::string err = img1.GetError();
+ GetLogger()->Error("texture 1 not loaded, error: %d!\n", err.c_str());
+ }
+ CImage img2;
+ if (! img2.Load("tex2.png"))
+ {
+ std::string err = img2.GetError();
+ GetLogger()->Error("texture 2 not loaded, error: %d!\n", err.c_str());
+ }
+
+ Gfx::TextureCreateParams tex1CreateParams;
+ tex1CreateParams.mipmap = true;
+ tex1CreateParams.format = Gfx::TEX_IMG_RGBA;
+ tex1CreateParams.minFilter = Gfx::TEX_MIN_FILTER_LINEAR_MIPMAP_LINEAR;
+ tex1CreateParams.magFilter = Gfx::TEX_MAG_FILTER_LINEAR;
+
+ Gfx::TextureCreateParams tex2CreateParams;
+ tex2CreateParams.mipmap = true;
+ tex2CreateParams.format = Gfx::TEX_IMG_RGBA;
+ tex2CreateParams.minFilter = Gfx::TEX_MIN_FILTER_NEAREST_MIPMAP_NEAREST;
+ tex2CreateParams.magFilter = Gfx::TEX_MAG_FILTER_NEAREST;
+
+ Gfx::Texture tex1 = device->CreateTexture(&img1, tex1CreateParams);
+ Gfx::Texture tex2 = device->CreateTexture(&img2, tex2CreateParams);
+
+ device->SetTexture(0, tex1);
+ device->SetTexture(1, tex2);
+}
+
+void Render(Gfx::CGLDevice *device)
+{
+ device->BeginScene();
+
+ Math::Matrix ortho;
+ Math::LoadOrthoProjectionMatrix(ortho, -10, 10, -10, 10);
+ device->SetTransform(Gfx::TRANSFORM_PROJECTION, ortho);
+
+ Math::Matrix id;
+ id.LoadIdentity();
+
+ device->SetTransform(Gfx::TRANSFORM_WORLD, id);
+ device->SetTransform(Gfx::TRANSFORM_VIEW, id);
+
+ static Gfx::VertexTex2 quad[] =
+ {
+ Gfx::VertexTex2(Math::Vector(-2.0f, 2.0f, 0.0f), Math::Vector(), Math::Point(0.0f, 0.0f), Math::Point(0.0f, 0.0f)),
+ Gfx::VertexTex2(Math::Vector( 2.0f, 2.0f, 0.0f), Math::Vector(), Math::Point(1.0f, 0.0f), Math::Point(1.0f, 0.0f)),
+ Gfx::VertexTex2(Math::Vector( 2.0f, -2.0f, 0.0f), Math::Vector(), Math::Point(1.0f, 1.0f), Math::Point(1.0f, 1.0f)),
+
+ Gfx::VertexTex2(Math::Vector( 2.0f, -2.0f, 0.0f), Math::Vector(), Math::Point(1.0f, 1.0f), Math::Point(1.0f, 1.0f)),
+ Gfx::VertexTex2(Math::Vector(-2.0f, -2.0f, 0.0f), Math::Vector(), Math::Point(0.0f, 1.0f), Math::Point(0.0f, 1.0f)),
+ Gfx::VertexTex2(Math::Vector(-2.0f, 2.0f, 0.0f), Math::Vector(), Math::Point(0.0f, 0.0f), Math::Point(0.0f, 0.0f)),
+ };
+
+ Gfx::TextureStageParams tex1StageParams;
+ tex1StageParams.colorOperation = Gfx::TEX_MIX_OPER_DEFAULT;
+ tex1StageParams.alphaOperation = Gfx::TEX_MIX_OPER_DEFAULT;
+ device->SetTextureStageParams(0, tex1StageParams);
+
+ Gfx::TextureStageParams tex2StageParams;
+ tex2StageParams.colorOperation = Gfx::TEX_MIX_OPER_DEFAULT;
+ tex2StageParams.alphaOperation = Gfx::TEX_MIX_OPER_DEFAULT;
+ device->SetTextureStageParams(1, tex2StageParams);
+
+ Math::Matrix t;
+ Math::LoadTranslationMatrix(t, Math::Vector(-4.0f, 4.0f, 0.0f));
+ device->SetTransform(Gfx::TRANSFORM_VIEW, t);
+
+ device->SetTextureEnabled(0, true);
+ device->SetTextureEnabled(1, false);
+
+ device->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLES, quad, 6);
+
+ Math::LoadTranslationMatrix(t, Math::Vector( 4.0f, 4.0f, 0.0f));
+ device->SetTransform(Gfx::TRANSFORM_VIEW, t);
+
+ device->SetTextureEnabled(0, false);
+ device->SetTextureEnabled(1, true);
+
+ device->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLES, quad, 6);
+
+ Math::LoadTranslationMatrix(t, Math::Vector( 0.0f, -4.0f, 0.0f));
+ device->SetTransform(Gfx::TRANSFORM_VIEW, t);
+
+ device->SetTextureEnabled(0, true);
+ device->SetTextureEnabled(1, true);
+
+ tex1StageParams.colorOperation = Gfx::TEX_MIX_OPER_DEFAULT;
+ tex1StageParams.alphaOperation = Gfx::TEX_MIX_OPER_DEFAULT;
+ device->SetTextureStageParams(0, tex1StageParams);
+
+ tex2StageParams.colorOperation = Gfx::TEX_MIX_OPER_ADD;
+ tex2StageParams.colorArg1 = Gfx::TEX_MIX_ARG_COMPUTED_COLOR;
+ tex2StageParams.colorArg2 = Gfx::TEX_MIX_ARG_TEXTURE;
+ tex2StageParams.alphaOperation = Gfx::TEX_MIX_OPER_DEFAULT;
+ device->SetTextureStageParams(1, tex2StageParams);
+
+ device->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLES, quad, 6);
+
+ device->EndScene();
+}
+
+int main()
+{
+ CLogger();
+
+ // Without any error checking, for simplicity
+
+ SDL_Init(SDL_INIT_VIDEO);
+
+ IMG_Init(IMG_INIT_PNG);
+
+ const SDL_VideoInfo *videoInfo = SDL_GetVideoInfo();
+
+ Uint32 videoFlags = SDL_OPENGL | SDL_GL_DOUBLEBUFFER | SDL_HWPALETTE;
+
+ if (videoInfo->hw_available)
+ videoFlags |= SDL_HWSURFACE;
+ else
+ videoFlags |= SDL_SWSURFACE;
+
+ if (videoInfo->blit_hw)
+ videoFlags |= SDL_HWACCEL;
+
+
+ SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
+
+ SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 8);
+
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+
+ SDL_Surface *surface = SDL_SetVideoMode(800, 600, 32, videoFlags);
+
+
+ SDL_WM_SetCaption("Texture Test", "Texture Test");
+
+ Gfx::CGLDevice *device = new Gfx::CGLDevice();
+ device->Create();
+
+ Init(device);
+
+ bool done = false;
+ while (! done)
+ {
+ Render(device);
+
+ SDL_GL_SwapBuffers();
+
+ SDL_Event event;
+ SDL_PollEvent(&event);
+ if (event.type == SDL_QUIT)
+ done = true;
+
+ usleep(10000);
+ }
+
+ device->Destroy();
+ delete device;
+
+ SDL_FreeSurface(surface);
+
+ IMG_Quit();
+
+ SDL_Quit();
+
+ return 0;
+}
diff --git a/src/graphics/opengl/test/transform_test.cpp b/src/graphics/opengl/test/transform_test.cpp
new file mode 100644
index 0000000..83819b8
--- /dev/null
+++ b/src/graphics/opengl/test/transform_test.cpp
@@ -0,0 +1,339 @@
+#include "app/system.h"
+#include "common/logger.h"
+#include "common/image.h"
+#include "common/iman.h"
+#include "graphics/opengl/gldevice.h"
+#include "math/geometry.h"
+
+#include <SDL/SDL.h>
+#include <SDL/SDL_image.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <map>
+
+enum KeySlots
+{
+ K_Forward,
+ K_Back,
+ K_Left,
+ K_Right,
+ K_Up,
+ K_Down,
+ K_Count
+};
+bool KEYMAP[K_Count] = { false };
+
+Math::Point MOUSE_POS_BASE;
+
+Math::Vector TRANSLATION(0.0f, 2.0f, 0.0f);
+Math::Vector ROTATION, ROTATION_BASE;
+
+const int FRAME_DELAY = 5000;
+
+SystemTimeStamp *PREV_TIME = NULL, *CURR_TIME = NULL;
+
+void Init(Gfx::CGLDevice *device)
+{
+ device->SetRenderState(Gfx::RENDER_STATE_DEPTH_TEST, true);
+ device->SetShadeModel(Gfx::SHADE_SMOOTH);
+}
+
+void Render(Gfx::CGLDevice *device)
+{
+ device->BeginScene();
+
+ Math::Matrix persp;
+ Math::LoadProjectionMatrix(persp, Math::PI / 4.0f, (800.0f) / (600.0f), 0.1f, 100.0f);
+ device->SetTransform(Gfx::TRANSFORM_PROJECTION, persp);
+
+
+ Math::Matrix viewMat;
+ Math::Matrix mat;
+
+ viewMat.LoadIdentity();
+
+ Math::LoadRotationXMatrix(mat, -ROTATION.x);
+ viewMat = Math::MultiplyMatrices(viewMat, mat);
+
+ Math::LoadRotationYMatrix(mat, -ROTATION.y);
+ viewMat = Math::MultiplyMatrices(viewMat, mat);
+
+ Math::LoadTranslationMatrix(mat, -TRANSLATION);
+ viewMat = Math::MultiplyMatrices(viewMat, mat);
+
+ device->SetTransform(Gfx::TRANSFORM_VIEW, viewMat);
+
+
+ Math::Matrix worldMat;
+ worldMat.LoadIdentity();
+ device->SetTransform(Gfx::TRANSFORM_WORLD, worldMat);
+
+ Gfx::VertexCol line[2] = { Gfx::VertexCol() };
+
+ for (int x = -40; x <= 40; ++x)
+ {
+ line[0].color = Gfx::Color(0.7f + x / 120.0f, 0.0f, 0.0f);
+ line[0].coord.z = -40;
+ line[0].coord.x = x;
+ line[1].color = Gfx::Color(0.7f + x / 120.0f, 0.0f, 0.0f);
+ line[1].coord.z = 40;
+ line[1].coord.x = x;
+ device->DrawPrimitive(Gfx::PRIMITIVE_LINES, line, 2);
+ }
+
+ for (int z = -40; z <= 40; ++z)
+ {
+ line[0].color = Gfx::Color(0.0f, 0.7f + z / 120.0f, 0.0f);
+ line[0].coord.z = z;
+ line[0].coord.x = -40;
+ line[1].color = Gfx::Color(0.0f, 0.7f + z / 120.0f, 0.0f);
+ line[1].coord.z = z;
+ line[1].coord.x = 40;
+ device->DrawPrimitive(Gfx::PRIMITIVE_LINES, line, 2);
+ }
+
+
+ Gfx::VertexCol quad[6] = { Gfx::VertexCol() };
+
+ for (int i = 0; i < 6; ++i)
+ quad[i].color = Gfx::Color(1.0f, 1.0f, 0.0f);
+
+ quad[0].coord = Math::Vector(-1.0f, -1.0f, 0.0f);
+ quad[1].coord = Math::Vector( 1.0f, -1.0f, 0.0f);
+ quad[2].coord = Math::Vector( 1.0f, 1.0f, 0.0f);
+ quad[3].coord = Math::Vector( 1.0f, 1.0f, 0.0f);
+ quad[4].coord = Math::Vector(-1.0f, 1.0f, 0.0f);
+ quad[5].coord = Math::Vector(-1.0f, -1.0f, 0.0f);
+
+ Math::LoadTranslationMatrix(worldMat, Math::Vector(40.0f, 2.0f, 40.0f));
+ device->SetTransform(Gfx::TRANSFORM_WORLD, worldMat);
+
+ device->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLES, quad, 6);
+
+ for (int i = 0; i < 6; ++i)
+ quad[i].color = Gfx::Color(0.0f, 1.0f, 1.0f);
+
+ Math::LoadTranslationMatrix(worldMat, Math::Vector(-40.0f, 2.0f, -40.0f));
+ device->SetTransform(Gfx::TRANSFORM_WORLD, worldMat);
+
+ device->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLES, quad, 6);
+
+ for (int i = 0; i < 6; ++i)
+ quad[i].color = Gfx::Color(1.0f, 0.0f, 1.0f);
+
+ Math::LoadTranslationMatrix(worldMat, Math::Vector(0.0f, 10.0f, 0.0f));
+ device->SetTransform(Gfx::TRANSFORM_WORLD, worldMat);
+
+ device->DrawPrimitive(Gfx::PRIMITIVE_TRIANGLES, quad, 6);
+
+ device->EndScene();
+}
+
+void Update()
+{
+ const float TRANS_SPEED = 6.0f; // units / sec
+
+ GetCurrentTimeStamp(CURR_TIME);
+ float timeDiff = TimeStampDiff(PREV_TIME, CURR_TIME, STU_SEC);
+ CopyTimeStamp(PREV_TIME, CURR_TIME);
+
+ Math::Vector incTrans;
+
+ if (KEYMAP[K_Forward])
+ incTrans.z = +TRANS_SPEED * timeDiff;
+ if (KEYMAP[K_Back])
+ incTrans.z = -TRANS_SPEED * timeDiff;
+ if (KEYMAP[K_Right])
+ incTrans.x = +TRANS_SPEED * timeDiff;
+ if (KEYMAP[K_Left])
+ incTrans.x = -TRANS_SPEED * timeDiff;
+ if (KEYMAP[K_Up])
+ incTrans.y = +TRANS_SPEED * timeDiff;
+ if (KEYMAP[K_Down])
+ incTrans.y = -TRANS_SPEED * timeDiff;
+
+ Math::Point rotTrans = Math::RotatePoint(-ROTATION.y, Math::Point(incTrans.x, incTrans.z));
+ incTrans.x = rotTrans.x;
+ incTrans.z = rotTrans.y;
+ TRANSLATION += incTrans;
+}
+
+void KeyboardDown(SDLKey key)
+{
+ switch (key)
+ {
+ case SDLK_w:
+ KEYMAP[K_Forward] = true;
+ break;
+ case SDLK_s:
+ KEYMAP[K_Back] = true;
+ break;
+ case SDLK_d:
+ KEYMAP[K_Right] = true;
+ break;
+ case SDLK_a:
+ KEYMAP[K_Left] = true;
+ break;
+ case SDLK_z:
+ KEYMAP[K_Down] = true;
+ break;
+ case SDLK_x:
+ KEYMAP[K_Up] = true;
+ break;
+ default:
+ break;
+ }
+}
+
+void KeyboardUp(SDLKey key)
+{
+ switch (key)
+ {
+ case SDLK_w:
+ KEYMAP[K_Forward] = false;
+ break;
+ case SDLK_s:
+ KEYMAP[K_Back] = false;
+ break;
+ case SDLK_d:
+ KEYMAP[K_Right] = false;
+ break;
+ case SDLK_a:
+ KEYMAP[K_Left] = false;
+ break;
+ case SDLK_z:
+ KEYMAP[K_Down] = false;
+ break;
+ case SDLK_x:
+ KEYMAP[K_Up] = false;
+ break;
+ default:
+ break;
+ }
+}
+
+void MouseMove(int x, int y)
+{
+ Math::Point currentPos((float)x, (float)y);
+
+ static bool first = true;
+ if (first || (x < 10) || (y < 10) || (x > 790) || (y > 590))
+ {
+ SDL_WarpMouse(400, 300);
+ MOUSE_POS_BASE.x = 400;
+ MOUSE_POS_BASE.y = 300;
+ ROTATION_BASE = ROTATION;
+ first = false;
+ return;
+ }
+
+ ROTATION.y = ROTATION_BASE.y + ((float) (x - MOUSE_POS_BASE.x) / 800.0f) * Math::PI;
+ ROTATION.x = ROTATION_BASE.x + ((float) (y - MOUSE_POS_BASE.y) / 600.0f) * Math::PI;
+}
+
+int main(int argc, char *argv[])
+{
+ CLogger logger;
+
+ PREV_TIME = CreateTimeStamp();
+ CURR_TIME = CreateTimeStamp();
+
+ GetCurrentTimeStamp(PREV_TIME);
+ GetCurrentTimeStamp(CURR_TIME);
+
+ CInstanceManager iMan;
+
+ // Without any error checking, for simplicity
+
+ SDL_Init(SDL_INIT_VIDEO);
+
+ IMG_Init(IMG_INIT_PNG);
+
+ const SDL_VideoInfo *videoInfo = SDL_GetVideoInfo();
+
+ Uint32 videoFlags = SDL_OPENGL | SDL_GL_DOUBLEBUFFER | SDL_HWPALETTE;
+
+ if (videoInfo->hw_available)
+ videoFlags |= SDL_HWSURFACE;
+ else
+ videoFlags |= SDL_SWSURFACE;
+
+ if (videoInfo->blit_hw)
+ videoFlags |= SDL_HWACCEL;
+
+
+ SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
+
+ SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 8);
+
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+
+ SDL_Surface *surface = SDL_SetVideoMode(800, 600, 32, videoFlags);
+
+
+ SDL_WM_SetCaption("Transform Test", "Transform Test");
+
+ //SDL_WM_GrabInput(SDL_GRAB_ON);
+ SDL_ShowCursor(SDL_DISABLE);
+
+ Gfx::CGLDevice *device = new Gfx::CGLDevice();
+ device->Create();
+
+ Init(device);
+
+ bool done = false;
+ while (! done)
+ {
+ Render(device);
+ Update();
+
+ SDL_GL_SwapBuffers();
+
+ SDL_Event event;
+ while (SDL_PollEvent(&event))
+ {
+ if (event.type == SDL_QUIT)
+ {
+ break;
+ done = true;
+ }
+ else if (event.type == SDL_KEYDOWN)
+ {
+ if (event.key.keysym.sym == SDLK_q)
+ {
+ done = true;
+ break;
+ }
+ else
+ KeyboardDown(event.key.keysym.sym);
+ }
+ else if (event.type == SDL_KEYUP)
+ KeyboardUp(event.key.keysym.sym);
+ else if (event.type == SDL_MOUSEMOTION)
+ MouseMove(event.motion.x, event.motion.y);
+ }
+
+ usleep(FRAME_DELAY);
+ }
+
+ //SDL_WM_GrabInput(SDL_GRAB_OFF);
+ SDL_ShowCursor(SDL_ENABLE);
+
+ device->Destroy();
+ delete device;
+
+ SDL_FreeSurface(surface);
+
+ IMG_Quit();
+
+ SDL_Quit();
+
+ DestroyTimeStamp(PREV_TIME);
+ DestroyTimeStamp(CURR_TIME);
+
+ return 0;
+}