From a2ae98098676875bd601c230ca1f68ee2943f777 Mon Sep 17 00:00:00 2001 From: Mark Vejvoda Date: Tue, 18 Oct 2011 04:32:02 +0000 Subject: [PATCH] - added md5 code to project (but not actively used, only compiled) --- .../include/graphics/md5/ArbProgram.h | 141 ++ .../include/graphics/md5/DataManager.h | 101 + .../include/graphics/md5/DataManager.inl | 169 ++ .../include/graphics/md5/GlErrors.h | 27 + .../shared_lib/include/graphics/md5/Image.h | 539 ++++ .../shared_lib/include/graphics/md5/Mathlib.h | 431 ++++ .../include/graphics/md5/Mathlib.inl | 2158 +++++++++++++++++ .../include/graphics/md5/Md5Model.h | 522 ++++ .../shared_lib/include/graphics/md5/Shader.h | 171 ++ .../shared_lib/include/graphics/md5/Texture.h | 200 ++ .../include/graphics/md5/TextureManager.h | 73 + .../shared_lib/include/graphics/md5/md5util.h | 34 + .../sources/graphics/md5/ArbProgram.cpp | 262 ++ .../sources/graphics/md5/GlErrors.cpp | 53 + .../shared_lib/sources/graphics/md5/Image.cpp | 1516 ++++++++++++ .../sources/graphics/md5/Md5Model.cpp | 1935 +++++++++++++++ .../sources/graphics/md5/Shader.cpp | 454 ++++ .../sources/graphics/md5/Texture.cpp | 516 ++++ .../sources/graphics/md5/md5util.cpp | 569 +++++ .../sources/platform/miniupnpc/LICENCE | 26 - 20 files changed, 9871 insertions(+), 26 deletions(-) create mode 100644 source/shared_lib/include/graphics/md5/ArbProgram.h create mode 100644 source/shared_lib/include/graphics/md5/DataManager.h create mode 100644 source/shared_lib/include/graphics/md5/DataManager.inl create mode 100644 source/shared_lib/include/graphics/md5/GlErrors.h create mode 100644 source/shared_lib/include/graphics/md5/Image.h create mode 100644 source/shared_lib/include/graphics/md5/Mathlib.h create mode 100644 source/shared_lib/include/graphics/md5/Mathlib.inl create mode 100644 source/shared_lib/include/graphics/md5/Md5Model.h create mode 100644 source/shared_lib/include/graphics/md5/Shader.h create mode 100644 source/shared_lib/include/graphics/md5/Texture.h create mode 100644 source/shared_lib/include/graphics/md5/TextureManager.h create mode 100644 source/shared_lib/include/graphics/md5/md5util.h create mode 100644 source/shared_lib/sources/graphics/md5/ArbProgram.cpp create mode 100644 source/shared_lib/sources/graphics/md5/GlErrors.cpp create mode 100644 source/shared_lib/sources/graphics/md5/Image.cpp create mode 100644 source/shared_lib/sources/graphics/md5/Md5Model.cpp create mode 100644 source/shared_lib/sources/graphics/md5/Shader.cpp create mode 100644 source/shared_lib/sources/graphics/md5/Texture.cpp create mode 100644 source/shared_lib/sources/graphics/md5/md5util.cpp delete mode 100644 source/shared_lib/sources/platform/miniupnpc/LICENCE diff --git a/source/shared_lib/include/graphics/md5/ArbProgram.h b/source/shared_lib/include/graphics/md5/ArbProgram.h new file mode 100644 index 00000000..0ba07283 --- /dev/null +++ b/source/shared_lib/include/graphics/md5/ArbProgram.h @@ -0,0 +1,141 @@ +// ============================================================== +// This file is part of MegaGlest (www.glest.org) +// +// ArbProgram.h -- Copyright (c) 2007 David Henry +// changed for use with MegaGlest: Copyright (C) 2011- by Mark Vejvoda +// +// This code is licenced under the MIT license. +// +// This software is provided "as is" without express or implied +// warranties. You may freely copy and compile this source into +// applications you distribute provided that the copyright text +// below is included in the resulting source code. +// +// Definitions of ARB program related classes. +// +///////////////////////////////////////////////////////////////////////////// +#ifndef __ARB_PROGRAM_H__ +#define __ARB_PROGRAM_H__ + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif // _WIN32 + +#include +#include +#include + +namespace Shared { namespace Graphics { namespace md5 { + +using std::string; + +///////////////////////////////////////////////////////////////////////////// +// ARB Program class diagram: +// +// +---------- (abs) +// | ArbProgram | +// +--------------+ +// ^ +// | +// +-------------+-------------+ +// | | +// +------------------+ +--------------------+ +// | ArbVertexProgram | | ArbFragmentProgram | +// +------------------+ +--------------------+ +// +///////////////////////////////////////////////////////////////////////////// + +// Global functions for initializing ARB program extensions and query for +// vertex and fragment program support on the host. +GLboolean hasArbVertexProgramSupport(); +GLboolean hasArbFragmentProgramSupport(); + +void initArbProgramHandling(); + +///////////////////////////////////////////////////////////////////////////// +// +// class ArbProgram -- ARB Program abstract object. Can be a vertex program +// or a fragment program. +// +///////////////////////////////////////////////////////////////////////////// +class ArbProgram +{ +protected: + // Constructor + ArbProgram (const string &filename); + +public: + // Destructor + virtual ~ArbProgram (); + +public: + // Accessors + const string &name () const { return _name; } + const string &code () const { return _code; } + GLuint handle () const { return _handle; } + bool fail () const { return _fail; } + + virtual GLenum programType () const = 0; + +public: + // Public interface + void use () const; + void unuse () const; + +protected: + // Internal functions + void printProgramString (int errPos); + void load () + throw (std::runtime_error); + void loadProgramFile (const string &filename) + throw (std::runtime_error); + +protected: + // Member variables + string _name; + string _code; + GLuint _handle; + GLboolean _fail; +}; + +///////////////////////////////////////////////////////////////////////////// +// +// class ArbVertexProgram -- ARB vertex program object. +// +///////////////////////////////////////////////////////////////////////////// +class ArbVertexProgram : public ArbProgram +{ +public: + // Constructor + ArbVertexProgram (const string &filename); + +public: + // Return the program enum type + virtual GLenum programType () const { + return GL_VERTEX_PROGRAM_ARB; + } +}; + + +///////////////////////////////////////////////////////////////////////////// +// +// class ArbFragmentProgram -- ARB fragment program object. +// +///////////////////////////////////////////////////////////////////////////// +class ArbFragmentProgram : public ArbProgram +{ +public: + // Constructor + ArbFragmentProgram (const string &filename); + +public: + // Return the program enum type + virtual GLenum programType () const { + return GL_FRAGMENT_PROGRAM_ARB; + } +}; + +}}} //end namespace + +#endif // __ARB_PROGRAM_H__ diff --git a/source/shared_lib/include/graphics/md5/DataManager.h b/source/shared_lib/include/graphics/md5/DataManager.h new file mode 100644 index 00000000..e01f28f9 --- /dev/null +++ b/source/shared_lib/include/graphics/md5/DataManager.h @@ -0,0 +1,101 @@ +// ============================================================== +// This file is part of MegaGlest (www.glest.org) +// +// DataManager.h -- Copyright (c) 2006 David Henry +// changed for use with MegaGlest: Copyright (C) 2011- by Mark Vejvoda +// +// This code is licenced under the MIT license. +// +// This software is provided "as is" without express or implied +// warranties. You may freely copy and compile this source into +// applications you distribute provided that the copyright text +// below is included in the resulting source code. +// +// Definitions of a data manager class. +// +///////////////////////////////////////////////////////////////////////////// +#ifndef __DATAMANAGER_H__ +#define __DATAMANAGER_H__ + +#include +#include +#include + +using std::string; +using std::map; + +namespace Shared { namespace Graphics { namespace md5 { + +///////////////////////////////////////////////////////////////////////////// +// +// class DataManagerException - Exception class for DataManager classes. +// This acts like a standard runtime_error exception but +// know the name of the resource which caused the exception. +// +///////////////////////////////////////////////////////////////////////////// +class DataManagerException : public std::runtime_error { +public: + // Constructors + DataManagerException (const string &error) + : std::runtime_error (error) { } + DataManagerException (const string &error, const string &name) + : std::runtime_error (error), _which (name) { } + virtual ~DataManagerException () throw () { } + +public: + // Public interface + virtual const char *which () const throw () { + return _which.c_str (); + } + +private: + // Member variables + string _which; +}; + +///////////////////////////////////////////////////////////////////////////// +// +// class DataManager -- a data manager which can register/unregister +// generic objects. Destroy all registred objects at death. +// +// The data manager is a singleton. +// +///////////////////////////////////////////////////////////////////////////// +template +class DataManager { +protected: + // Constructor/destructor + DataManager (); + virtual ~DataManager (); + +public: + // Public interface + T *request (const string &name); + + void registerObject (const string &name, T *object) + throw (DataManagerException); + void unregisterObject (const string &name, bool deleteObject = false); + + void purge (); + +private: + // Member variables + typedef map DataMap; + DataMap _registry; + +public: + // Singleton related functions + static C *getInstance (); + static void kill (); + +private: + // The unique instance of this class + static C *_singleton; +}; + +// Include inline function definitions +#include "DataManager.inl" + +}}} //end namespace + +#endif // __DATAMANAGER_H__ diff --git a/source/shared_lib/include/graphics/md5/DataManager.inl b/source/shared_lib/include/graphics/md5/DataManager.inl new file mode 100644 index 00000000..7c7989c5 --- /dev/null +++ b/source/shared_lib/include/graphics/md5/DataManager.inl @@ -0,0 +1,169 @@ +// ============================================================== +// This file is part of MegaGlest (www.glest.org) +// +// DataManager.inl -- Copyright (c) 2006 David Henry +// changed for use with MegaGlest: Copyright (C) 2011- by Mark Vejvoda +// +// This code is licenced under the MIT license. +// +// This software is provided "as is" without express or implied +// warranties. You may freely copy and compile this source into +// applications you distribute provided that the copyright text +// below is included in the resulting source code. +// +// Implementation of the data manager. +// +///////////////////////////////////////////////////////////////////////////// + +#include "DataManager.h" + +///////////////////////////////////////////////////////////////////////////// +// +// class DataManager implementation. +// +///////////////////////////////////////////////////////////////////////////// + +// Singleton initialization. At first, there is no object created. +template +C *DataManager::_singleton = NULL; + +// -------------------------------------------------------------------------- +// DataManager::DataManager +// +// Constructor. +// -------------------------------------------------------------------------- + +template +inline +DataManager::DataManager () { +} + +// -------------------------------------------------------------------------- +// DataManager::~DataManager +// +// Destructor. Purge all registred objects. +// -------------------------------------------------------------------------- +template +inline +DataManager::~DataManager () { + purge (); +} + +// -------------------------------------------------------------------------- +// DataManager::request +// +// Retrieve an object from the registry. Return NULL if there if the +// requested object has not been found in the registry. +// -------------------------------------------------------------------------- +template +inline T * +DataManager::request (const string &name) { + typename DataMap::iterator itor; + itor = _registry.find (name); + + if (itor != _registry.end ()) + { + // The object has been found + return itor->second; + } + else + { + return NULL; + } +} + + +// -------------------------------------------------------------------------- +// DataManager::registerObject +// +// Register an object. If kOverWrite is set, then it will overwrite +// the already existing object. If kOverWrite is combined +// with kDelete, then it will also delete the previous object from memory. +// -------------------------------------------------------------------------- +template +inline void +DataManager::registerObject (const string &name, T *object) + throw (DataManagerException) { + std::pair res; + + // Register the object as a new entry + res = _registry.insert (typename DataMap::value_type (name, object)); + + // Throw an exception if the insertion failed + if (!res.second) + throw DataManagerException ("Name collision", name); +} + +// -------------------------------------------------------------------------- +// DataManager::unregisterObject +// +// Unregister an object given its name. If deleteObject is true, +// then it delete the object, otherwise it just remove the object +// from the registry whitout freeing it from memory. +// -------------------------------------------------------------------------- +template +inline void +DataManager::unregisterObject (const string &name, bool deleteObject) { + typename DataMap::iterator itor; + itor = _registry.find (name); + + if (itor != _registry.end ()) + { + if (deleteObject) + delete itor->second; + + _registry.erase (itor); + } +} + +// -------------------------------------------------------------------------- +// DataManager::purge +// +// Destroy all registred objects and clear the registry. +// -------------------------------------------------------------------------- +template +inline void +DataManager::purge () { + // Not exception safe! + for (typename DataMap::iterator itor = _registry.begin (); + itor != _registry.end (); ++itor) + { + // Destroy object + delete itor->second; + } + + _registry.clear (); +} + +// -------------------------------------------------------------------------- +// DataManager::getInstance +// +// Return a pointer of the unique instance of this class. If there is no +// object build yet, create it. +// NOTE: This is the only way to get access to the data manager since +// constructor is private. +// -------------------------------------------------------------------------- +template +inline C * +DataManager::getInstance () { + if (_singleton == NULL) + _singleton = new C; + + return _singleton; +} + +// -------------------------------------------------------------------------- +// DataManager::kill +// +// Destroy the data manager, i.e. delete the unique instance of +// this class. +// NOTE: this function must be called before exiting in order to +// properly destroy all registred objects. +// -------------------------------------------------------------------------- + +template +inline void +DataManager::kill () { + delete _singleton; + _singleton = NULL; +} diff --git a/source/shared_lib/include/graphics/md5/GlErrors.h b/source/shared_lib/include/graphics/md5/GlErrors.h new file mode 100644 index 00000000..42220f4f --- /dev/null +++ b/source/shared_lib/include/graphics/md5/GlErrors.h @@ -0,0 +1,27 @@ +// ============================================================== +// This file is part of MegaGlest (www.glest.org) +// +// GlErrors.h -- Copyright (c) 2006-2007 David Henry +// changed for use with MegaGlest: Copyright (C) 2011- by Mark Vejvoda +// +// This code is licenced under the MIT license. +// +// This software is provided "as is" without express or implied +// warranties. You may freely copy and compile this source into +// applications you distribute provided that the copyright text +// below is included in the resulting source code. +// +// OpenGL error management. +// +///////////////////////////////////////////////////////////////////////////// + +#ifndef __GLERRORS_H__ +#define __GLERRORS_H__ + +namespace Shared { namespace Graphics { namespace md5 { + +GLenum checkOpenGLErrors (const char *file, int line); + +}}} //end namespace + +#endif // __GLERRORS_H__ diff --git a/source/shared_lib/include/graphics/md5/Image.h b/source/shared_lib/include/graphics/md5/Image.h new file mode 100644 index 00000000..880c7c81 --- /dev/null +++ b/source/shared_lib/include/graphics/md5/Image.h @@ -0,0 +1,539 @@ +// ============================================================== +// This file is part of MegaGlest (www.glest.org) +// +// Image.h -- Copyright (c) 2006 David Henry +// changed for use with MegaGlest: Copyright (C) 2011- by Mark Vejvoda +// +// This code is licenced under the MIT license. +// +// This software is provided "as is" without express or implied +// warranties. You may freely copy and compile this source into +// applications you distribute provided that the copyright text +// below is included in the resulting source code. +// +// Declaration of DDS, TGA, PCX, JPEG and PNG image loader classes. +// +///////////////////////////////////////////////////////////////////////////// +#ifndef __IMAGE_H__ +#define __IMAGE_H__ + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif // _WIN32 + +#include +//#include +#include +#include +#include +#include +#include +#include + +extern "C" { +#include +} + +using std::cout; +using std::endl; +using std::string; + +namespace Shared { namespace Graphics { namespace md5 { + +//using boost::shared_ptr; +using std::tr1::shared_ptr; + +///////////////////////////////////////////////////////////////////////////// +// Image class diagram: +// +// +------- (abs) +---------------+ +// | Image | | runtime_error | +// +---------+ +---------------+ +// ^ ^ +// | +------------+ | +// +---| ImageDDS | +----------------+ +// | +------------+ | ImageException | +// | +----------------+ +// | +------------+ +// +---| ImageTGA | +// | +------------+ +// | +---------------+ +// | +------------+ | ImageBuffer | +// +---| ImagePCX | +---------------+ +// | +------------+ +// | +// | +------------+ +// +---| ImageJPEG | +----------------+ +// | +------------+ | ImageFactory | +// | +----------------+ +// | +------------+ +// +---| ImagePNG | +// +------------+ +// +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// +// class ImageException - Exception class for ImageBuffer and Image +// loaders. This acts like a standard runtime_error exception but +// know which file has failed to be loaded. +// +///////////////////////////////////////////////////////////////////////////// +class ImageException : public std::runtime_error { +public: + // Constructors + ImageException (const string &error) + : std::runtime_error (error) { } + ImageException (const string &error, const string &filename) + : std::runtime_error (error), _which (filename) { } + virtual ~ImageException () throw () { } + +public: + // Public interface + virtual const char *which () const throw () { + return _which.c_str (); + } + +private: + // Member variables + string _which; +}; + +///////////////////////////////////////////////////////////////////////////// +// +// class ImageBuffer - An image file loader class. Load a whole file +// into a memory buffer. +// +///////////////////////////////////////////////////////////////////////////// +class ImageBuffer { +public: + // Constructors/destructor + ImageBuffer (const string &filename); + + ImageBuffer (const ImageBuffer &that) + : _filename (that._filename), _data (NULL), _length (that._length) + { + _data = new GLubyte[_length]; + memcpy (_data, that._data, _length); + } + + ~ImageBuffer (); + +private: + // Disable default constructor + ImageBuffer (); + +public: + // Accessors + const string &filename () const { return _filename; } + const GLubyte *data () const { return _data; } + size_t length () const { return _length; } + + ImageBuffer &operator= (const ImageBuffer &rhs) + { + this->~ImageBuffer (); + new (this) ImageBuffer (rhs); + return *this; + } + +private: + // Member variables + string _filename; + + GLubyte *_data; + size_t _length; +}; + +///////////////////////////////////////////////////////////////////////////// +// +// class Image - A generic image loader class for creating OpenGL +// textures from. All other specific image loader are derived from it. +// +///////////////////////////////////////////////////////////////////////////// +class Image { +protected: + // Default constructor + Image () + : _width (0), _height (0), _numMipmaps (0), + _format (0), _components (0), _pixels (NULL), + _standardCoordSystem (true) { } + +private: + // Disable copy constructor. + Image (const Image &img); + +public: + // Constructors/destructor + Image (const string &name, GLsizei w, GLsizei h, GLint numMipMaps, + GLenum format, GLint components, const GLubyte *pixels, + bool stdCoordSystem); + + virtual ~Image(); + +public: + // Return true if we're working with S3 + // compressed textures (DDS files) + bool isCompressed () const; + bool isPowerOfTwo () const; + + // Accessors + GLsizei width () const { return _width; } + GLsizei height () const { return _height; } + GLint numMipmaps () const { return _numMipmaps; } + GLenum format () const { return _format; } + GLint components () const { return _components; } + const GLubyte *pixels () const { return _pixels; } + const string &name () const { return _name; } + bool stdCoordSystem () const { return _standardCoordSystem; } + +protected: + // Member variables + GLsizei _width; + GLsizei _height; + GLint _numMipmaps; + + // OpenGL texture format and internal + // format (components) + GLenum _format; + GLint _components; + + // Image data + GLubyte *_pixels; + + string _name; + + // Is the picture in standard OpenGL 2D coordinate + // system? (starts lower-left corner) + bool _standardCoordSystem; +}; + + +// Definition of type aliases +typedef shared_ptr ImagePtr; + +///////////////////////////////////////////////////////////////////////////// +// +// class ImageDDS - A DirectDraw Surface (DDS) image loader class. +// Support only DXT1, DXT3 and DXT5 formats, since OpenGL doesn't +// support others. GL_EXT_texture_compression_s3tc must be supported. +// +// NOTE: Because DirectX uses the upper left corner as origin of the +// picture when OpenGL uses the lower left corner, the texture generated +// from it will be rendered upside-down. +// +///////////////////////////////////////////////////////////////////////////// +class ImageDDS : public Image { +public: + // Constructor + ImageDDS (const ImageBuffer &ibuff); + +private: + // Internal data structures and + // member variables + struct DDPixelFormat + { + GLuint size; + GLuint flags; + GLuint fourCC; + GLuint bpp; + GLuint redMask; + GLuint greenMask; + GLuint blueMask; + GLuint alphaMask; + }; + + struct DDSCaps + { + GLuint caps; + GLuint caps2; + GLuint caps3; + GLuint caps4; + }; + + struct DDColorKey + { + GLuint lowVal; + GLuint highVal; + }; + + struct DDSurfaceDesc + { + GLuint size; + GLuint flags; + GLuint height; + GLuint width; + GLuint pitch; + GLuint depth; + GLuint mipMapLevels; + GLuint alphaBitDepth; + GLuint reserved; + GLuint surface; + + DDColorKey ckDestOverlay; + DDColorKey ckDestBlt; + DDColorKey ckSrcOverlay; + DDColorKey ckSrcBlt; + + DDPixelFormat format; + DDSCaps caps; + + GLuint textureStage; + }; + + const DDSurfaceDesc *_ddsd; +}; + +///////////////////////////////////////////////////////////////////////////// +// +// class glImageTGA - A TrueVision TARGA (TGA) image loader class. +// Support 24-32 bits BGR files; 16 bits RGB; 8 bits indexed (BGR +// palette); 8 and 16 bits grayscale; all compressed and uncompressed. +// Compressed TGA images use RLE algorithm. +// +///////////////////////////////////////////////////////////////////////////// +class ImageTGA : public Image { +public: + // Constructor + ImageTGA (const ImageBuffer &ibuff); + +private: + // Internal functions + void getTextureInfo (); + + void readTGA8bits (const GLubyte *data, const GLubyte *colormap); + void readTGA16bits (const GLubyte *data); + void readTGA24bits (const GLubyte *data); + void readTGA32bits (const GLubyte *data); + void readTGAgray8bits (const GLubyte *data); + void readTGAgray16bits (const GLubyte *data); + + void readTGA8bitsRLE (const GLubyte *data, const GLubyte *colormap); + void readTGA16bitsRLE (const GLubyte *data); + void readTGA24bitsRLE (const GLubyte *data); + void readTGA32bitsRLE (const GLubyte *data); + void readTGAgray8bitsRLE (const GLubyte *data); + void readTGAgray16bitsRLE (const GLubyte *data); + +private: + // Member variables +#pragma pack(push, 1) + // tga header + struct TGA_Header + { + GLubyte id_lenght; // size of image id + GLubyte colormap_type; // 1 is has a colormap + GLubyte image_type; // compression type + + short cm_first_entry; // colormap origin + short cm_length; // colormap length + GLubyte cm_size; // colormap size + + short x_origin; // bottom left x coord origin + short y_origin; // bottom left y coord origin + + short width; // picture width (in pixels) + short height; // picture height (in pixels) + + GLubyte pixel_depth; // bits per pixel: 8, 16, 24 or 32 + GLubyte image_descriptor; // 24 bits = 0x00; 32 bits = 0x80 + + }; +#pragma pack(pop) + + const TGA_Header *_header; + + // NOTE: + // 16 bits images are stored in RGB + // 8-24-32 images are stored in BGR(A) + + // RGBA/BGRA component table access -- usefull for + // switching from bgra to rgba at load time. + static int rgbaTable[4]; // bgra to rgba: 2, 1, 0, 3 + static int bgraTable[4]; // bgra to bgra: 0, 1, 2, 3 +}; + +///////////////////////////////////////////////////////////////////////////// +// +// class ImagePCX - A Zsoft PCX image loader class. +// +///////////////////////////////////////////////////////////////////////////// +class ImagePCX : public Image { +public: + // Constructor + ImagePCX (const ImageBuffer &ibuff); + +private: + // Internal functions + void readPCX1bit (const GLubyte *data); + void readPCX4bits (const GLubyte *data); + void readPCX8bits (const GLubyte *data, + const GLubyte *palette); + void readPCX24bits (const GLubyte *data); + +private: +#pragma pack(push, 1) + // pcx header + struct PCX_Header + { + GLubyte manufacturer; + GLubyte version; + GLubyte encoding; + GLubyte bitsPerPixel; + + GLushort xmin, ymin; + GLushort xmax, ymax; + GLushort horzRes, vertRes; + + GLubyte palette[48]; + GLubyte reserved; + GLubyte numColorPlanes; + + GLushort bytesPerScanLine; + GLushort paletteType; + GLushort horzSize, vertSize; + + GLubyte padding[54]; + }; +#pragma pack(pop) + + const PCX_Header *_header; + + // RGBA/BGRA component table access -- usefull for + // switching from bgra to rgba at load time. + static int rgbTable[3]; // bgra to rgba: 0, 1, 2 + static int bgrTable[3]; // bgra to bgra: 2, 1, 0 +}; + +///////////////////////////////////////////////////////////////////////////// +// +// class ImageJPEG - A JPEG image loader class using libjpeg. +// +///////////////////////////////////////////////////////////////////////////// +class ImageJPEG : public Image { +public: + // Constructor + ImageJPEG (const ImageBuffer &ibuff); + +private: + // Error manager, using C's setjmp/longjmp + struct my_error_mgr + { + jpeg_error_mgr pub; // "public" fields + jmp_buf setjmp_buffer; // for return to caller + + string errorMsg; // last error message + }; + + typedef my_error_mgr *my_error_ptr; + +private: + // libjpeg's callback functions for reading data + static void initSource_callback (j_decompress_ptr cinfo); + static boolean fillInputBuffer_callback (j_decompress_ptr cinfo); + static void skipInputData_callback (j_decompress_ptr cinfo, + long num_bytes); + static void termSource_callback (j_decompress_ptr cinfo); + + // libjpeg's callback functions for error handling + static void errorExit_callback (j_common_ptr cinfo); + static void outputMessage_callback (j_common_ptr cinfo); +}; + +///////////////////////////////////////////////////////////////////////////// +// +// class ImagePNG - A Portable Network Graphic (PNG) image loader +// class using libpng and zlib. +// +///////////////////////////////////////////////////////////////////////////// +class ImagePNG : public Image { +public: + // Constructor + ImagePNG (const ImageBuffer &ibuff); + +private: + // Internal functions + void getTextureInfo (int color_type); + + // libpng's callback functions for reading data + // and error handling + static void read_callback (png_structp png_ptr, + png_bytep data, png_size_t length); + static void error_callback (png_structp png_ptr, + png_const_charp error_msg); + static void warning_callback (png_structp png_ptr, + png_const_charp warning_msg); + +public: + // Data source manager. Contains an image buffer and + // an offset position into file's data + struct my_source_mgr + { + // Constructors + my_source_mgr () + : pibuff (NULL), offset (0) { } + my_source_mgr (const ImageBuffer &ibuff) + : pibuff (&ibuff), offset (0) { } + + // Public member variables + const ImageBuffer *pibuff; + size_t offset; + }; + + typedef my_source_mgr *my_source_ptr; + +private: + // Member variables + string errorMsg; +}; + +///////////////////////////////////////////////////////////////////////////// +// +// class ImageFactory - An Image Factory Class. +// +///////////////////////////////////////////////////////////////////////////// +class ImageFactory { +public: + // Public interface + static Image *createImage (const ImageBuffer &ibuff) + { + string ext; + Image *result; + + // Extract file extension + const string &filename = ibuff.filename (); + ext.assign (filename, filename.find_last_of ('.') + 1, string::npos); + + if (ext.compare ("dds") == 0) + { + result = new ImageDDS (ibuff); + } + else if (ext.compare ("tga") == 0) + { + result = new ImageTGA (ibuff); + } + else if (ext.compare ("pcx") == 0) + { + result = new ImagePCX (ibuff); + } + else if (ext.compare ("jpg") == 0) + { + result = new ImageJPEG (ibuff); + } + else if (ext.compare ("png") == 0) + { + result = new ImagePNG (ibuff); + } + else + { + throw ImageException ("Unhandled image file format", filename); + } + + return result; + } +}; + +}}} //end namespace + +#endif // __IMAGE_H__ diff --git a/source/shared_lib/include/graphics/md5/Mathlib.h b/source/shared_lib/include/graphics/md5/Mathlib.h new file mode 100644 index 00000000..49f0856c --- /dev/null +++ b/source/shared_lib/include/graphics/md5/Mathlib.h @@ -0,0 +1,431 @@ +// ============================================================== +// This file is part of MegaGlest (www.glest.org) +// +// Mathlib.h -- Copyright (c) 2005-2006 David Henry +// changed for use with MegaGlest: Copyright (C) 2011- by Mark Vejvoda +// +// This code is licenced under the MIT license. +// +// This software is provided "as is" without express or implied +// warranties. You may freely copy and compile this source into +// applications you distribute provided that the copyright text +// below is included in the resulting source code. +// +// Declarations for 3D maths object and functions to use with OpenGL. +// +// Provide vector, matrix and quaternion operations. +// +///////////////////////////////////////////////////////////////////////////// +#ifndef __MATHLIB_H__ +#define __MATHLIB_H__ + +#include + +namespace Shared { namespace Graphics { namespace md5 { + +// Forward declarations +template class Vector3; +template class Matrix4x4; +template class Quaternion; + +// Type definitions +enum Axis { + kXaxis, kYaxis, kZaxis +}; + +// Declare a global constant for pi and a few multiples. +const float kPi = 3.14159265358979323846f; +const float k2Pi = kPi * 2.0f; +const float kPiOver2 = kPi / 2.0f; +const float k1OverPi = 1.0f / kPi; +const float k1Over2Pi = 1.0f / k2Pi; +const float kPiOver180 = kPi / 180.0f; +const float k180OverPi = 180.0f / kPi; + +// "Wrap" an angle in range -pi...pi by adding the correct multiple +// of 2 pi +template +Real wrapPi (Real theta); + +// "Safe" inverse trig functions +template +Real safeAcos (Real x); + +// Set the Euler angle triple to its "canonical" value +template +void canonizeEulerAngles (Real &roll, Real &pitch, Real &yaw); + +// Convert between degrees and radians +template +inline Real degToRad (Real deg) { return deg * kPiOver180; } + +template +inline Real radToDeg (Real rad) { return rad * k180OverPi; } + +// Convert between "field of view" and "zoom". +// The FOV angle is specified in radians. +template +inline Real fovToZoom (Real fov) { return 1.0f / std::tan (fov * 0.5f); } + +template +inline Real zoomToFov (Real zoom) { return 2.0f * std::atan (1.0f / zoom); } + + +///////////////////////////////////////////////////////////////////////////// +// +// class Vector3 - A simple 3D vector class. +// +///////////////////////////////////////////////////////////////////////////// + +template +class Vector3 +{ +public: + // Constructors + Vector3 () { } + Vector3 (Real x, Real y, Real z) + : _x (x), _y (y), _z (z) { } + +public: + // Vector comparison + bool operator== (const Vector3 &v) const; + bool operator!= (const Vector3 &v) const; + + // Vector negation + Vector3 operator- () const; + + // Vector operations + Vector3 operator+ (const Vector3 &v) const; + Vector3 operator- (const Vector3 &v) const; + Vector3 operator* (Real s) const; + Vector3 operator/ (Real s) const; + + // Combined assignment operators to conform to + // C notation convention + Vector3 &operator+= (const Vector3 &v); + Vector3 &operator-= (const Vector3 &v); + Vector3 &operator*= (Real s); + Vector3 &operator/= (Real s); + + // Accessor. This allows to use the vector object + // like an array of Real. For example: + // Vector3 v (...); + // float f = v[1]; // access to _y + operator const Real *() { return _v; } + +public: + // Other vector operations + bool isZero (); + void normalize (); + +public: + // Member variables + union + { + struct + { + Real _x, _y, _z; + }; + + Real _v[3]; + }; +}; + + +// Predefined Vector3 types +typedef Vector3 Vector3f; +typedef Vector3 Vector3d; + +// We provide a global constant zero vector +static const Vector3f kZeroVectorf (0.0f, 0.0f, 0.0f); +static const Vector3d kZeroVectord (0.0, 0.0, 0.0); + + +// +// Nonmember Vector3 functions +// + +template +Vector3 operator* (Real k, Vector3 v); + +template +Real VectorMag (const Vector3 &v); + +template +Real DotProduct (const Vector3 &a, const Vector3 &b); + +template +Vector3 CrossProduct (const Vector3 &a, const Vector3 &b); + +template +Vector3 ComputeNormal (const Vector3 &p1, + const Vector3 &p2, const Vector3 &p3); + +template +Real Distance (const Vector3 &a, const Vector3 &b); + +template +Real DistanceSquared (const Vector3 &a, const Vector3 &b); + + +///////////////////////////////////////////////////////////////////////////// +// +// class Matrix4x4 - Implement a 4x4 Matrix class that can represent +// any 3D affine transformation. +// +///////////////////////////////////////////////////////////////////////////// + +template +class Matrix4x4 +{ +public: + // Constructor - Initialize the last (never used) row of the matrix + // so that we can do any operation on matrices on the 3x4 portion + // and forget that line which will (and should) never change. + Matrix4x4 () + : _h14 (0.0f), _h24 (0.0f), _h34 (0.0f), _tw (1.0f) { } + + // Note that we don't define the copy constructor and let the compiler + // doing it itself because such initialization is not necessary + // since the source matrix has its last row already initialized... + +public: + // Public interface + void identity (); + void transpose (); + void invert (); + void setTranslation (const Vector3 &v); + + void transform (Vector3 &v) const; + void rotate (Vector3 &v) const; + void inverseRotate (Vector3 &v) const; + void inverseTranslate (Vector3 &v) const; + + void fromQuaternion (const Quaternion &q); + + // Matrix <-> Euler conversions; XYZ rotation order; angles in radians + void fromEulerAngles (Real x, Real y, Real z); + void toEulerAngles (Real &x, Real &y, Real &z) const; + + // Return a base vector from the matrix + Vector3 rightVector () const; + Vector3 upVector () const; + Vector3 forwardVector () const; + Vector3 translationVector () const; + + // Accessor. This allows to use the matrix object + // like an array of Real. For example: + // Matrix4x4 mat; + // float f = mat[4]; // access to _m21 + operator const Real *() { return _m; } + +public: + // Member variables + + // The values of the matrix. Basically the upper 3x3 portion + // contains a linear transformation, and the last column is the + // translation portion. Here data is transposed, see the Mathlib.inl + // for more details. + union + { + struct + { + Real _m11, _m12, _m13, _h14; + Real _m21, _m22, _m23, _h24; + Real _m31, _m32, _m33, _h34; + Real _tx, _ty, _tz, _tw; + }; + + // Access to raw packed matrix data (usefull for + // glLoadMatrixf () and glMultMatrixf ()) + Real _m[16]; + }; +}; + + +// Predefined Matrix4x4 types +typedef Matrix4x4 Matrix4x4f; +typedef Matrix4x4 Matrix4x4d; + + +// +// Nonmember Matrix4x4 functions +// + +// Matrix concatenation +template +Matrix4x4 operator* (const Matrix4x4 &a, const Matrix4x4 &b); + +template +Matrix4x4 &operator*= (Matrix4x4 &a, const Matrix4x4 &b); + +// Vector transformation +template +Vector3 operator* (const Matrix4x4 &m, const Vector3 &p); + +// Transpose matrix +template +Matrix4x4 Transpose (const Matrix4x4 &m); + +// Invert matrix +template +Matrix4x4 Invert (const Matrix4x4 &m); + +// +// Matrix-builder functions +// + +template Matrix4x4 RotationMatrix (Axis axis, Real theta); +template Matrix4x4 RotationMatrix (const Vector3 &axis, Real theta); +template Matrix4x4 TranslationMatrix (Real x, Real y, Real z); +template Matrix4x4 TranslationMatrix (const Vector3 &v); +template Matrix4x4 ScaleMatrix (const Vector3 &s); +template Matrix4x4 ScaleAlongAxisMatrix (const Vector3 &axis, Real k); +template Matrix4x4 ShearMatrix (Axis axis, Real s, Real t); +template Matrix4x4 ProjectionMatrix (const Vector3 &n); +template Matrix4x4 ReflectionMatrix (Axis axis, Real k); +template Matrix4x4 AxisReflectionMatrix (const Vector3 &n); + +template +Matrix4x4 LookAtMatrix (const Vector3 &camPos, + const Vector3 &target, const Vector3 &camUp); +template +Matrix4x4 FrustumMatrix (Real l, Real r, Real b, Real t, Real n, Real f); +template +Matrix4x4 PerspectiveMatrix (Real fovY, Real aspect, Real n, Real f); +template +Matrix4x4 OrthoMatrix (Real l, Real r, Real b, Real t, Real n, Real f); +template +Matrix4x4 OrthoNormalMatrix (const Vector3 &xdir, + const Vector3 &ydir, const Vector3 &zdir); + + +///////////////////////////////////////////////////////////////////////////// +// +// class Quaternion - Implement a quaternion, for purposes of +// representing an angular displacement (orientation) in 3D. +// +///////////////////////////////////////////////////////////////////////////// + +template +class Quaternion +{ +public: + // Constructors + Quaternion () { } + Quaternion (Real w, Real x, Real y, Real z) + : _w (w), _x (x), _y (y), _z (z) { } + +public: + // Public interface + void identity (); + void normalize (); + void computeW (); + void rotate (Vector3 &v) const; + + void fromMatrix (const Matrix4x4 &m); + + // Quaternion <-> Euler conversions; XYZ rotation order; angles in radians + void fromEulerAngles (Real x, Real y, Real z); + void toEulerAngles (Real &x, Real &y, Real &z) const; + + Real rotationAngle () const; + Vector3 rotationAxis () const; + + // Quaternion operations + Quaternion operator+ (const Quaternion &q) const; + Quaternion &operator+= (const Quaternion &q); + + Quaternion operator- (const Quaternion &q) const; + Quaternion &operator-= (const Quaternion &q); + + Quaternion operator* (const Quaternion &q) const; + Quaternion &operator*= (const Quaternion &q); + + Quaternion operator* (Real k) const; + Quaternion &operator*= (Real k); + + Quaternion operator* (const Vector3 &v) const; + Quaternion &operator*= (const Vector3 &v); + + Quaternion operator/ (Real k) const; + Quaternion &operator/= (Real k); + + Quaternion operator~ () const; // Quaternion conjugate + Quaternion operator- () const; // Quaternion negation + +public: + // Member variables + + // The 4 values of the quaternion. Normally, it will not + // be necessary to manipulate these directly. However, + // we leave them public, since prohibiting direct access + // makes some operations, such as file I/O, unnecessarily + // complicated. + + union + { + struct + { + Real _w, _x, _y, _z; + }; + + Real _q[4]; + }; +}; + + +// Predefined Quaternion types +typedef Quaternion Quaternionf; +typedef Quaternion Quaterniond; + +// A global "identity" quaternion constant +static const Quaternionf kQuaternionIdentityf (1.0f, 0.0f, 0.0f, 0.0f); +static const Quaterniond kQuaternionIdentityd (1.0f, 0.0f, 0.0f, 0.0f); + + +// +// Nonmember Matrix4x functions +// + +template +Quaternion operator* (Real k, const Quaternion &q); + +template +Real DotProduct (const Quaternion &a, const Quaternion &b); + +template +Quaternion Conjugate (const Quaternion &q); + +template +Quaternion Inverse (const Quaternion &q); + +template +Quaternion RotationQuaternion (Axis axis, Real theta); + +template +Quaternion RotationQuaternion (const Vector3 &axis, Real theta); + +template +Quaternion Log (const Quaternion &q); +template +Quaternion Exp (const Quaternion &q); +template +Quaternion Pow (const Quaternion &q, Real exponent); + +template +Quaternion Slerp (const Quaternion &q0, const Quaternion &q1, Real t); +template +Quaternion Squad (const Quaternion &q0, const Quaternion &qa, + const Quaternion &qb, const Quaternion &q1, Real t); +template +inline void Intermediate (const Quaternion &qprev, const Quaternion &qcurr, + const Quaternion &qnext, Quaternion &qa, + Quaternion &qb); + +// Include inline function definitions +#include "Mathlib.inl" + +}}} //end namespace + +#endif // __MATHLIB_H__ diff --git a/source/shared_lib/include/graphics/md5/Mathlib.inl b/source/shared_lib/include/graphics/md5/Mathlib.inl new file mode 100644 index 00000000..59c96218 --- /dev/null +++ b/source/shared_lib/include/graphics/md5/Mathlib.inl @@ -0,0 +1,2158 @@ +// ============================================================== +// This file is part of MegaGlest (www.glest.org) +// +// Mathlib.inl -- Copyright (c) 2005-2006 David Henry +// changed for use with MegaGlest: Copyright (C) 2011- by Mark Vejvoda// +// ------------------------------------------------------------------- +// Portions Copyright (c) Dante Treglia II and Mark A. DeLoura, 2000. +// Portions Copyright (c) Fletcher Dunn and Ian Parberry, 2002. +// ------------------------------------------------------------------- +// +// This software is provided "as is" without express or implied +// warranties. You may freely copy and compile this source into +// applications you distribute provided that the copyright text +// below is included in the resulting source code. +// +// Implementation of a math library to use with OpenGL. +// +// Provide vector, matrix and quaternion operations. +// +///////////////////////////////////////////////////////////////////////////// + +#include + +///////////////////////////////////////////////////////////////////////////// +// +// Global functions +// +///////////////////////////////////////////////////////////////////////////// +// -------------------------------------------------------------------------- +// wrapPi +// +// "Wrap" an angle in range -pi...pi by adding the correct multiple +// of 2 pi +// -------------------------------------------------------------------------- + +template +inline Real +wrapPi (Real theta) +{ + theta += kPi; + theta -= std::floor (theta * k1Over2Pi) * k2Pi; + theta -= kPi; + return theta; +} + + +// -------------------------------------------------------------------------- +// safeAcos +// +// Same as acos(x), but if x is out of range, it is "clamped" to the nearest +// valid value. The value returned is in range 0...pi, the same as the +// standard C acos() function +// -------------------------------------------------------------------------- + +template +inline Real +safeAcos (Real x) +{ + // Check limit conditions + if (x <= -1.0) + return kPi; + + if (x >= 1.0) + return 0.0; + + // value is in the domain - use standard C function + return std::acos (x); +} + + +// -------------------------------------------------------------------------- +// canonizeEulerAngles +// +// Set the Euler angle triple to its "canonical" value. This does not change +// the meaning of the Euler angles as a representation of Orientation in 3D, +// but if the angles are for other purposes such as angular velocities, etc, +// then the operation might not be valid. +// -------------------------------------------------------------------------- + +template +inline void +canonizeEulerAngles (Real &roll, Real &pitch, Real &yaw) +{ + // First, wrap pitch in range -pi ... pi + pitch = wrapPi (pitch); + + // Now, check for "the back side" of the matrix, pitch outside + // the canonical range of -pi/2 ... pi/2 + if (pitch < -kPiOver2) + { + roll += kPi; + pitch = -kPi - pitch; + yaw += kPi; + } + else if (pitch > kPiOver2) + { + roll += kPi; + pitch = kPi - pitch; + yaw += kPi; + } + + // OK, now check for the Gimbal lock case (within a slight + // tolerance) + + if (std::fabs (pitch) > kPiOver2 - 1e-4) + { + // We are in gimbal lock. Assign all rotation + // about the vertical axis to heading + yaw += roll; + roll = 0.0; + } + else + { + // Not in gimbal lock. Wrap the bank angle in + // canonical range + roll = wrapPi (roll); + } + + // Wrap heading in canonical range + yaw = wrapPi (yaw); +} + + +///////////////////////////////////////////////////////////////////////////// +// +// class Vector3 implementation. +// +///////////////////////////////////////////////////////////////////////////// + +// -------------------------------------------------------------------------- +// Vector3::isZero +// +// Return true if is zero vector. +// -------------------------------------------------------------------------- + +template +inline bool +Vector3::isZero () +{ + return (_x == 0.0) && (_y == 0.0) && (_z == 0.0); +} + + +// -------------------------------------------------------------------------- +// Vector3::normalize +// +// Set vector length to 1. +// -------------------------------------------------------------------------- + +template +inline void +Vector3::normalize() +{ + Real magSq = (_x * _x) + (_y * _y) + (_z * _z); + + if (magSq > 0.0) + { + // check for divide-by-zero + Real oneOverMag = 1.0 / std::sqrt (magSq); + _x *= oneOverMag; + _y *= oneOverMag; + _z *= oneOverMag; + } +} + + +// -------------------------------------------------------------------------- +// Vector3 operators +// +// Operator overloading for basic vector operations. +// -------------------------------------------------------------------------- + +template +inline bool +Vector3::operator== (const Vector3 &v) const +{ + return ((_x == v._x) && (_y == v._y) && (_z == v._z)); +} + +template +inline bool +Vector3::operator!= (const Vector3 &v) const +{ + return ((_x != v._x) || (_y != v._y) || (_z != v._z)); +} + +template +inline Vector3 +Vector3::operator- () const +{ + return Vector3 (-_x, -_y, -_z); +} + +template +inline Vector3 +Vector3::operator+ (const Vector3 &v) const +{ + return Vector3 (_x + v._x, _y + v._y, _z + v._z); +} + +template +inline Vector3 +Vector3::operator- (const Vector3 &v) const +{ + return Vector3 (_x - v._x, _y - v._y, _z - v._z); +} + +template +inline Vector3 +Vector3::operator* (Real s) const +{ + return Vector3 (_x * s, _y * s, _z * s); +} + +template +inline Vector3 +Vector3::operator/ (Real s) const +{ + Real oneOverS = 1.0 / s; // Note: no check for divide by zero + return Vector3 (_x * oneOverS, _y * oneOverS, _z * oneOverS); +} + +template +inline Vector3 & +Vector3::operator+= (const Vector3 &v) +{ + _x += v._x; _y += v._y; _z += v._z; + return *this; +} + +template +inline Vector3 & +Vector3::operator-= (const Vector3 &v) +{ + _x -= v._x; _y -= v._y; _z -= v._z; + return *this; +} + +template +inline Vector3 & +Vector3::operator*= (Real s) +{ + _x *= s; _y *= s; _z *= s; + return *this; +} + +template +inline Vector3 & +Vector3::operator/= (Real s) +{ + Real oneOverS = 1.0 / s; // Note: no check for divide by zero! + _x *= oneOverS; _y *= oneOverS ; _z *= oneOverS; + return *this; +} + + +// -------------------------------------------------------------------------- +// +// Nonmember Vector3 functions +// +// -------------------------------------------------------------------------- + +// Scalar on the left multiplication, for symmetry +template +inline Vector3 +operator* (Real k, Vector3 v) +{ + return Vector3 (k * v._x, k * v._y, k * v._z); +} + +// Compute vector lenght +template +inline Real +VectorMag (const Vector3 &v) +{ + return std::sqrt ((v._x * v._x) + (v._y * v._y) + (v._z * v._z)); +} + +// Vector3 dot product +template +inline Real +DotProduct (const Vector3 &a, const Vector3 &b) +{ + return ((a._x * b._x) + (a._y * b._y) + (a._z * b._z)); +} + +// Vector3 cross product +template +inline Vector3 +CrossProduct (const Vector3 &a, const Vector3 &b) +{ + return Vector3 ( + (a._y * b._z) - (a._z * b._y), + (a._z * b._x) - (a._x * b._z), + (a._x * b._y) - (a._y * b._x) + ); +} + +// Compute normal plane given three points +template +inline Vector3 +ComputeNormal (const Vector3 &p1, const Vector3 &p2, const Vector3 &p3) +{ + Vector3 vec1 (p1 - p2); + Vector3 vec2 (p1 - p3); + + Vector3 result (CrossProduct (vec1, vec2)); + result.normalize (); + + return result; +} + +// Compute distance between two points +template +inline Real +Distance (const Vector3 &a, const Vector3 &b) +{ + Real dx = a._x - b._x; + Real dy = a._y - b._y; + Real dz = a._z - b._z; + return std::sqrt ((dx * dx) + (dy * dy) + (dz * dz)); +} + +// Compute squared distance between two points. +// Useful when comparing distances, since we don't need +// to square the result. +template +inline Real +DistanceSquared (const Vector3 &a, const Vector3 &b) +{ + Real dx = a._x - b._x; + Real dy = a._y - b._y; + Real dz = a._z - b._z; + return ((dx * dx) + (dy * dy) + (dz * dz)); +} + + +///////////////////////////////////////////////////////////////////////////// +// +// class Matrix4x4 implementation. +// +// -------------------------------------------------------------------------- +// +// MATRIX ORGANIZATION +// +// The purpose of this class is so that a user might perform transformations +// without fiddling with plus or minus signs or transposing the matrix +// until the output "looks right". But of course, the specifics of the +// internal representation is important. Not only for the implementation +// in this file to be correct, but occasionally direct access to the +// matrix variables is necessary, or beneficial for optimization. Thus, +// we document our matrix conventions here. +// +// Strict adherance to linear algebra rules dictates that the +// multiplication of a 4x4 matrix by a 3D vector is actually undefined. +// To circumvent this, we can consider the input and output vectors as +// having an assumed fourth coordinate of 1. Also, since the rightmost +// column is [ 0 0 0 1 ], we can simplificate calculations ignoring +// this last column. This is shown below: +// +// | m11 m12 m13 0 | | x | | x'| +// | m21 m22 m23 0 | | y | = | y'| +// | m31 m32 m33 0 | | z | | z'| +// | tx ty tz 1 | | 1 | | 1 | +// +// We use row vectors to represent the right, up and forward vectors +// in the 4x4 matrix. OpenGL uses column vectors, but the elements of +// an OpenGL matrix are ordered in columns so that m[i][j] is in row j +// and column i. This is the reverse of the standard C convention in +// which m[i][j] is in row i and column j. The matrix should be +// transposed before being sent to OpenGL. +// +// | m11 m21 m31 tx | | m0 m4 m8 m12 | | m0 m1 m2 m3 | +// | m12 m22 m32 ty | | m1 m5 m9 m13 | | m4 m5 m6 m7 | +// | m13 m23 m33 tz | | m2 m6 m10 m14 | | m8 m9 m10 m11 | +// | 0 0 0 tw | | m3 m7 m11 m15 | | m12 m13 m14 m15 | +// +// OpenGL style OpenGL matrix standard C +// arrangement convention +// +// Fortunately, accessing to the raw matrix data via the _m[] array gives +// us the transpose matrix; i.e. in OpenGL form, so that we can directly use +// it with glLoadMatrix{fd}() or glMultMatrix{fd}(). +// +// Also, since the rightmost column (in standard C form) should always +// be [ 0 0 0 1 ], and sice these values (_h14, _h24, _h34 and _tw) are +// initialized in constructors, we don't need to modify them in our +// matrix operations, so we don't perform useless calculations... +// +// The right-hand rule is used to define "positive" rotation. +// +// +y +x' +// | | +// | | +// |______ +x +y' ______| +// / / +// / / +// +z +z' +// +// initial position Positive rotation of +// pi/2 around z-axis +// +///////////////////////////////////////////////////////////////////////////// + + +// -------------------------------------------------------------------------- +// Matrix4x4::identity +// +// Set matrix to identity. +// -------------------------------------------------------------------------- + +template +inline void +Matrix4x4::identity () +{ + _m11 = 1.0; _m21 = 0.0; _m31 = 0.0; _tx = 0.0; + _m12 = 0.0; _m22 = 1.0; _m32 = 0.0; _ty = 0.0; + _m13 = 0.0; _m23 = 0.0; _m33 = 1.0; _tz = 0.0; + _h14 = 0.0; _h24 = 0.0; _h34 = 0.0; _tw = 1.0; +} + + +// -------------------------------------------------------------------------- +// Matrix4x4::transpose +// +// Transpose the current matrix. +// -------------------------------------------------------------------------- + +template +inline void +Matrix4x4::transpose () +{ + *this = Transpose (*this); +} + + +// -------------------------------------------------------------------------- +// Matrix4x4::invert +// +// Invert the current matrix. +// -------------------------------------------------------------------------- + +template +inline void +Matrix4x4::invert () +{ + *this = Invert (*this); +} + + +// -------------------------------------------------------------------------- +// Matrix4x4::setTranslation +// +// Set the translation portion of the matrix. +// -------------------------------------------------------------------------- + +template +inline void +Matrix4x4::setTranslation (const Vector3 &v) +{ + _tx = v._x; _ty = v._y; _tz = v._z; +} + + +// -------------------------------------------------------------------------- +// Matrix4x4::transform +// +// Transform a point by the matrix. +// -------------------------------------------------------------------------- + +template +inline void +Matrix4x4::transform (Vector3 &v) const +{ + // Grind through the linear algebra. + v = Vector3 ( + (v._x * _m11) + (v._y * _m21) + (v._z * _m31) + _tx, + (v._x * _m12) + (v._y * _m22) + (v._z * _m32) + _ty, + (v._x * _m13) + (v._y * _m23) + (v._z * _m33) + _tz + ); +} + + +// -------------------------------------------------------------------------- +// Matrix4x4::rotate +// +// Rotate a point by the 3x3 upper left portion of the matrix. +// -------------------------------------------------------------------------- + +template +inline void +Matrix4x4::rotate (Vector3 &v) const +{ + v = Vector3 ( + (v._x * _m11) + (v._y * _m21) + (v._z * _m31), + (v._x * _m12) + (v._y * _m22) + (v._z * _m32), + (v._x * _m13) + (v._y * _m23) + (v._z * _m33) + ); +} + + +// -------------------------------------------------------------------------- +// Matrix4x4::inverseRotate +// +// Rotate a point by the inverse 3x3 upper left portion of the matrix. +// -------------------------------------------------------------------------- + +template +inline void +Matrix4x4::inverseRotate (Vector3 &v) const +{ + v = Vector3 ( + (v._x * _m11) + (v._y * _m12) + (v._z * _m13), + (v._x * _m21) + (v._y * _m22) + (v._z * _m23), + (v._x * _m31) + (v._y * _m32) + (v._z * _m33) + ); +} + + +// -------------------------------------------------------------------------- +// Matrix4x4::inverseRotate +// +// Translate a point by the inverse matrix. +// -------------------------------------------------------------------------- + +template +inline void +Matrix4x4::inverseTranslate (Vector3 &v) const +{ + v._x -= _tx; + v._y -= _ty; + v._z -= _tz; +} + + +// -------------------------------------------------------------------------- +// Matrix4x4::fromQuaternion +// +// Convert a quaternion to a matrix. +// -------------------------------------------------------------------------- + +template +inline void +Matrix4x4::fromQuaternion (const Quaternion &q) +{ + // Compute a few values to optimize common subexpressions + Real ww = 2.0 * q._w; + Real xx = 2.0 * q._x; + Real yy = 2.0 * q._y; + Real zz = 2.0 * q._z; + + // Set the matrix elements. There is still a little more + // opportunity for optimization due to the many common + // subexpressions. We'll let the compiler handle that... + _m11 = 1.0 - (yy * q._y) - (zz * q._z); + _m12 = (xx * q._y) + (ww * q._z); + _m13 = (xx * q._z) - (ww * q._y); + + _m21 = (xx * q._y) - (ww * q._z); + _m22 = 1.0 - (xx * q._x) - (zz * q._z); + _m23 = (yy * q._z) + (ww * q._x); + + _m31 = (xx * q._z) + (ww * q._y); + _m32 = (yy * q._z) - (ww * q._x); + _m33 = 1.0 - (xx * q._x) - (yy * q._y); + + // Reset the translation portion + _tx = _ty = _tz = 0.0; +} + + +// -------------------------------------------------------------------------- +// Matrix4x4::fromEulerAngles +// +// Setup a rotation matrix, given three X-Y-Z rotation angles. The +// rotations are performed first on x-axis, then y-axis and finaly z-axis. +// -------------------------------------------------------------------------- + +template +inline void +Matrix4x4::fromEulerAngles (Real x, Real y, Real z) +{ + // Fetch sine and cosine of angles + Real cx = std::cos (x); + Real sx = std::sin (x); + Real cy = std::cos (y); + Real sy = std::sin (y); + Real cz = std::cos (z); + Real sz = std::sin (z); + + Real sxsy = sx * sy; + Real cxsy = cx * sy; + + // Fill in the matrix elements + _m11 = (cy * cz); + _m12 = (sxsy * cz) + (cx * sz); + _m13 = -(cxsy * cz) + (sx * sz); + + _m21 = -(cy * sz); + _m22 = -(sxsy * sz) + (cx * cz); + _m23 = (cxsy * sz) + (sx * cz); + + _m31 = (sy); + _m32 = -(sx * cy); + _m33 = (cx * cy); + + // Reset the translation portion + _tx = _ty = _tz = 0.0; +} + + +// -------------------------------------------------------------------------- +// Matrix4x4::toEulerAngles +// +// Setup the euler angles in radians, given a rotation matrix. The rotation +// matrix could have been obtained from euler angles given the expression: +// M = X.Y.Z +// where X, Y and Z are rotation matrices about X, Y and Z axes. +// This is the "opposite" of the fromEulerAngles function. +// -------------------------------------------------------------------------- + +template +inline void +Matrix4x4::toEulerAngles (Real &x, Real &y, Real &z) const +{ + // Compute Y-axis angle + y = std::asin (_m31); + + // Compute cos and one over cos for optimization + Real cy = std::cos (y); + Real oneOverCosY = 1.0 / cy; + + if (std::fabs (cy) > 0.001) + { + // No gimball lock + x = std::atan2 (-_m32 * oneOverCosY, _m33 * oneOverCosY); + z = std::atan2 (-_m21 * oneOverCosY, _m11 * oneOverCosY); + } + else + { + // Gimbal lock case + x = 0.0; + z = std::atan2 (_m12, _m22); + } +} + + +// -------------------------------------------------------------------------- +// Matrix4x4::rightVector +// Matrix4x4::upVector +// Matrix4x4::forwardVector +// Matrix4x4::translationVector +// +// Return a base vector from the matrix. +// -------------------------------------------------------------------------- + +template +inline Vector3 +Matrix4x4::rightVector () const +{ + return Vector3 (_m11, _m12, _m13); +} + +template +inline Vector3 +Matrix4x4::upVector () const +{ + return Vector3 (_m21, _m22, _m23); +} + +template +inline Vector3 +Matrix4x4::forwardVector () const +{ + return Vector3 (_m31, _m32, _m33); +} + +template +inline Vector3 +Matrix4x4::translationVector () const +{ + return Vector3 (_tx, _ty, _tz); +} + + +// -------------------------------------------------------------------------- +// +// Nonmember Matrix4x4 functions +// +// -------------------------------------------------------------------------- + + +// -------------------------------------------------------------------------- +// Matrix4x4 * Matrix4x4 +// +// Matrix concatenation. +// +// We also provide a *= operator, as per C convention. +// -------------------------------------------------------------------------- + +template +inline Matrix4x4 +operator* (const Matrix4x4 &a, const Matrix4x4 &b) +{ + Matrix4x4 res; + + // Compute the left 4x3 (linear transformation) portion + res._m11 = (a._m11 * b._m11) + (a._m21 * b._m12) + (a._m31 * b._m13); + res._m12 = (a._m12 * b._m11) + (a._m22 * b._m12) + (a._m32 * b._m13); + res._m13 = (a._m13 * b._m11) + (a._m23 * b._m12) + (a._m33 * b._m13); + + res._m21 = (a._m11 * b._m21) + (a._m21 * b._m22) + (a._m31 * b._m23); + res._m22 = (a._m12 * b._m21) + (a._m22 * b._m22) + (a._m32 * b._m23); + res._m23 = (a._m13 * b._m21) + (a._m23 * b._m22) + (a._m33 * b._m23); + + res._m31 = (a._m11 * b._m31) + (a._m21 * b._m32) + (a._m31 * b._m33); + res._m32 = (a._m12 * b._m31) + (a._m22 * b._m32) + (a._m32 * b._m33); + res._m33 = (a._m13 * b._m31) + (a._m23 * b._m32) + (a._m33 * b._m33); + + // Compute the translation portion + res._tx = (a._m11 * b._tx) + (a._m21 * b._ty) + (a._m31 * b._tz) + a._tx; + res._ty = (a._m12 * b._tx) + (a._m22 * b._ty) + (a._m32 * b._tz) + a._ty; + res._tz = (a._m13 * b._tx) + (a._m23 * b._ty) + (a._m33 * b._tz) + a._tz; + + return res; +} + +template +inline Matrix4x4 & +operator*= (Matrix4x4 &a, const Matrix4x4 &b) +{ + a = a * b; + return a; +} + + +// -------------------------------------------------------------------------- +// Matrix4x4 * Vector3 +// +// Transform a point by a matrix. This makes using the vector class look +// like it does with linear algebra notation on paper. +// -------------------------------------------------------------------------- + +template +inline Vector3 +operator* (const Matrix4x4 &m, const Vector3 &p) +{ + Vector3 res (p); + m.transform (res); + return res; +} + + +// -------------------------------------------------------------------------- +// Transpose +// +// Return the transpose matrix. +// -------------------------------------------------------------------------- + +template +inline Matrix4x4 +Transpose (const Matrix4x4 &m) +{ + Matrix4x4 res; + + res._m11 = m._m11; res._m21 = m._m12; res._m31 = m._m13; res._tx = m._h14; + res._m12 = m._m21; res._m22 = m._m22; res._m32 = m._m23; res._ty = m._h24; + res._m13 = m._m31; res._m23 = m._m32; res._m33 = m._m33; res._tz = m._h34; + res._h14 = m._tx; res._h24 = m._ty; res._h34 = m._tz; res._tw = m._tw; + + return res; +} + + +// -------------------------------------------------------------------------- +// Determinant +// +// Compute the determinant of the 3x3 portion of the matrix. +// -------------------------------------------------------------------------- + +template +inline static Real +Determinant3x3 (const Matrix4x4 &m) +{ + return m._m11 * ((m._m22 * m._m33) - (m._m23 * m._m32)) + + m._m12 * ((m._m23 * m._m31) - (m._m21 * m._m33)) + + m._m13 * ((m._m21 * m._m32) - (m._m22 * m._m31)); +} + + +// -------------------------------------------------------------------------- +// Invert +// +// Compute the inverse of a matrix. We use the classical adjoint divided +// by the determinant method. +// -------------------------------------------------------------------------- + +template +inline Matrix4x4 +Invert (const Matrix4x4 &m) +{ + // Compute the determinant of the 3x3 portion + Real det = Determinant3x3 (m); + + // If we're singular, then the determinant is zero and there's + // no inverse + assert (std::fabs (det) > 0.000001); + + // Compute one over the determinant, so we divide once and + // can *multiply* per element + Real oneOverDet = 1.0 / det; + + // Compute the 3x3 portion of the inverse, by + // dividing the adjoint by the determinant + Matrix4x4 res; + + res._m11 = ((m._m22 * m._m33) - (m._m23 * m._m32)) * oneOverDet; + res._m12 = ((m._m13 * m._m32) - (m._m12 * m._m33)) * oneOverDet; + res._m13 = ((m._m12 * m._m23) - (m._m13 * m._m22)) * oneOverDet; + + res._m21 = ((m._m23 * m._m31) - (m._m21 * m._m33)) * oneOverDet; + res._m22 = ((m._m11 * m._m33) - (m._m13 * m._m31)) * oneOverDet; + res._m23 = ((m._m13 * m._m21) - (m._m11 * m._m23)) * oneOverDet; + + res._m31 = ((m._m21 * m._m32) - (m._m22 * m._m31)) * oneOverDet; + res._m32 = ((m._m12 * m._m31) - (m._m11 * m._m32)) * oneOverDet; + res._m33 = ((m._m11 * m._m22) - (m._m12 * m._m21)) * oneOverDet; + + // Compute the translation portion of the inverse + res._tx = -((m._tx * res._m11) + (m._ty * res._m21) + (m._tz * res._m31)); + res._ty = -((m._tx * res._m12) + (m._ty * res._m22) + (m._tz * res._m32)); + res._tz = -((m._tx * res._m13) + (m._ty * res._m23) + (m._tz * res._m33)); + + // Return it. + return res; +} + + +// -------------------------------------------------------------------------- +// RotationMatrix +// +// Setup the matrix to perform a rotation about one of the three cardinal +// X-Y-Z axes. +// +// The axis of rotation is specified by the 1-based "axis" index. +// +// theta is the amount of rotation, in radians. The right-hand rule is +// used to define "positive" rotation. +// +// The translation portion is reset. +// -------------------------------------------------------------------------- + +template +inline Matrix4x4 +RotationMatrix (Axis axis, Real theta) +{ + Matrix4x4 res; + + // Get sin and cosine of rotation angle + Real s = std::sin (theta); + Real c = std::cos (theta); + + // Check which axis they are rotating about + switch (axis) + { + case kXaxis: // Rotate about the x-axis + res._m11 = 1.0; res._m21 = 0.0; res._m31 = 0.0; + res._m12 = 0.0; res._m22 = c; res._m32 = -s; + res._m13 = 0.0; res._m23 = s; res._m33 = c; + break; + + case kYaxis: // Rotate about the y-axis + res._m11 = c; res._m21 = 0.0; res._m31 = s; + res._m12 = 0.0; res._m22 = 1.0; res._m32 = 0.0; + res._m13 = -s; res._m23 = 0.0; res._m33 = c; + break; + + case kZaxis: // Rotate about the z-axis + res._m11 = c; res._m21 = -s; res._m31 = 0.0; + res._m12 = s; res._m22 = c; res._m32 = 0.0; + res._m13 = 0.0; res._m23 = 0.0; res._m33 = 1.0; + break; + + default: + // bogus axis index + assert (false); + } + + // Reset the translation portion + res._tx = res._ty = res._tz = 0.0; + + return res; +} + + +//--------------------------------------------------------------------------- +// AxisRotationMatrix +// +// Setup the matrix to perform a rotation about an arbitrary axis. +// The axis of rotation must pass through the origin. +// +// axis defines the axis of rotation, and must be a unit vector. +// +// theta is the amount of rotation, in radians. The right-hand rule is +// used to define "positive" rotation. +// +// The translation portion is reset. +// -------------------------------------------------------------------------- + +template +inline Matrix4x4 +RotationMatrix (const Vector3 &axis, Real theta) +{ + Matrix4x4 res; + + // Quick sanity check to make sure they passed in a unit vector + // to specify the axis + assert (std::fabs (DotProduct (axis, axis) - 1.0) < 0.001); + + // Get sin and cosine of rotation angle + Real s = std::sin (theta); + Real c = std::cos (theta); + + // Compute 1 - cos(theta) and some common subexpressions + Real a = 1.0 - c; + Real ax = a * axis._x; + Real ay = a * axis._y; + Real az = a * axis._z; + + // Set the matrix elements. There is still a little more + // opportunity for optimization due to the many common + // subexpressions. We'll let the compiler handle that... + res._m11 = (ax * axis._x) + c; + res._m12 = (ax * axis._y) + (axis._z * s); + res._m13 = (ax * axis._z) - (axis._y * s); + + res._m21 = (ay * axis._x) - (axis._z * s); + res._m22 = (ay * axis._y) + c; + res._m23 = (ay * axis._z) + (axis._x * s); + + res._m31 = (az * axis._x) + (axis._y * s); + res._m32 = (az * axis._y) - (axis._x * s); + res._m33 = (az * axis._z) + c; + + // Reset the translation portion + res._tx = res._ty = res._tz = 0.0; + + return res; +} + + +// -------------------------------------------------------------------------- +// TranslationMatrix +// +// Build a translation matrix given a translation vector. +// -------------------------------------------------------------------------- + +template +inline Matrix4x4 +TranslationMatrix (Real x, Real y, Real z) +{ + return TranslationMatrix (Vector3 (x, y, z)); +} + +template +inline Matrix4x4 +TranslationMatrix (const Vector3 &v) +{ + Matrix4x4 res; + + res._m11 = 1.0; res._m21 = 0.0; res._m31 = 0.0; res._tx = v._x; + res._m12 = 0.0; res._m22 = 1.0; res._m32 = 0.0; res._ty = v._y; + res._m13 = 0.0; res._m23 = 0.0; res._m33 = 1.0; res._tz = v._z; + + return res; +} + + +// -------------------------------------------------------------------------- +// ScaleMatrix +// +// Setup the matrix to perform scale on each axis. For uniform scale by k, +// use a vector of the form Vector3( k, k, k ). +// +// The translation portion is reset. +// -------------------------------------------------------------------------- + +template +inline Matrix4x4 +ScaleMatrix (const Vector3 &s) +{ + Matrix4x4 res; + + // Set the matrix elements. Pretty straightforward + res._m11 = s._x; res._m21 = 0.0; res._m31 = 0.0; + res._m12 = 0.0; res._m22 = s._y; res._m32 = 0.0; + res._m13 = 0.0; res._m23 = 0.0; res._m33 = s._z; + + // Reset the translation portion + res._tx = res._ty = res._tz = 0.0; + + return res; +} + + +// -------------------------------------------------------------------------- +// ScaleAlongAxisMatrix +// +// Setup the matrix to perform scale along an arbitrary axis. +// +// The axis is specified using a unit vector. +// +// The translation portion is reset. +// -------------------------------------------------------------------------- + +template +inline Matrix4x4 +ScaleAlongAxisMatrix (const Vector3 &axis, Real k) +{ + Matrix4x4 res; + + // Quick sanity check to make sure they passed in a unit vector + // to specify the axis + assert (std::fabs (DotProduct (axis, axis) - 1.0) < 0.001); + + // Compute k-1 and some common subexpressions + Real a = k - 1.0; + Real ax = a * axis._x; + Real ay = a * axis._y; + Real az = a * axis._z; + + // Fill in the matrix elements. We'll do the common + // subexpression optimization ourselves here, since diagonally + // opposite matrix elements are equal + res._m11 = (ax * axis._x) + 1.0; + res._m22 = (ay * axis._y) + 1.0; + res._m32 = (az * axis._z) + 1.0; + + res._m12 = res._m21 = (ax * axis._y); + res._m13 = res._m31 = (ax * axis._z); + res._m23 = res._m32 = (ay * axis._z); + + // Reset the translation portion + res._tx = res._ty = res._tz = 0.0; + + return res; +} + + +// -------------------------------------------------------------------------- +// ShearMatrix +// +// Setup the matrix to perform a shear +// +// The type of shear is specified by the 1-based "axis" index. The effect +// of transforming a point by the matrix is described by the pseudocode +// below: +// +// xAxis => y += s * x, z += t * x +// yAxis => x += s * y, z += t * y +// zAxis => x += s * z, y += t * z +// +// The translation portion is reset. +// -------------------------------------------------------------------------- + +template +inline Matrix4x4 +ShearMatrix (Axis axis, Real s, Real t) +{ + Matrix4x4 res; + + // Check which type of shear they want + switch (axis) + { + case kXaxis: // Shear y and z using x + res._m11 = 1.0; res._m21 = 0.0; res._m31 = 0.0; + res._m12 = s; res._m22 = 1.0; res._m32 = 0.0; + res._m13 = t; res._m23 = 0.0; res._m33 = 1.0; + break; + + case kYaxis: // Shear x and z using y + res._m11 = 1.0; res._m21 = s; res._m31 = 0.0; + res._m12 = 0.0; res._m22 = 1.0; res._m32 = 0.0; + res._m13 = 0.0; res._m23 = t; res._m33 = 1.0; + break; + + case kZaxis: // Shear x and y using z + res._m11 = 1.0; res._m21 = 0.0; res._m31 = s; + res._m12 = 0.0; res._m22 = 1.0; res._m32 = t; + res._m13 = 0.0; res._m23 = 0.0; res._m33 = 1.0; + break; + + default: + // bogus axis index + assert (false); + } + + // Reset the translation portion + res._tx = res._ty = res._tz = 0.0; + + return res; +} + + +// -------------------------------------------------------------------------- +// ProjectionMatrix +// +// Setup the matrix to perform a projection onto a plane passing +// through the origin. The plane is perpendicular to the +// unit vector n. +// -------------------------------------------------------------------------- + +template +inline Matrix4x4 +ProjectionMatrix (const Vector3 &n) +{ + Matrix4x4 res; + + // Quick sanity check to make sure they passed in a unit vector + // to specify the axis + assert (std::fabs (DotProduct (n, n) - 1.0) < 0.001); + + // Fill in the matrix elements. We'll do the common + // subexpression optimization ourselves here, since diagonally + // opposite matrix elements are equal + res._m11 = 1.0 - (n._x * n._x); + res._m22 = 1.0 - (n._y * n._y); + res._m33 = 1.0 - (n._z * n._z); + + res._m12 = res._m21 = -(n._x * n._y); + res._m13 = res._m31 = -(n._x * n._z); + res._m23 = res._m32 = -(n._y * n._z); + + // Reset the translation portion + res._tx = res._ty = res._tz = 0.0; + + return res; +} + + +// -------------------------------------------------------------------------- +// ReflectionMatrix +// +// Setup the matrix to perform a reflection about a plane parallel +// to a cardinal plane. +// +// axis is a 1-based index which specifies the plane to project about: +// +// xAxis => reflect about the plane x=k +// yAxis => reflect about the plane y=k +// zAxis => reflect about the plane z=k +// +// The translation is set appropriately, since translation must occur if +// k != 0 +// -------------------------------------------------------------------------- + +template +inline Matrix4x4 +ReflectionMatrix (Axis axis, Real k) +{ + Matrix4x4 res; + + // Check which plane they want to reflect about + switch (axis) + { + case kXaxis: // Reflect about the plane x=k + res._m11 = -1.0; res._m21 = 0.0; res._m31 = 0.0; res._tx = 2.0 * k; + res._m12 = 0.0; res._m22 = 1.0; res._m32 = 0.0; res._ty = 0.0; + res._m13 = 0.0; res._m23 = 0.0; res._m33 = 1.0; res._tz = 0.0; + break; + + case kYaxis: // Reflect about the plane y=k + res._m11 = 1.0; res._m21 = 0.0; res._m31 = 0.0; res._tx = 0.0; + res._m12 = 0.0; res._m22 = -1.0; res._m32 = 0.0; res._ty = 2.0 * k; + res._m13 = 0.0; res._m23 = 0.0; res._m33 = 1.0; res._tz = 0.0; + break; + + case kZaxis: // Reflect about the plane z=k + res._m11 = 1.0; res._m21 = 0.0; res._m31 = 0.0; res._tx = 0.0; + res._m12 = 0.0; res._m22 = 1.0; res._m32 = 0.0; res._ty = 0.0; + res._m13 = 0.0; res._m23 = 0.0; res._m33 = -1.0; res._tz = 2.0 * k; + break; + + default: + // bogus axis index + assert (false); + } + + return res; +} + + +// -------------------------------------------------------------------------- +// AxisReflectionMatrix +// +// Setup the matrix to perform a reflection about an arbitrary plane +// through the origin. The unit vector n is perpendicular to the plane. +// +// The translation portion is reset. +// -------------------------------------------------------------------------- + +template +inline Matrix4x4 +AxisReflectionMatrix (const Vector3 &n) +{ + Matrix4x4 res; + + // Quick sanity check to make sure they passed in a unit vector + // to specify the axis + assert (std::fabs (DotProduct (n, n) - 1.0) < 0.001); + + // Compute common subexpressions + Real ax = -2.0 * n._x; + Real ay = -2.0 * n._y; + Real az = -2.0 * n._z; + + // Fill in the matrix elements. We'll do the common + // subexpression optimization ourselves here, since diagonally + // opposite matrix elements are equal + res._m11 = 1.0 + (ax * n._x); + res._m22 = 1.0 + (ay * n._y); + res._m32 = 1.0 + (az * n._z); + + res._m12 = res._m21 = (ax * n._y); + res._m13 = res._m31 = (ax * n._z); + res._m23 = res._m32 = (ay * n._z); + + // Reset the translation portion + res._tx = res._ty = res._tz = 0.00; + + return res; +} + + +// -------------------------------------------------------------------------- +// LookAtMatrix +// +// Setup the matrix to perform a "Look At" transformation like a first +// person camera. +// -------------------------------------------------------------------------- +template +inline Matrix4x4 +LookAtMatrix (const Vector3 &camPos, const Vector3 &target, + const Vector3 &camUp) +{ + Matrix4x4 rot, trans; + + Vector3 forward (camPos - target); + forward.normalize (); + + Vector3 right (CrossProduct (camUp, forward)); + Vector3 up (CrossProduct (forward, right)); + + right.normalize (); + up.normalize (); + + rot._m11 = right._x; + rot._m21 = right._y; + rot._m31 = right._z; + + rot._m12 = up._x; + rot._m22 = up._y; + rot._m32 = up._z; + + rot._m13 = forward._x; + rot._m23 = forward._y; + rot._m33 = forward._z; + + rot._tx = 0.0; + rot._ty = 0.0; + rot._tz = 0.0; + + trans = TranslationMatrix (-camPos); + + return (rot * trans); +} + + +// -------------------------------------------------------------------------- +// FrustumMatrix +// +// Setup a frustum matrix given the left, right, bottom, top, near, and far +// values for the frustum boundaries. +// -------------------------------------------------------------------------- + +template +inline Matrix4x4 +FrustumMatrix (Real l, Real r, Real b, Real t, Real n, Real f) +{ + assert (n >= 0.0); + assert (f >= 0.0); + + Matrix4x4 res; + + Real width = r - l; + Real height = t - b; + Real depth = f - n; + + res._m[0] = (2 * n) / width; + res._m[1] = 0.0; + res._m[2] = 0.0; + res._m[3] = 0.0; + + res._m[4] = 0.0; + res._m[5] = (2 * n) / height; + res._m[6] = 0.0; + res._m[7] = 0.0; + + res._m[8] = (r + l) / width; + res._m[9] = (t + b) / height; + res._m[10]= -(f + n) / depth; + res._m[11]= -1.0; + + res._m[12]= 0.0; + res._m[13]= 0.0; + res._m[14]= -(2 * f * n) / depth; + res._m[15]= 0.0; + + return res; +} + + +// -------------------------------------------------------------------------- +// PerspectiveMatrix +// +// Setup a perspective matrix given the field-of-view in the Y direction +// in degrees, the aspect ratio of Y/X, and near and far plane distances. +// -------------------------------------------------------------------------- + +template +inline Matrix4x4 +PerspectiveMatrix (Real fovY, Real aspect, Real n, Real f) +{ + Matrix4x4 res; + + Real angle; + Real cot; + + angle = fovY / 2.0; + angle = degToRad (angle); + + cot = std::cos (angle) / std::sin (angle); + + res._m[0] = cot / aspect; + res._m[1] = 0.0; + res._m[2] = 0.0; + res._m[3] = 0.0; + + res._m[4] = 0.0; + res._m[5] = cot; + res._m[6] = 0.0; + res._m[7] = 0.0; + + res._m[8] = 0.0; + res._m[9] = 0.0; + res._m[10]= -(f + n) / (f - n); + res._m[11]= -1.0; + + res._m[12]= 0.0; + res._m[13]= 0.0; + res._m[14]= -(2 * f * n) / (f - n); + res._m[15]= 0.0; + + return res; +} + + +// -------------------------------------------------------------------------- +// OrthoMatrix +// +// Setup an orthographic Matrix4x4 given the left, right, bottom, top, near, +// and far values for the frustum boundaries. +// -------------------------------------------------------------------------- + +template +inline Matrix4x4 +OrthoMatrix (Real l, Real r, Real b, Real t, Real n, Real f) +{ + Matrix4x4 res; + + Real width = r - l; + Real height = t - b; + Real depth = f - n; + + res._m[0] = 2.0 / width; + res._m[1] = 0.0; + res._m[2] = 0.0; + res._m[3] = 0.0; + + res._m[4] = 0.0; + res._m[5] = 2.0 / height; + res._m[6] = 0.0; + res._m[7] = 0.0; + + res._m[8] = 0.0; + res._m[9] = 0.0; + res._m[10]= -2.0 / depth; + res._m[11]= 0.0; + + res._m[12]= -(r + l) / width; + res._m[13]= -(t + b) / height; + res._m[14]= -(f + n) / depth; + res._m[15]= 1.0; + + return res; +} + + +// -------------------------------------------------------------------------- +// OrthoNormalMatrix +// +// Setup an orientation matrix using 3 basis normalized vectors. +// -------------------------------------------------------------------------- + +template +inline Matrix4x4 +OrthoNormalMatrix (const Vector3 &xdir, const Vector3 &ydir, + const Vector3 &zdir) +{ + Matrix4x4 res; + + res._m[0] = xdir._x; res._m[4] = ydir._x; res._m[8] = zdir._x; res._m[12] = 0.0; + res._m[1] = xdir._y; res._m[5] = ydir._y; res._m[9] = zdir._y; res._m[13] = 0.0; + res._m[2] = xdir._z; res._m[6] = ydir._z; res._m[10]= zdir._z; res._m[14] = 0.0; + res._m[3] = 0.0; res._m[7] = 0.0; res._m[11]= 0.0; res._m[15] = 1.0; + + return res; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// class Quaternion implementation. +// +///////////////////////////////////////////////////////////////////////////// + +// -------------------------------------------------------------------------- +// Quaternion::identity +// +// Set to identity +// -------------------------------------------------------------------------- + +template +inline void +Quaternion::identity () +{ + _w = 1.0; _x = _y = _z = 0.0; +} + + +// -------------------------------------------------------------------------- +// Quaternion::normalize +// +// "Normalize" a quaternion. Note that normally, quaternions +// are always normalized (within limits of numerical precision). +// +// This function is provided primarily to combat floating point "error +// creep", which can occur when many successive quaternion operations +// are applied. +// -------------------------------------------------------------------------- + +template +inline void +Quaternion::normalize () +{ + // Compute magnitude of the quaternion + Real mag = std::sqrt ((_w * _w) + (_x * _x) + (_y * _y) + (_z * _z)); + + // Check for bogus length, to protect against divide by zero + if (mag > 0.0) + { + // Normalize it + Real oneOverMag = 1.0 / mag; + + _w *= oneOverMag; + _x *= oneOverMag; + _y *= oneOverMag; + _z *= oneOverMag; + } +} + + +// -------------------------------------------------------------------------- +// Quaternion::computeW +// +// Compute the W component of a unit quaternion given its x,y,z components. +// -------------------------------------------------------------------------- + +template +inline void +Quaternion::computeW () +{ + Real t = 1.0 - (_x * _x) - (_y * _y) - (_z * _z); + + if (t < 0.0) + _w = 0.0; + else + _w = -std::sqrt (t); +} + + +// -------------------------------------------------------------------------- +// Quaternion::rotate +// +// Rotate a point by quaternion. v' = q.p.q*, where p = <0, v>. +// -------------------------------------------------------------------------- + +template +inline void +Quaternion::rotate (Vector3 &v) const +{ + Quaternion qf = *this * v * ~(*this); + v._x = qf._x; + v._y = qf._y; + v._z = qf._z; +} + + +// -------------------------------------------------------------------------- +// Quaternion::fromMatrix +// +// Setup the quaternion to perform a rotation, given the angular displacement +// in matrix form. +// -------------------------------------------------------------------------- + +template +inline void +Quaternion::fromMatrix (const Matrix4x4 &m) +{ + Real trace = m._m11 + m._m22 + m._m33 + 1.0; + + if (trace > 0.0001) + { + Real s = 0.5 / std::sqrt (trace); + _w = 0.25 / s; + _x = (m._m23 - m._m32) * s; + _y = (m._m31 - m._m13) * s; + _z = (m._m12 - m._m21) * s; + } + else + { + if ((m._m11 > m._m22) && (m._m11 > m._m33)) + { + Real s = 0.5 / std::sqrt (1.0 + m._m11 - m._m22 - m._m33); + _x = 0.25 / s; + _y = (m._m21 + m._m12) * s; + _z = (m._m31 + m._m13) * s; + _w = (m._m32 - m._m23) * s; + } + else if (m._m22 > m._m33) + { + Real s = 0.5 / std::sqrt (1.0 + m._m22 - m._m11 - m._m33); + _x = (m._m21 + m._m12) * s; + _y = 0.25 / s; + _z = (m._m32 + m._m23) * s; + _w = (m._m31 - m._m13) * s; + } + else + { + Real s = 0.5 / std::sqrt (1.0 + m._m33 - m._m11 - m._m22); + _x = (m._m31 + m._m13) * s; + _y = (m._m32 + m._m23) * s; + _z = 0.25 / s; + _w = (m._m21 - m._m12) * s; + } + } +} + + +// -------------------------------------------------------------------------- +// Quaternion::fromEulerAngles +// +// Setup the quaternion to perform an object->inertial rotation, given the +// orientation in XYZ-Euler angles format. x,y,z parameters must be in +// radians. +// -------------------------------------------------------------------------- + +template +inline void +Quaternion::fromEulerAngles (Real x, Real y, Real z) +{ + // Compute sine and cosine of the half angles + Real sr = std::sin (x * 0.5); + Real cr = std::cos (x * 0.5); + Real sp = std::sin (y * 0.5); + Real cp = std::cos (y * 0.5); + Real sy = std::sin (z * 0.5); + Real cy = std::cos (z * 0.5); + + // Compute values + _w = (cy * cp * cr) + (sy * sp * sr); + _x = -(sy * sp * cr) + (cy * cp * sr); + _y = (cy * sp * cr) + (sy * cp * sr); + _z = -(cy * sp * sr) + (sy * cp * cr); +} + + +// -------------------------------------------------------------------------- +// Quaternion::toEulerAngles +// +// Setup the Euler angles, given an object->inertial rotation quaternion. +// Returned x,y,z are in radians. +// -------------------------------------------------------------------------- + +template +inline void +Quaternion::toEulerAngles (Real &x, Real &y, Real &z) const +{ + // Compute Y-axis angle + y = std::asin (2.0 * ((_x * _z) + (_w * _y))); + + // Compute cos and one over cos for optimization + Real cy = std::cos (y); + Real oneOverCosY = 1.0 / cy; + + if (std::fabs (cy) > 0.001) + { + // No gimball lock + x = std::atan2 (2.0 * ((_w * _x) - (_y * _z)) * oneOverCosY, + (1.0 - 2.0 * (_x*_x + _y*_y)) * oneOverCosY); + z = std::atan2 (2.0 * ((_w * _z) - (_x * _y)) * oneOverCosY, + (1.0 - 2.0 * (_y*_y + _z*_z)) * oneOverCosY); + } + else + { + // Gimbal lock case + x = 0.0; + z = std::atan2 (2.0 * ((_x * _y) + (_w * _z)), + 1.0 - 2.0 * (_x*_x + _z*_z)); + } +} + + +// -------------------------------------------------------------------------- +// Quaternion::getRotationAngle +// +// Return the rotation angle theta (in radians). +// -------------------------------------------------------------------------- + +template +inline Real +Quaternion::rotationAngle () const +{ + // Compute the half angle. Remember that w = cos(theta / 2) + Real thetaOver2 = safeAcos (_w); + + // Return the rotation angle + return thetaOver2 * 2.0; +} + + +// -------------------------------------------------------------------------- +// Quaternion::getRotationAxis +// +// Return the rotation axis. +// -------------------------------------------------------------------------- + +template +inline Vector3 +Quaternion::rotationAxis () const +{ + // Compute sin^2(theta/2). Remember that w = cos(theta/2), + // and sin^2(x) + cos^2(x) = 1 + Real sinThetaOver2Sq = 1.0 - (_w * _w); + + // Protect against numerical imprecision + if (sinThetaOver2Sq <= 0.0) + { + // Identity quaternion, or numerical imprecision. Just + // return any valid vector, since it doesn't matter + + return Vector3 (1.0, 0.0, 0.0); + } + + // Compute 1 / sin(theta/2) + Real oneOverSinThetaOver2 = 1.0 / std::sqrt (sinThetaOver2Sq); + + // Return axis of rotation + return Vector3 ( + _x * oneOverSinThetaOver2, + _y * oneOverSinThetaOver2, + _z * oneOverSinThetaOver2 + ); +} + + +// -------------------------------------------------------------------------- +// Quaternion operators +// +// Operator overloading for basic quaternion operations. +// -------------------------------------------------------------------------- + +template +inline Quaternion +Quaternion::operator+ (const Quaternion &q) const +{ + return Quaternion (_w + q._w, _x + q._x, _y + q._y, _z + q._z); +} + +template +inline Quaternion & +Quaternion::operator+= (const Quaternion &q) +{ + _w += q._w; _x += q._x; _y += q._y; _z += q._z; + return *this; +} + +template +inline Quaternion +Quaternion::operator- (const Quaternion &q) const +{ + return Quaternion (_w - q._w, _x - q._x, _y - q._y, _z - q._z); +} + +template +inline Quaternion & +Quaternion::operator-= (const Quaternion &q) +{ + _w -= q._w; _x -= q._x; _y -= q._y; _z -= q._z; + return *this; +} + +// Quaternion multiplication. The order of multiplication, from left +// to right, corresponds to the order of concatenated rotations. +// NOTE: Quaternion multiplication is NOT commutative, p * q != q * p +template +inline Quaternion +Quaternion::operator* (const Quaternion &q) const +{ + // We use the Grassman product formula: + // pq = + return Quaternion ( + (_w * q._w) - (_x * q._x) - (_y * q._y) - (_z * q._z), + (_x * q._w) + (_w * q._x) + (_y * q._z) - (_z * q._y), + (_y * q._w) + (_w * q._y) + (_z * q._x) - (_x * q._z), + (_z * q._w) + (_w * q._z) + (_x * q._y) - (_y * q._x) + ); +} + +template +inline Quaternion & +Quaternion::operator*= (const Quaternion &q) +{ + *this = *this * q; + return *this; +} + +template +inline Quaternion +Quaternion::operator* (const Vector3 &v) const +{ + // q * v = q * p where p = <0,v> + // Thus, we can simplify the operations. + return Quaternion ( + - (_x * v._x) - (_y * v._y) - (_z * v._z), + (_w * v._x) + (_y * v._z) - (_z * v._y), + (_w * v._y) + (_z * v._x) - (_x * v._z), + (_w * v._z) + (_x * v._y) - (_y * v._x) + ); +} + +template +inline Quaternion & +Quaternion::operator*= (const Vector3 &v) +{ + *this = *this * v; + return *this; +} + +template +inline Quaternion +Quaternion::operator* (Real k) const +{ + return Quaternion (_w * k, _x * k, _y * k, _z * k); +} + +template +inline Quaternion & +Quaternion::operator*= (Real k) +{ + _w *= k; _x *= k; _y *= k; _z *= k; + return *this; +} + +template +inline Quaternion +Quaternion::operator/ (Real k) const +{ + Real oneOverK = 1.0 / k; + return Quaternion (_w * oneOverK, _x * oneOverK, _y * oneOverK, _z * oneOverK); +} + +template +inline Quaternion & +Quaternion::operator/= (Real k) +{ + Real oneOverK = 1.0 / k; + _w *= oneOverK; _x *= oneOverK; _y *= oneOverK; _z *= oneOverK; + return *this; +} + +// Quaternion conjugate +template +inline Quaternion +Quaternion::operator~ () const +{ + return Quaternion (_w, -_x, -_y, -_z); +} + + +// Quaternion negation +template +inline Quaternion +Quaternion::operator- () const +{ + return Quaternion (-_w, -_x, -_y, -_z); +} + + +// -------------------------------------------------------------------------- +// +// Nonmember Quaternion functions +// +// -------------------------------------------------------------------------- + +// Scalar on left multiplication +template +inline Quaternion +operator* (Real k, const Quaternion &q) +{ + return Quaternion (q._w * k, q._x * k, q._y * k, q._z * k); +} + +// Quaternion dot product +template +inline Real +DotProduct (const Quaternion &a, const Quaternion &b) +{ + return ((a._w * b._w) + (a._x * b._x) + (a._y * b._y) + (a._z * b._z)); +} + +// Compute the quaternion conjugate. This is the quaternian +// with the opposite rotation as the original quaternion. +template +inline Quaternion +Conjugate (const Quaternion &q) +{ + return Quaternion (q._w, -q._x, -q._y, -q._z); +} + + +// Compute the inverse quaternion (for unit quaternion only). +template +inline Quaternion +Inverse (const Quaternion &q) +{ + // Assume this is a unit quaternion! No check for this! + Quaternion res (q._w, -q._x, -q._y, -q._z); + res.normalize (); + return res; +} + + +// -------------------------------------------------------------------------- +// RotationQuaternion +// +// Setup the quaternion to rotate about the specified axis. theta must +// be in radians. +// -------------------------------------------------------------------------- + +template +inline Quaternion +RotationQuaternion (Axis axis, Real theta) +{ + Quaternion res; + + // Compute the half angle + Real thetaOver2 = theta * 0.5; + + // Set the values + switch (axis) + { + case kXaxis: + res._w = std::cos (thetaOver2); + res._x = std::sin (thetaOver2); + res._y = 0.0; + res._z = 0.0; + break; + + case kYaxis: + res._w = std::cos (thetaOver2); + res._x = 0.0; + res._y = std::sin (thetaOver2); + res._z = 0.0; + break; + + case kZaxis: + res._w = std::cos (thetaOver2); + res._x = 0.0; + res._y = 0.0; + res._z = std::sin (thetaOver2); + break; + + default: + // Bad axis + assert (false); + } + + return res; +} + +template +inline Quaternion +RotationQuaternion (const Vector3 &axis, Real theta) +{ + Quaternion res; + + // The axis of rotation must be normalized + assert (std::fabs (DotProduct (axis, axis) - 1.0) < 0.001); + + // Compute the half angle and its sin + Real thetaOver2 = theta * 0.5; + Real sinThetaOver2 = std::sin (thetaOver2); + + // Set the values + res._w = std::cos (thetaOver2); + res._x = axis._x * sinThetaOver2; + res._y = axis._y * sinThetaOver2; + res._z = axis._z * sinThetaOver2; + + return res; +} + + +// -------------------------------------------------------------------------- +// Log +// +// Unit quaternion logarithm. log(q) = log(cos(theta) + n*sin(theta)) +// -------------------------------------------------------------------------- + +template +inline Quaternion +Log (const Quaternion &q) +{ + Quaternion res; + res._w = 0.0; + + if (std::fabs (q._w) < 1.0) + { + Real theta = std::acos (q._w); + Real sin_theta = std::sin (theta); + + if (std::fabs (sin_theta) > 0.00001) + { + Real thetaOverSinTheta = theta / sin_theta; + res._x = q._x * thetaOverSinTheta; + res._y = q._y * thetaOverSinTheta; + res._z = q._z * thetaOverSinTheta; + return res; + } + } + + res._x = q._x; + res._y = q._y; + res._z = q._z; + + return res; +} + + +// -------------------------------------------------------------------------- +// Exp +// +// Quaternion exponential. +// -------------------------------------------------------------------------- + +template +inline Quaternion +Exp (const Quaternion &q) +{ + Real theta = std::sqrt (DotProduct (q, q)); + Real sin_theta = std::sin (theta); + + Quaternion res; + res._w = std::cos (theta); + + if (std::fabs (sin_theta) > 0.00001) + { + Real sinThetaOverTheta = sin_theta / theta; + res._x = q._x * sinThetaOverTheta; + res._y = q._y * sinThetaOverTheta; + res._z = q._z * sinThetaOverTheta; + } + else + { + res._x = q._x; + res._y = q._y; + res._z = q._z; + } + + return res; +} + + +// -------------------------------------------------------------------------- +// Pow +// +// Quaternion exponentiation. +// -------------------------------------------------------------------------- + +template +inline Quaternion +Pow (const Quaternion &q, Real exponent) +{ + // Check for the case of an identity quaternion. + // This will protect against divide by zero + if (std::fabs (q._w) > 0.9999) + return q; + + // Extract the half angle alpha (alpha = theta/2) + Real alpha = std::acos (q._w); + + // Compute new alpha value + Real newAlpha = alpha * exponent; + + // Compute new quaternion + Vector3 n (q._x, q._y, q._z); + n *= std::sin (newAlpha) / std::sin (alpha); + + return Quaternion (std::cos (newAlpha), n); +} + + +// -------------------------------------------------------------------------- +// Slerp +// +// Spherical linear interpolation. +// -------------------------------------------------------------------------- + +template +inline Quaternion +Slerp (const Quaternion &q0, const Quaternion &q1, Real t) +{ + // Check for out-of range parameter and return edge points if so + if (t <= 0.0) return q0; + if (t >= 1.0) return q1; + + // Compute "cosine of angle between quaternions" using dot product + Real cosOmega = DotProduct (q0, q1); + + // If negative dot, use -q1. Two quaternions q and -q + // represent the same rotation, but may produce + // different slerp. We chose q or -q to rotate using + // the acute angle. + Real q1w = q1._w; + Real q1x = q1._x; + Real q1y = q1._y; + Real q1z = q1._z; + + if (cosOmega < 0.0) + { + q1w = -q1w; + q1x = -q1x; + q1y = -q1y; + q1z = -q1z; + cosOmega = -cosOmega; + } + + // We should have two unit quaternions, so dot should be <= 1.0 + assert (cosOmega < 1.1); + + // Compute interpolation fraction, checking for quaternions + // almost exactly the same + Real k0, k1; + + if (cosOmega > 0.9999) + { + // Very close - just use linear interpolation, + // which will protect againt a divide by zero + + k0 = 1.0 - t; + k1 = t; + } + else + { + // Compute the sin of the angle using the + // trig identity sin^2(omega) + cos^2(omega) = 1 + Real sinOmega = std::sqrt (1.0 - (cosOmega * cosOmega)); + + // Compute the angle from its sin and cosine + Real omega = std::atan2 (sinOmega, cosOmega); + + // Compute inverse of denominator, so we only have + // to divide once + Real oneOverSinOmega = 1.0 / sinOmega; + + // Compute interpolation parameters + k0 = std::sin ((1.0 - t) * omega) * oneOverSinOmega; + k1 = std::sin (t * omega) * oneOverSinOmega; + } + + // Interpolate and return new quaternion + return Quaternion ( + (k0 * q0._w) + (k1 * q1w), + (k0 * q0._x) + (k1 * q1x), + (k0 * q0._y) + (k1 * q1y), + (k0 * q0._z) + (k1 * q1z) + ); +} + + +// -------------------------------------------------------------------------- +// Squad +// +// Spherical cubic interpolation. +// squad = slerp (slerp (q0, q1, t), slerp (qa, qb, t), 2t(1 - t)). +// -------------------------------------------------------------------------- + +template +inline Quaternion +Squad (const Quaternion &q0, const Quaternion &qa, + const Quaternion &qb, const Quaternion &q1, Real t) +{ + Real slerp_t = 2.0 * t * (1.0 - t); + + Quaternion slerp_q0 = Slerp (q0, q1, t); + Quaternion slerp_q1 = Slerp (qa, qb, t); + + return Slerp (slerp_q0, slerp_q1, slerp_t); +} + + +// -------------------------------------------------------------------------- +// Intermediate +// +// Compute intermediate quaternions for building spline segments. +// -------------------------------------------------------------------------- + +template +inline void +Intermediate (const Quaternion &qprev, const Quaternion &qcurr, + const Quaternion &qnext, Quaternion &qa, Quaternion &qb) +{ + // We should have unit quaternions + assert (DotProduct (qprev, qprev) <= 1.0001); + assert (DotProduct (qcurr, qcurr) <= 1.0001); + + Quaternion inv_prev = Conjugate (qprev); + Quaternion inv_curr = Conjugate (qcurr); + + Quaternion p0 = inv_prev * qcurr; + Quaternion p1 = inv_curr * qnext; + + Quaternion arg = (Log (p0) - Log (p1)) * 0.25; + + qa = qcurr * Exp ( arg); + qb = qcurr * Exp (-arg); +} diff --git a/source/shared_lib/include/graphics/md5/Md5Model.h b/source/shared_lib/include/graphics/md5/Md5Model.h new file mode 100644 index 00000000..a4dff0e4 --- /dev/null +++ b/source/shared_lib/include/graphics/md5/Md5Model.h @@ -0,0 +1,522 @@ +// ============================================================== +// This file is part of MegaGlest (www.glest.org) +// +// Md5Model.h -- Copyright (c) 2006 David Henry +// changed for use with MegaGlest: Copyright (C) 2011- by Mark Vejvoda +// +// This code is licenced under the MIT license. +// +// This software is provided "as is" without express or implied +// warranties. You may freely copy and compile this source into +// applications you distribute provided that the copyright text +// below is included in the resulting source code. +// +// Declarations for MD5 Model Classes (object, mesh, animation and +// skeleton). +// +///////////////////////////////////////////////////////////////////////////// +#ifndef __MD5_H__ +#define __MD5_H__ + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif // _WIN32 + +#include +#include +#include +#include +#include +//#include +#include + +#include "Mathlib.h" +#include "Texture.h" + +namespace Shared { namespace Graphics { namespace md5 { + +using std::string; +using std::vector; +using std::map; + +//using std::shared_ptr; + +// Forward declarations +class Md5Skeleton; +class Md5Mesh; +class Md5Model; +class Md5Animation; +class Md5Object; +class ShaderProgram; +class ArbVertexProgram; +class ArbFragmentProgram;; + +// MD5 Constants +extern const int kMd5Version; + +// We can use a specific render path, depending on +// which shader/program we want to use... +enum render_path_e + { + R_normal, + R_ARBfp_diffuse, + R_ARBfp_diffuse_specular, + R_ARBfp_ds_parallax, + R_shader + }; + +// Tangent uniform's location +//extern GLint tangentLoc; + +// ARB program's tangent location +#define TANGENT_LOC 6 + +// OpenGL vector types +typedef GLfloat vec2_t[2]; +typedef GLfloat vec3_t[3]; +typedef GLfloat vec4_t[4]; + +struct Md5Joint_t +{ + string name; + int parent; + + Vector3f pos; + Quaternionf orient; +}; + + +struct Md5Vertex_t +{ + float st[2]; // Texture coordinates + + int startWeight; // Start index weights + int countWeight; // Number of weights +}; + + +struct Md5Triangle_t +{ + int index[3]; // Vertex indices +}; + + +struct Md5Weight_t +{ + int joint; // Joint index + float bias; + + Vector3f pos; + Vector3f norm; + Vector3f tan; +}; + + +struct BoundingBox_t +{ + Vector3f min; + Vector3f max; +}; + + +struct OBBox_t +{ + Matrix4x4f world; + + Vector3f center; + Vector3f extent; +}; + + +///////////////////////////////////////////////////////////////////////////// +// +// class Md5Exception - Exception class for MD5 loader classes. +// This acts like a standard runtime_error exception but +// know which file or mesh has failed to be loaded. +// +///////////////////////////////////////////////////////////////////////////// + +class Md5Exception : public std::runtime_error +{ +public: + // Constructors + Md5Exception (const string &error) + : std::runtime_error (error) { } + Md5Exception (const string &error, const string &name) + : std::runtime_error (error), _which (name) { } + virtual ~Md5Exception () throw () { } + +public: + // Public interface + virtual const char *which () const throw () { + return _which.c_str (); + } + +private: + // Member variables + string _which; +}; + + +///////////////////////////////////////////////////////////////////////////// +// +// class Md5Skeleton - Skeleton model data class. +// +///////////////////////////////////////////////////////////////////////////// + +class Md5Skeleton +{ +public: + // Constructors/Destructor + Md5Skeleton () { } + Md5Skeleton (std::ifstream &file, int numJoints) + throw (Md5Exception); + ~Md5Skeleton (); + +private: + // Internal types + typedef shared_ptr Md5JointPtr; + +public: + // Public interface + void draw (const Matrix4x4f &modelView, bool labelJoints); + + void setNumJoints (int numJoints); + void addJoint (Md5Joint_t *thisJoint); + + Md5Skeleton *clone () const; + + // Accessors + int numJoints () const { return _joints.size (); } + Md5Joint_t *joint (int index) const { return _joints[index].get (); } + +private: + // Member variables + vector _joints; +}; + + +///////////////////////////////////////////////////////////////////////////// +// +// class Md5Mesh - Mesh data class. +// +///////////////////////////////////////////////////////////////////////////// + +class Md5Mesh +{ +public: + // Public internal types + enum + { + kHide, // Skip mesh + kNoDraw, // Don't draw but prepare vertices + kShow, // Draw mesh + }; + + typedef shared_ptr Md5VertexPtr; + typedef shared_ptr Md5TrianglePtr; + typedef shared_ptr Md5WeightPtr; + +public: + // Constructor/Destructor + Md5Mesh (std::ifstream &ifs) + throw (Md5Exception); + ~Md5Mesh (); + +public: + // Public interface + void setupVertexArrays (Md5Skeleton *skel); + void computeWeightNormals (Md5Skeleton *skel); + void computeWeightTangents (Md5Skeleton *skel); + void computeBoundingBox (Md5Skeleton *skel); + void renderVertexArrays () const; + void drawNormals () const; + + // Hide/NoDraw/Show state + void setState (int state) { _renderState = state; } + + // Texture setters + void setDecalMap (const Texture2D *tex) { _decal = tex; } + void setSpecularMap (const Texture2D *tex) { _specMap = tex; } + void setNormalMap (const Texture2D *tex) { _normalMap = tex; } + void setHeightMap (const Texture2D *tex) { _heightMap = tex; } + + // Accessors + const string &name () const { return _name; } + const BoundingBox_t &boundingBox () const { return _boundingBox; } + + // Mesh render state + bool hiden () const { return (_renderState == kHide); } + bool noDraw () const { return (_renderState == kNoDraw); } + bool show () const { return (_renderState == kShow); } + +private: + // Internal functions + void preRenderVertexArrays () const; + void postRenderVertexArrays () const; + void allocVertexArrays (); + void setupTexCoordArray (); + void setupTexture (const Texture2D *tex, GLenum texUnit) const; + void resetReversedTexture(const Texture2D *tex, GLenum texUnit) const; + +private: + // Member variables + string _name; + string _shader; + int _renderState; + + BoundingBox_t _boundingBox; + + int _numVerts; + int _numTris; + int _numWeights; + + // Original mesh data + vector _verts; + vector _tris; + vector _weights; + + // Final mesh data; vertex arrays for fast rendering + vector _vertexArray; + vector _normalArray; + vector _tangentArray; + vector _texCoordArray; + vector _vertIndices; + + // Textures + const Texture2D *_decal; + const Texture2D *_specMap; + const Texture2D *_normalMap; + const Texture2D *_heightMap; +}; + + +///////////////////////////////////////////////////////////////////////////// +// +// class Md5Model - MD5 Mesh Model class. +// +///////////////////////////////////////////////////////////////////////////// + +class Md5Model +{ +public: + // Constructor/Destructor + Md5Model (const string &filename) + throw (Md5Exception); + ~Md5Model (); + +public: + // Internal type definitions + typedef shared_ptr Md5SkeletonPtr; + typedef shared_ptr Md5MeshPtr; + typedef shared_ptr Md5AnimationPtr; + typedef map AnimMap; + +public: + // Public interface + void prepare (Md5Skeleton *skel); + void drawModel () const; + bool addAnim (const string &filename); + + // Setters + void setMeshRenderState (const string &name, int state); + void setMeshDecalMap (const string &name, const Texture2D *tex); + void setMeshSpecularMap (const string &name, const Texture2D *tex); + void setMeshNormalMap (const string &name, const Texture2D *tex); + void setMeshHeightMap (const string &name, const Texture2D *tex); + + // Accessors + const Md5Animation *anim (const string &name) const; + int numJoints () const { return _numJoints; } + const Md5Skeleton *baseSkeleton () const { return _baseSkeleton.get (); } + const AnimMap &anims () const { return _animList; } + const BoundingBox_t &bindPoseBoundingBox () const { return _bindPoseBox; } + + static render_path_e renderPath; + static ShaderProgram *shader; + static ArbVertexProgram *vp; + static ArbFragmentProgram *fp; + static GLint tangentLoc; + static bool bDrawNormals; + +private: + // Internal functions + void computeBindPoseBoundingBox (); + + // Check if an animation is valid for this model, + // i.e. anim's skeleton matches with model's skeleton + bool validityCheck (Md5Animation *anim) const; + + // Access to a mesh, given its name + Md5Mesh *getMeshByName (const string &name) const; + +private: + // Member variables + int _numJoints; + int _numMeshes; + + Md5SkeletonPtr _baseSkeleton; + + vector _meshes; + AnimMap _animList; + + BoundingBox_t _bindPoseBox; +}; + + +///////////////////////////////////////////////////////////////////////////// +// +// class Md5Animation - MD5 model animation class. +// +///////////////////////////////////////////////////////////////////////////// + +class Md5Animation +{ +public: + // Constructor/Destructor + Md5Animation (const string &filename) + throw (Md5Exception); + ~Md5Animation (); + +private: + // Internal type + struct JointInfo + { + string name; + int parent; + + // NOTE: this structure is stored in + // little-endian format + union JointFlags + { + short value; + + struct + { + bool tx: 1; + bool ty: 1; + bool tz: 1; + + bool qx: 1; + bool qy: 1; + bool qz: 1; + }; + } flags; + + int startIndex; + }; + + struct BaseFrameJoint + { + Vector3f pos; + Quaternionf orient; + }; + + typedef shared_ptr Md5SkeletonPtr; + typedef shared_ptr BoundingBoxPtr; + +public: + // Public interface + void interpolate (int frameA, int frameB, + float interp, Md5Skeleton *out) const; + + // Accessors + int maxFrame () const { return _numFrames - 1; } + int frameRate () const { return _frameRate; } + const string &name () const { return _name; } + + Md5Skeleton *frame (int frame) const { + return _skelframes[frame].get (); + } + + const BoundingBox_t *frameBounds (int frame) const { + return _bboxes[frame].get (); + } + +private: + // Internal functions + void buildFrameSkeleton (vector &jointInfos, + vector &baseFrame, + vector &animFrameData); + +private: + // Member variables + int _numFrames; + int _frameRate; + + string _name; + + // Store each frame as a skeleton + vector _skelframes; + + // Bounding boxes for each frame + vector _bboxes; +}; + + +///////////////////////////////////////////////////////////////////////////// +// +// class Md5Object - MD5 object class. +// +///////////////////////////////////////////////////////////////////////////// + +class Md5Object +{ +public: + // Public internal types/enums + enum + { + kDrawModel = 1, + kDrawSkeleton = 2, + kDrawJointLabels = 4, + }; + +public: + // Contructor/Destructor + Md5Object (Md5Model *model); + virtual ~Md5Object (); + +public: + // Public interface + void animate (double dt); + void computeBoundingBox (); + void prepare (bool softwareTransformation); + void render () const; + + // Setters + void setMd5Model (Md5Model *model); + void setAnim (const string &name); + void setModelViewMatrix (const Matrix4x4f &mat) { _modelView = mat; } + void setRenderFlags (int flags) { _renderFlags = flags; } + + // Accessors + int renderFlags () const { return _renderFlags; } + const Md5Model *getModelPtr () const { return _model; } + const string currAnimName () const { return _currAnimName; } + const OBBox_t &boundingBox () const { return _bbox; } + +protected: + // Member variables; + Md5Model *_model; + Md5Skeleton *_animatedSkeleton; + + Matrix4x4f _modelView; + bool _softwareTransformation; + + const Md5Animation *_currAnim; + string _currAnimName; + unsigned int _currFrame; + unsigned int _nextFrame; + + double _last_time; + double _max_time; + + int _renderFlags; + + OBBox_t _bbox; +}; + +}}} //end namespace + +#endif // __MD5_H__ diff --git a/source/shared_lib/include/graphics/md5/Shader.h b/source/shared_lib/include/graphics/md5/Shader.h new file mode 100644 index 00000000..bbe337e7 --- /dev/null +++ b/source/shared_lib/include/graphics/md5/Shader.h @@ -0,0 +1,171 @@ +// ============================================================== +// This file is part of MegaGlest (www.glest.org) +// +// Shader.h -- Copyright (c) 2006 David Henry +// changed for use with MegaGlest: Copyright (C) 2011- by Mark Vejvoda +// +// This code is licenced under the MIT license. +// +// This software is provided "as is" without express or implied +// warranties. You may freely copy and compile this source into +// applications you distribute provided that the copyright text +// below is included in the resulting source code. +// +// Definitions of GLSL shader related classes. +// +///////////////////////////////////////////////////////////////////////////// +#ifndef __SHADER_H__ +#define __SHADER_H__ + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif // _WIN32 + +#include +#include +#include + +namespace Shared { namespace Graphics { namespace md5 { + +using std::string; + +///////////////////////////////////////////////////////////////////////////// +// Shader class diagram: +// +// +-------- (abs) +---------------+ +// | Shader | | ShaderProgram | +// +----------+ +---------------+ +// ^ +// | +// +----------+----------+ +// | | +// +--------------+ +----------------+ +// | VertexShader | | FragmentShader | +// +--------------+ +----------------+ +// +///////////////////////////////////////////////////////////////////////////// + +// Global functions for initializing GLSL extensions and query for +// shader support on the host. +GLboolean hasShaderSupport(); +void initShaderHandling(); + +///////////////////////////////////////////////////////////////////////////// +// +// class Shader -- GLSL abstract shader object. Can be a vertex shader +// or a fragment shader. +// +///////////////////////////////////////////////////////////////////////////// +class Shader { +protected: + // Constructor + Shader (const string &filename); + +public: + // Destructor + virtual ~Shader (); + +public: + // Accessors + const string &name () const { return _name; } + const string &code () const { return _code; } + GLuint handle () const { return _handle; } + bool fail () const { return (_compiled == GL_FALSE); } + + virtual GLenum shaderType () const = 0; + +public: + // Public interface + void printInfoLog () const; + +protected: + // Internal functions + void compile () + throw (std::runtime_error); + void loadShaderFile (const string &filename) + throw (std::runtime_error); + +protected: + // Member variables + string _name; + string _code; + GLuint _handle; + GLint _compiled; +}; + +///////////////////////////////////////////////////////////////////////////// +// +// class VertexShader -- GLSL vertex shader object. +// +///////////////////////////////////////////////////////////////////////////// +class VertexShader : public Shader { +public: + // Constructor + VertexShader (const string &filename); + +public: + // Return the shader enum type + virtual GLenum shaderType () const { + if (GLEW_VERSION_2_0) + return GL_VERTEX_SHADER; + else + return GL_VERTEX_SHADER_ARB; + } +}; + + +///////////////////////////////////////////////////////////////////////////// +// +// class FragmentShader -- GLSL fragment shader object. +// +///////////////////////////////////////////////////////////////////////////// +class FragmentShader : public Shader { +public: + // Constructor + FragmentShader (const string &filename); + +public: + // Return the shader enum type + virtual GLenum shaderType () const { + if (GLEW_VERSION_2_0) + return GL_FRAGMENT_SHADER; + else + return GL_FRAGMENT_SHADER_ARB; + } +}; + +///////////////////////////////////////////////////////////////////////////// +// +// class ShaderProgram -- GLSL shader program object. +// +///////////////////////////////////////////////////////////////////////////// +class ShaderProgram { +public: + // Constructor/destructor + ShaderProgram (const string &name, + const VertexShader &vertexShader, + const FragmentShader &fragmentShader); + ~ShaderProgram (); + +public: + // Public interface + void use () const; + void unuse () const; + void printInfoLog () const; + + // Accessors + const string &name () const { return _name; } + GLuint handle () const { return _handle; } + bool fail () const { return (_linked == GL_FALSE); } + +private: + // Member variables + string _name; + GLuint _handle; + GLint _linked; +}; + +}}} //end namespace + +#endif // __SHADER_H__ diff --git a/source/shared_lib/include/graphics/md5/Texture.h b/source/shared_lib/include/graphics/md5/Texture.h new file mode 100644 index 00000000..9f80b174 --- /dev/null +++ b/source/shared_lib/include/graphics/md5/Texture.h @@ -0,0 +1,200 @@ +// ============================================================== +// This file is part of MegaGlest (www.glest.org) +// +// Texture.h -- Copyright (c) 2006 David Henry +// changed for use with MegaGlest: Copyright (C) 2011- by Mark Vejvoda +// +// This code is licenced under the MIT license. +// +// This software is provided "as is" without express or implied +// warranties. You may freely copy and compile this source into +// applications you distribute provided that the copyright text +// below is included in the resulting source code. +// +// Definition of an OpenGL texture classes. +// +///////////////////////////////////////////////////////////////////////////// +#ifndef __TEXTURE_H__ +#define __TEXTURE_H__ + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif // _WIN32 + +#include +#include +#include +#include +#include +#include +#include + +#include "Image.h" + +namespace Shared { namespace Graphics { namespace md5 { + +using std::string; +using std::vector; +using std::map; +using std::auto_ptr; + +///////////////////////////////////////////////////////////////////////////// +// Texture class diagram: +// +// +--------- (abs) +// | Texture | +// +-----------+ +// ^ +// | +// +---------------------+----------------------+ +// | | | +// +-----------+ +------------------+ +----------------+ +// | Texture2D | | TextureRectangle | | TextureCubeMap | +// +-----------+ +------------------+ +----------------+ +// +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// +// class Texture -- OpenGL texture base class. This is an abstract +// class for more specialized OpenGL texture classes. +// +///////////////////////////////////////////////////////////////////////////// +class Texture { +public: + // Constructor/destructor + Texture (); + virtual ~Texture (); + +public: + // Constants + enum { + //Default behaviour + kDefault = 0, + + // Use texture compression + kCompress = (1 << 0), + }; + + typedef int TextureFlags; + +public: + // Public interface + void bind () const; + void bind (GLenum texUnit) const; + bool fail () const { return _fail; } + bool stdCoordSystem () const { return _standardCoordSystem; } + + // Accessors + const string &name () const { return _name; } + const GLuint handle () const { return _handle; } + + virtual GLenum target () const = 0; + +private: + // Copy operations are not allowed for textures, because + // when the source texture is destroyed, it releases + // its texture handle and so dest texture's handle is + // not valid anymore. + Texture (const Texture &); + Texture &operator= (const Texture &); + +protected: + // Internal functions + GLubyte *loadImageFile (const string &filename); + GLint getCompressionFormat (GLint internalFormat); + GLint getInternalFormat (GLint components); + +protected: + // Member variables + string _name; + GLuint _handle; + + TextureFlags _flags; + bool _standardCoordSystem; + bool _fail; +}; + + +///////////////////////////////////////////////////////////////////////////// +// +// class Texture2D -- OpenGL texture 2D object. +// +///////////////////////////////////////////////////////////////////////////// +class Texture2D : public Texture { +public: + // Constructors + Texture2D (const string &filename, TextureFlags flags = kDefault); + Texture2D (const Image *img, TextureFlags flags = kDefault); + +protected: + // Default constructor is not public + Texture2D (); + + // Internal functions + virtual void create (const Image *img, TextureFlags flags); + +public: + // Accessors + virtual GLenum target () const { return GL_TEXTURE_2D; } +}; + +///////////////////////////////////////////////////////////////////////////// +// +// class TextureRectangle -- OpenGL texture rectangle object. +// +///////////////////////////////////////////////////////////////////////////// +class TextureRectangle : public Texture { +public: + // Constructors + TextureRectangle (const string &filename, TextureFlags flags = kDefault); + TextureRectangle (const Image *img, TextureFlags flags = kDefault); + +protected: + // Internal functions + virtual void create (const Image *img, TextureFlags flags); + +public: + // Accessors + virtual GLenum target () const { return GL_TEXTURE_RECTANGLE_ARB; } + + GLint width () const { return _width; } + GLint height () const { return _height; } + +protected: + // Member variables + GLint _width; + GLint _height; +}; + +///////////////////////////////////////////////////////////////////////////// +// +// class TextureCubeMap -- OpenGL texture cube map object. +// The order of images to pass to constructors is: +// - positive x, negative x, +// - positive y, negative y, +// - positive z, negative z. +// +///////////////////////////////////////////////////////////////////////////// +class TextureCubeMap : public Texture { +public: + // Constructors + TextureCubeMap (const string &basename, const vector &files, + TextureFlags flags = kDefault); + TextureCubeMap (const string &basename, const vector &faces, + TextureFlags flags = kDefault); + +protected: + // Internal function + virtual void create (const vector &faces, TextureFlags flags); + +public: + // Accessors + virtual GLenum target () const { return GL_TEXTURE_CUBE_MAP_ARB; } +}; + +}}} //end namespace + +#endif // __TEXTURE_H__ diff --git a/source/shared_lib/include/graphics/md5/TextureManager.h b/source/shared_lib/include/graphics/md5/TextureManager.h new file mode 100644 index 00000000..e1bffd05 --- /dev/null +++ b/source/shared_lib/include/graphics/md5/TextureManager.h @@ -0,0 +1,73 @@ +// ============================================================== +// This file is part of MegaGlest (www.glest.org) +// +// TextureManager.h -- Copyright (c) 2006 David Henry +// changed for use with MegaGlest: Copyright (C) 2011- by Mark Vejvoda +// +// This code is licenced under the MIT license. +// +// This software is provided "as is" without express or implied +// warranties. You may freely copy and compile this source into +// applications you distribute provided that the copyright text +// below is included in the resulting source code. +// +// Definitions of a texture manager class. +// +///////////////////////////////////////////////////////////////////////////// +#ifndef __TEXTUREMANAGER_H__ +#define __TEXTUREMANAGER_H__ + +#include "DataManager.h" +#include "Texture.h" + +namespace Shared { namespace Graphics { namespace md5 { + +///////////////////////////////////////////////////////////////////////////// +// +// class Texture2DManager -- a texture manager which can register/unregister +// Texture2D objects. Destroy all registred textures at death. +// +// The texture manager is a singleton. +// +///////////////////////////////////////////////////////////////////////////// +class Texture2DManager : + public DataManager { + friend class DataManager; + +public: + // Public interface + + // Load and register a texture. If the texture has already been + // loaded previously, return it instead of loading it. + Texture2D *load (const string &filename) + { + // Look for the texture in the registry + Texture2D *tex = request (filename); + + // If not found, load the texture + if (tex == NULL) + { + tex = new Texture2D (filename); + + // If the texture creation failed, delete the + // unusable object and return NULL + if (tex->fail ()) + { + delete tex; + tex = NULL; + } + else + { + // The texture has been successfully loaded, + // register it. + registerObject (tex->name (), tex); + } + } + + return tex; + } +}; + +}}} //end namespace + +#endif // __TEXTUREMANAGER_H__ diff --git a/source/shared_lib/include/graphics/md5/md5util.h b/source/shared_lib/include/graphics/md5/md5util.h new file mode 100644 index 00000000..29b7d6e5 --- /dev/null +++ b/source/shared_lib/include/graphics/md5/md5util.h @@ -0,0 +1,34 @@ +// ============================================================== +// This file is part of MegaGlest (www.glest.org) +// +// Copyright (C) 2011- by Mark Vejvoda +// +// You can redistribute this code 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 +// ============================================================== + +#ifndef __MD5Loader_H__ +#define __MD5Loader_H__ + +//#include "Mathlib.h" + +namespace Shared { namespace Graphics { namespace md5 { + +class Md5Object; + +class Real; +template +class Matrix4x4; +typedef Matrix4x4 Matrix4x4f; + +void initMD5OpenGL(string shaderPath); +void cleanupMD5OpenGL(); + +Md5Object * getMD5ObjectFromLoaderScript(const string &filename); +void renderMD5Object(Md5Object *object, double anim, Matrix4x4f *modelViewMatrix=NULL); + +}}} //end namespace + +#endif // __MD5Loader_H__ diff --git a/source/shared_lib/sources/graphics/md5/ArbProgram.cpp b/source/shared_lib/sources/graphics/md5/ArbProgram.cpp new file mode 100644 index 00000000..15bb08c3 --- /dev/null +++ b/source/shared_lib/sources/graphics/md5/ArbProgram.cpp @@ -0,0 +1,262 @@ +// ============================================================== +// This file is part of MegaGlest (www.glest.org) +// +// ArbProgram.cpp -- Copyright (c) 2007 David Henry +// changed for use with MegaGlest: Copyright (C) 2011- by Mark Vejvoda +// +// This code is licenced under the MIT license. +// +// This software is provided "as is" without express or implied +// warranties. You may freely copy and compile this source into +// applications you distribute provided that the copyright text +// below is included in the resulting source code. +// +// Implementation of ARB program related classes. +// +///////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "ArbProgram.h" + +namespace Shared { namespace Graphics { namespace md5 { + +using std::cout; +using std::cerr; +using std::endl; + +///////////////////////////////////////////////////////////////////////////// +// +// Global ARB program related functions. +// +///////////////////////////////////////////////////////////////////////////// + +static GLboolean ArbVpCapable = GL_FALSE; +static GLboolean ArbFpCapable = GL_FALSE; + +// -------------------------------------------------------------------------- +// hasArbVertexProgramSupport +// hasArbFragmentProgramSupport +// +// Return true if the host has ARB program support (vertex or fragment). +// -------------------------------------------------------------------------- + +GLboolean hasArbVertexProgramSupport () { + return ArbVpCapable; +} + +GLboolean hasArbFragmentProgramSupport () { + return ArbFpCapable; +} + +// -------------------------------------------------------------------------- +// initArbProgramHandling +// +// Initialize variables and extensions needed for using ARB Programs. +// This function should be called before any shader usage (at application +// initialization for example). +// -------------------------------------------------------------------------- + +void initArbProgramHandling () { + // Check for extensions needed for ARB program support on host + ArbVpCapable = glewIsSupported ("GL_ARB_vertex_program"); + ArbFpCapable = glewIsSupported ("GL_ARB_fragment_program"); + + if (!hasArbVertexProgramSupport ()) + cerr << "* missing GL_ARB_vertex_program extension" << endl; + + if (!hasArbFragmentProgramSupport ()) + cerr << "* missing GL_ARB_fragment_program extension" << endl; +} + +///////////////////////////////////////////////////////////////////////////// +// +// class ArbProgram implementation. +// +///////////////////////////////////////////////////////////////////////////// + +// -------------------------------------------------------------------------- +// ArbProgram::ArbProgram +// +// Constructor. +// -------------------------------------------------------------------------- + +ArbProgram::ArbProgram (const string &filename) + : _name (filename), _handle (0), _fail (true) { +} + +// -------------------------------------------------------------------------- +// ArbProgram::~ArbProgram +// +// Destructor. Destroy the program handle. +// -------------------------------------------------------------------------- + +ArbProgram::~ArbProgram () { + if (glIsProgramARB (_handle)) + glDeleteProgramsARB (1, &_handle); +} + +// ------------------------------------------------------------------------- +// ArbProgram::use +// ArbProgram::unuse +// +// Bind/unbind the program. +// ------------------------------------------------------------------------- + +void ArbProgram::use () const { + const GLenum target = programType (); + + glEnable (target); + glBindProgramARB (target, _handle); +} + +void ArbProgram::unuse () const { + const GLenum target = programType (); + + glBindProgramARB (target, 0); + glDisable (target); +} + +// ------------------------------------------------------------------------- +// ArbProgram::printProgramString +// +// Print the ARB program string until a given position. This is +// usefull for printing code until error position. +// ------------------------------------------------------------------------- + +void ArbProgram::printProgramString (int errPos) { + int i = 0; + + cerr << endl << " > "; + for (i = 0; i < (errPos + 1) && _code[i]; i++) { + cerr.put (_code[i]); + if (_code[i] == '\n') + cerr << " > "; + } + cerr << " <---" << endl << endl; +} + +// ------------------------------------------------------------------------- +// ArbProgram::load +// +// Create and load the program. +// ------------------------------------------------------------------------- + +void ArbProgram::load () throw (std::runtime_error) { + const GLchar *code = _code.c_str (); + const GLenum target = programType (); + + // Generate a program object handle + glGenProgramsARB (1, &_handle); + + // Make the "current" program object progid + glBindProgramARB (target, _handle); + + // Specify the program for the current object + glProgramStringARB (target, GL_PROGRAM_FORMAT_ASCII_ARB, + _code.size (), code); + + // Check for errors and warnings... + if (GL_INVALID_OPERATION == glGetError ()) { + const GLubyte *errString; + GLint errPos; + + // Find the error position + glGetIntegerv (GL_PROGRAM_ERROR_POSITION_ARB, &errPos); + + // Print implementation-dependent program + // errors and warnings string + errString = glGetString (GL_PROGRAM_ERROR_STRING_ARB); + + cerr << "Error in " << ((GL_VERTEX_PROGRAM_ARB == target) ? + "vertex" : "fragment"); + cerr << " program at position: " << errPos << endl << errString; + + printProgramString (errPos); + + _fail = true; + throw std::runtime_error ("Compilation failed"); + } +} + +// ------------------------------------------------------------------------- +// ArbProgram::loadProgramFile +// +// Load program's code from file. The code is stored into the +// _code string member variable. +// ------------------------------------------------------------------------- + +void ArbProgram::loadProgramFile (const string &filename) throw (std::runtime_error) { + // Open the file + std::ifstream ifs (filename.c_str (), std::ios::in | std::ios::binary); + + if (ifs.fail ()) + throw std::runtime_error ("Couldn't open prog file: " + filename); + + // Read whole file into string + _code.assign (std::istreambuf_iterator(ifs), + std::istreambuf_iterator()); + + // Close file + ifs.close (); +} + + +///////////////////////////////////////////////////////////////////////////// +// +// class ArbVertexProgram implementation. +// +///////////////////////////////////////////////////////////////////////////// + +// -------------------------------------------------------------------------- +// ArbVertexProgram::ArbVertexProgram +// +// Constructor. Read vertex program code from file and load it. +// -------------------------------------------------------------------------- + +ArbVertexProgram::ArbVertexProgram (const string &filename) : ArbProgram (filename) { + try { + // Load program code from file + loadProgramFile (filename); + + // load the program from code buffer + load (); + + cout << "* Vertex program \"" << _name << "\" loaded" << endl; + } + catch (std::runtime_error &err) { + cerr << "Error: Faild to create vertex program from " << _name; + cerr << endl << "Reason: " << err.what () << endl; + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// class ArbFragmentProgram implementation. +// +///////////////////////////////////////////////////////////////////////////// + +// -------------------------------------------------------------------------- +// ArbFragmentProgram::ArbFragmentProgram +// +// Constructor. Read fragment program code from file and load it. +// -------------------------------------------------------------------------- + +ArbFragmentProgram::ArbFragmentProgram (const string &filename) : ArbProgram (filename) { + try { + // Load program code from file + loadProgramFile (filename); + + // load the program from code buffer + load (); + + cout << "* Fragment program \"" << _name << "\" loaded" << endl; + } + catch (std::runtime_error &err) { + cerr << "Error: Faild to create fragment program from " << _name; + cerr << endl << "Reason: " << err.what () << endl; + } +} + +}}} //end namespace diff --git a/source/shared_lib/sources/graphics/md5/GlErrors.cpp b/source/shared_lib/sources/graphics/md5/GlErrors.cpp new file mode 100644 index 00000000..16294221 --- /dev/null +++ b/source/shared_lib/sources/graphics/md5/GlErrors.cpp @@ -0,0 +1,53 @@ +// ============================================================== +// This file is part of MegaGlest (www.glest.org) +// +// GlErrors.cpp -- Copyright (c) 2006-2007 David Henry +// changed for use with MegaGlest: Copyright (C) 2011- by Mark Vejvoda +// +// This code is licenced under the MIT license. +// +// This software is provided "as is" without express or implied +// warranties. You may freely copy and compile this source into +// applications you distribute provided that the copyright text +// below is included in the resulting source code. +// +// OpenGL error management. +// +///////////////////////////////////////////////////////////////////////////// + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif // _WIN32 + +#include +#include +#include + +#include "GlErrors.h" + +namespace Shared { namespace Graphics { namespace md5 { + +using std::cerr; +using std::endl; + +// ------------------------------------------------------------------------- +// checkOpenGLErrors +// +// Print the last OpenGL error code. @file is the filename where the +// function has been called, @line is the line number. You should use +// this function like this: +// checkOpenGLErrors (__FILE__, __LINE__); +// ------------------------------------------------------------------------- + +GLenum checkOpenGLErrors (const char *file, int line) { + GLenum errCode = glGetError (); + + if (errCode != GL_NO_ERROR) + cerr << "(GL) " << file << " (" << line << "): " + << gluErrorString (errCode) << endl; + + return errCode; +} + +}}} //end namespace diff --git a/source/shared_lib/sources/graphics/md5/Image.cpp b/source/shared_lib/sources/graphics/md5/Image.cpp new file mode 100644 index 00000000..ae7b53b5 --- /dev/null +++ b/source/shared_lib/sources/graphics/md5/Image.cpp @@ -0,0 +1,1516 @@ +// ============================================================== +// This file is part of MegaGlest (www.glest.org) +// +// Image.cpp -- Copyright (c) 2006 David Henry +// changed for use with MegaGlest: Copyright (C) 2011- by Mark Vejvoda +// +// This code is licenced under the MIT license. +// +// This software is provided "as is" without express or implied +// warranties. You may freely copy and compile this source into +// applications you distribute provided that the copyright text +// below is included in the resulting source code. +// +// Implementation of image loader classes for DDS, TGA, PCX, JPEG +// and PNG image formats. +// +///////////////////////////////////////////////////////////////////////////// + +#include "Image.h" + +namespace Shared { namespace Graphics { namespace md5 { + +///////////////////////////////////////////////////////////////////////////// +// +// class ImageBuffer implementation. +// +///////////////////////////////////////////////////////////////////////////// + +// -------------------------------------------------------------------------- +// ImageBuffer::ImageBuffer +// +// Constructor. Load a file into memory buffer. +// -------------------------------------------------------------------------- +ImageBuffer::ImageBuffer (const string &filename) + : _filename (filename), _data (NULL), _length (0) { + // Open file + std::ifstream ifs (filename.c_str(), std::ios::in | std::ios::binary); + + if (ifs.fail ()) + throw ImageException ("Couldn't open image file: " + filename, filename); + + // Get file length + ifs.seekg (0, std::ios::end); + _length = ifs.tellg (); + ifs.seekg (0, std::ios::beg); + + try { + // Allocate memory for holding file data + _data = new GLubyte[_length]; + + // Read whole file data + ifs.read (reinterpret_cast (_data), _length); + ifs.close (); + } + catch (...) + { + delete [] _data; + throw; + } +} + +// -------------------------------------------------------------------------- +// ImageBuffer::~ImageBuffer +// +// Destructor. Free memory buffer. +// -------------------------------------------------------------------------- +ImageBuffer::~ImageBuffer () { + delete [] _data; + _data = NULL; +} + +///////////////////////////////////////////////////////////////////////////// +// +// class Image implementation. +// +///////////////////////////////////////////////////////////////////////////// + +// -------------------------------------------------------------------------- +// Image::Image +// +// Constructors. Create an image from a pixel buffer. It allows to +// create an image from an other source of data than an ImageBuffer. +// This is also the copy constructor to use if you want pixel data +// to be copied, since the Image(const Image&) constructor is protected. +// -------------------------------------------------------------------------- + +Image::Image (const string &name, GLsizei w, GLsizei h, GLint numMipMaps, + GLenum format, GLint components, const GLubyte *pixels, + bool stdCoordSystem) + : _width (w), _height (h), _numMipmaps (numMipMaps), + _format (format), _components (components), _name (name), + _standardCoordSystem (stdCoordSystem) { + // NOTE: pixels should be a valid pointer. w, h and components + // have to be non-zero positive values. + + long size = _width * _height * _components; + + if (size <= 0) + throw std::invalid_argument + ("Image::Image: Invalid width, height or component value"); + + if (!pixels) + throw std::invalid_argument + ("Image::Image: Invalid pixel data source"); + + // allocate memory for pixel data + _pixels = new GLubyte[size]; + + // Copy pixel data from buffer + memcpy (_pixels, pixels, size); +} + +// -------------------------------------------------------------------------- +// Image::~Image +// +// Destructor. Delete all memory allocated for this object, i.e. pixel +// data. +// -------------------------------------------------------------------------- +Image::~Image () { + delete [] _pixels; + _pixels = NULL; +} + +// -------------------------------------------------------------------------- +// Image::isCompressed +// +// Check if the image is using S3TC compression (this is the case for +// DDS image only). +// -------------------------------------------------------------------------- +bool Image::isCompressed () const { + switch (_format) + { + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + return true; + + default: + return false; + } +} + +// -------------------------------------------------------------------------- +// Image::isPowerOfTwo +// +// Check if the image dimensions are powers of two. +// -------------------------------------------------------------------------- +bool Image::isPowerOfTwo () const { + GLsizei m; + for (m = 1; m < _width; m *= 2) + ; + + if (m != _width) + return false; + + for (m = 1; m < _height; m *= 2) + ; + + if (m != _height) + return false; + + return true; +} + +///////////////////////////////////////////////////////////////////////////// +// +// class ImageDDS implementation. +// +///////////////////////////////////////////////////////////////////////////// + +// FourCC auto generation with templates +template +struct MakeFourCC { + enum { + Val =(((ch3 << 24) & 0xFF000000) | + ((ch2 << 16) & 0x00FF0000) | + ((ch1 << 8) & 0x0000FF00) | + (ch0 & 0x000000FF)) + }; +}; + +const unsigned int FOURCC_DXT1 = MakeFourCC<'D', 'X', 'T', '1'>::Val; +const unsigned int FOURCC_DXT3 = MakeFourCC<'D', 'X', 'T', '3'>::Val; +const unsigned int FOURCC_DXT5 = MakeFourCC<'D', 'X', 'T', '5'>::Val; + +// -------------------------------------------------------------------------- +// ImageDDS::ImageDDS +// +// Constructor. Read a DDS image from memory. +// -------------------------------------------------------------------------- +ImageDDS::ImageDDS (const ImageBuffer &ibuff) + : _ddsd (NULL) { + _name = ibuff.filename (); + + // DDS images use the GDI+ coordinate system (starts upper-left corner) + _standardCoordSystem = false; + + // There are extensions required for handling compressed DDS + // images as textures + if (!GLEW_ARB_texture_compression) + throw ImageException ("missing GL_ARB_texture_compression" + " extension", _name); + + if (!GLEW_EXT_texture_compression_s3tc) + throw ImageException ("missing GL_EXT_texture_compression_s3tc" + " extension", _name); + + try { + // Get pointer on file data + const GLubyte *data_ptr = ibuff.data (); + int bufferSize = ibuff.length (); + + // Check if is a valid DDS file + string magic; + magic.assign (reinterpret_cast(data_ptr), 4); + if (magic.compare (0, 4, "DDS ") != 0) + throw ImageException ("Not a valid DDS file", _name); + + // Eat 4 bytes magic number + data_ptr += 4; + bufferSize -= 4; + + // Read the surface descriptor and init some member variables + _ddsd = reinterpret_cast(data_ptr); + data_ptr += sizeof (DDSurfaceDesc); + bufferSize -= sizeof (DDSurfaceDesc); + + _width = _ddsd->width; + _height = _ddsd->height; + _numMipmaps = _ddsd->mipMapLevels; + + switch (_ddsd->format.fourCC) + { + case FOURCC_DXT1: + // DXT1's compression ratio is 8:1 + _format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + _components = 3; + break; + + case FOURCC_DXT3: + // DXT3's compression ratio is 4:1 + _format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + _components = 4; + break; + + case FOURCC_DXT5: + // DXT5's compression ratio is 4:1 + _format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + _components = 4; + break; + + default: + // Bad fourCC, unsupported or bad format + throw ImageException ("Unsupported DXT format", _name); + } + + // Read pixel data with mipmaps + _pixels = new GLubyte[bufferSize]; + memcpy (_pixels, data_ptr, bufferSize); + } + catch (...) + { + delete [] _pixels; + throw; + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// class ImageTGA implementation. +// +///////////////////////////////////////////////////////////////////////////// + +// Pixel's component table access +int ImageTGA::rgbaTable[4] = { 2, 1, 0, 3 }; +int ImageTGA::bgraTable[4] = { 0, 1, 2, 3 }; + +// -------------------------------------------------------------------------- +// ImageTGA::ImageTGA +// +// Constructor. Read a TGA image from memory. +// -------------------------------------------------------------------------- +ImageTGA::ImageTGA (const ImageBuffer &ibuff) + : _header (NULL) { + const GLubyte *colormap = NULL; + const GLubyte *data_ptr; + + try + { + _name = ibuff.filename (); + data_ptr = ibuff.data (); + _standardCoordSystem = true; + + // Read TGA header + _header = reinterpret_cast(data_ptr); + data_ptr += sizeof (TGA_Header) + _header->id_lenght; + + // Get image information + getTextureInfo (); + + // Memory allocation for pixel data + _pixels = new GLubyte[_width * _height * _components]; + + // Read color map, if present + if (_header->colormap_type) + { + // NOTE: color map is stored in BGR + colormap = data_ptr; + data_ptr += _header->cm_length * (_header->cm_size >> 3); + } + + // Decode image data + switch (_header->image_type) + { + case 0: + // No data + break; + + case 1: + // Uncompressed 8 bits color index + readTGA8bits (data_ptr, colormap); + break; + + case 2: + // Uncompressed 16-24-32 bits + switch (_header->pixel_depth) + { + case 16: + readTGA16bits (data_ptr); + break; + + case 24: + readTGA24bits (data_ptr); + break; + + case 32: + readTGA32bits (data_ptr); + break; + } + + break; + + case 3: + // Uncompressed 8 or 16 bits grayscale + if (_header->pixel_depth == 8) + readTGAgray8bits (data_ptr); + else // 16 bits + readTGAgray16bits (data_ptr); + + break; + + case 9: + // RLE compressed 8 bits color index + readTGA8bitsRLE (data_ptr, colormap); + break; + + case 10: + // RLE compressed 16-24-32 bits + switch (_header->pixel_depth) + { + case 16: + readTGA16bitsRLE (data_ptr); + break; + + case 24: + readTGA24bitsRLE (data_ptr); + break; + + case 32: + readTGA32bitsRLE (data_ptr); + break; + } + + break; + + case 11: + // RLE compressed 8 or 16 bits grayscale + if (_header->pixel_depth == 8) + readTGAgray8bitsRLE (data_ptr); + else // 16 bits + readTGAgray16bitsRLE (data_ptr); + + break; + + default: + // Image type is not correct, free memory and quit + throw ImageException ("Unknown TGA image type", _name); + } + } + catch (...) + { + delete [] _pixels; + throw; + } +} + +// -------------------------------------------------------------------------- +// ImageTGA::getTextureInfo +// +// Extract OpenGL texture informations from TGA header. +// -------------------------------------------------------------------------- +void ImageTGA::getTextureInfo () { + _width = _header->width; + _height = _header->height; + + switch (_header->image_type) + { + case 3: // grayscale 8 bits + case 11: // grayscale 8 bits (RLE) + { + if (_header->pixel_depth == 8) + { + _format = GL_LUMINANCE; + _components = 1; + } + else // 16 bits + { + _format = GL_LUMINANCE_ALPHA; + _components = 2; + } + + break; + } + + case 1: // 8 bits color index + case 2: // BGR 16-24-32 bits + case 9: // 8 bits color index (RLE) + case 10: // BGR 16-24-32 bits (RLE) + { + // 8 bits and 16 bits images will be converted to 24 bits + if (_header->pixel_depth <= 24) + { + _format = GLEW_EXT_bgra ? GL_BGR : GL_RGB; + _components = 3; + } + else // 32 bits + { + _format = GLEW_EXT_bgra ? GL_BGRA : GL_RGBA; + _components = 4; + } + + break; + } + } +} + +// -------------------------------------------------------------------------- +// ImageTGA::readTGA8bits +// +// Read 8 bits pixel data from TGA file. +// -------------------------------------------------------------------------- +void ImageTGA::readTGA8bits (const GLubyte *data, const GLubyte *colormap) { + const GLubyte *pData = data; + int *compTable = GLEW_EXT_bgra ? bgraTable : rgbaTable; + + for (int i = 0; i < _width * _height; ++i) + { + // Read index color byte + GLubyte color = *(pData++); + + // Convert to BGR/RGB 24 bits + _pixels[(i * 3) + compTable[0]] = colormap[(color * 3) + 0]; + _pixels[(i * 3) + compTable[1]] = colormap[(color * 3) + 1]; + _pixels[(i * 3) + compTable[2]] = colormap[(color * 3) + 2]; + } +} + + +// -------------------------------------------------------------------------- +// ImageTGA::readTGA16bits +// +// Read 16 bits pixel data from TGA file. +// -------------------------------------------------------------------------- +void ImageTGA::readTGA16bits (const GLubyte *data) { + const GLubyte *pData = data; + int *compTable = GLEW_EXT_bgra ? bgraTable : rgbaTable; + + for (int i = 0; i < _width * _height; ++i, pData += 2) { + // Read color word + unsigned short color = pData[0] + (pData[1] << 8); + + // convert to BGR/RGB 24 bits + _pixels[(i * 3) + compTable[2]] = (((color & 0x7C00) >> 10) << 3); + _pixels[(i * 3) + compTable[1]] = (((color & 0x03E0) >> 5) << 3); + _pixels[(i * 3) + compTable[0]] = (((color & 0x001F) >> 0) << 3); + } +} + +// -------------------------------------------------------------------------- +// ImageTGA::readTGA24bits +// +// Read 24 bits pixel data from TGA file. +// -------------------------------------------------------------------------- +void ImageTGA::readTGA24bits (const GLubyte *data) { + if (GLEW_EXT_bgra) + { + memcpy (_pixels, data, _width * _height * 3); + } + else + { + const GLubyte *pData = data; + + for (int i = 0; i < _width * _height; ++i) + { + // Read RGB 24 bits pixel + _pixels[(i * 3) + rgbaTable[0]] = *(pData++); + _pixels[(i * 3) + rgbaTable[1]] = *(pData++); + _pixels[(i * 3) + rgbaTable[2]] = *(pData++); + } + } +} + +// -------------------------------------------------------------------------- +// ImageTGA::readTGA32bits +// +// Read 32 bits pixel data from TGA file. +// -------------------------------------------------------------------------- +void ImageTGA::readTGA32bits (const GLubyte *data) { + if (GLEW_EXT_bgra) + { + memcpy (_pixels, data, _width * _height * 4); + } + else + { + const GLubyte *pData = data; + + for (int i = 0; i < _width * _height; ++i) + { + // Read RGB 32 bits pixel + _pixels[(i * 4) + rgbaTable[0]] = *(pData++); + _pixels[(i * 4) + rgbaTable[1]] = *(pData++); + _pixels[(i * 4) + rgbaTable[2]] = *(pData++); + _pixels[(i * 4) + rgbaTable[3]] = *(pData++); + } + } +} + + +// -------------------------------------------------------------------------- +// ImageTGA::readTGAgray8bits +// +// Read grey 8 bits pixel data from TGA file. +// -------------------------------------------------------------------------- +void ImageTGA::readTGAgray8bits (const GLubyte *data) { + memcpy (_pixels, data, _width * _height); +} + +// -------------------------------------------------------------------------- +// ImageTGA::readTGAgray16bits +// +// Read grey 16 bits pixel data from TGA file. +// -------------------------------------------------------------------------- + +void +ImageTGA::readTGAgray16bits (const GLubyte *data) +{ + memcpy (_pixels, data, _width * _height * 2); +} + + +// -------------------------------------------------------------------------- +// ImageTGA::readTGA8bitsRLE +// +// Read 8 bits pixel data from TGA file with RLE compression. +// -------------------------------------------------------------------------- + +void +ImageTGA::readTGA8bitsRLE (const GLubyte *data, const GLubyte *colormap) +{ + GLubyte *ptr = _pixels; + const GLubyte *pData = data; + int *compTable = GLEW_EXT_bgra ? bgraTable : rgbaTable; + + while (ptr < _pixels + (_width * _height) * 3) + { + // Read first byte + GLubyte packet_header = *(pData++); + int size = 1 + (packet_header & 0x7f); + + if (packet_header & 0x80) + { + // Run-length packet + GLubyte color = *(pData++); + + for (int i = 0; i < size; ++i, ptr += 3) + { + ptr[0] = colormap[(color * 3) + compTable[0]]; + ptr[1] = colormap[(color * 3) + compTable[1]]; + ptr[2] = colormap[(color * 3) + compTable[2]]; + } + } + else + { + // Non run-length packet + for (int i = 0; i < size; ++i, ptr += 3) + { + GLubyte color = *(pData++); + + ptr[0] = colormap[(color * 3) + compTable[0]]; + ptr[1] = colormap[(color * 3) + compTable[1]]; + ptr[2] = colormap[(color * 3) + compTable[2]]; + } + } + } +} + + +// -------------------------------------------------------------------------- +// ImageTGA::readTGA16bitsRLE +// +// Read 16 bits pixel data from TGA file with RLE compression. +// -------------------------------------------------------------------------- + +void +ImageTGA::readTGA16bitsRLE (const GLubyte *data) +{ + const GLubyte *pData = data; + GLubyte *ptr = _pixels; + int *compTable = GLEW_EXT_bgra ? bgraTable : rgbaTable; + + while (ptr < _pixels + (_width * _height) * 3) + { + // Read first byte + GLubyte packet_header = *(pData++); + int size = 1 + (packet_header & 0x7f); + + if (packet_header & 0x80) + { + // Run-length packet + unsigned short color = pData[0] + (pData[1] << 8); + pData += 2; + + for (int i = 0; i < size; ++i, ptr += 3) + { + ptr[compTable[2]] = (((color & 0x7C00) >> 10) << 3); + ptr[compTable[1]] = (((color & 0x03E0) >> 5) << 3); + ptr[compTable[0]] = (((color & 0x001F) >> 0) << 3); + } + } + else + { + // Non run-length packet + for (int i = 0; i < size; ++i, ptr += 3) + { + unsigned short color = pData[0] + (pData[1] << 8); + pData += 2; + + ptr[compTable[2]] = (((color & 0x7C00) >> 10) << 3); + ptr[compTable[1]] = (((color & 0x03E0) >> 5) << 3); + ptr[compTable[0]] = (((color & 0x001F) >> 0) << 3); + } + } + } +} + + +// -------------------------------------------------------------------------- +// ImageTGA::readTGA24bitsRLE +// +// Read 24 bits pixel data from TGA file with RLE compression. +// -------------------------------------------------------------------------- + +void +ImageTGA::readTGA24bitsRLE (const GLubyte *data) +{ + const GLubyte *pData = data; + GLubyte *ptr = _pixels; + int *compTable = GLEW_EXT_bgra ? bgraTable : rgbaTable; + + while (ptr < _pixels + (_width * _height) * 3) + { + // Read first byte + GLubyte packet_header = *(pData++); + int size = 1 + (packet_header & 0x7f); + + if (packet_header & 0x80) + { + // Run-length packet + for (int i = 0; i < size; ++i, ptr += 3) + { + ptr[0] = pData[compTable[0]]; + ptr[1] = pData[compTable[1]]; + ptr[2] = pData[compTable[2]]; + } + + pData += 3; + } + else + { + // Non run-length packet + for (int i = 0; i < size; ++i, ptr += 3) + { + ptr[0] = pData[compTable[0]]; + ptr[1] = pData[compTable[1]]; + ptr[2] = pData[compTable[2]]; + pData += 3; + } + } + } +} + + +// -------------------------------------------------------------------------- +// ImageTGA::readTGA32bitsRLE +// +// Read 32 bits pixel data from TGA file with RLE compression. +// -------------------------------------------------------------------------- + +void +ImageTGA::readTGA32bitsRLE (const GLubyte *data) +{ + const GLubyte *pData = data; + GLubyte *ptr = _pixels; + int *compTable = GLEW_EXT_bgra ? bgraTable : rgbaTable; + + while (ptr < _pixels + (_width * _height) * 4) + { + // Read first byte + GLubyte packet_header = *(pData++); + int size = 1 + (packet_header & 0x7f); + + if (packet_header & 0x80) + { + // Run-length packet */ + for (int i = 0; i < size; ++i, ptr += 4) + { + ptr[0] = pData[compTable[0]]; + ptr[1] = pData[compTable[1]]; + ptr[2] = pData[compTable[2]]; + ptr[3] = pData[compTable[3]]; + } + + pData += 4; + } + else + { + // Non run-length packet + for (int i = 0; i < size; ++i, ptr += 4) + { + ptr[0] = pData[compTable[0]]; + ptr[1] = pData[compTable[1]]; + ptr[2] = pData[compTable[2]]; + ptr[3] = pData[compTable[3]]; + pData += 4; + } + } + } +} + + +// -------------------------------------------------------------------------- +// ImageTGA::readTGAgray8bitsRLE +// +// Read grey 8 bits pixel data from TGA file with RLE compression. +// -------------------------------------------------------------------------- + +void +ImageTGA::readTGAgray8bitsRLE (const GLubyte *data) +{ + const GLubyte *pData = data; + GLubyte *ptr = _pixels; + + while (ptr < _pixels + (_width * _height)) + { + // Read first byte + GLubyte packet_header = *(pData++); + int size = 1 + (packet_header & 0x7f); + + if (packet_header & 0x80) + { + // Run-length packet + GLubyte color = *(pData++); + + memset (ptr, color, size); + ptr += size; + } + else + { + // Non run-length packet + memcpy (ptr, pData, size); + ptr += size; + pData += size; + } + } +} + + +// -------------------------------------------------------------------------- +// ImageTGA::readTGAgray16bitsRLE +// +// Read grey 16 bits pixel data from TGA file with RLE compression. +// -------------------------------------------------------------------------- + +void +ImageTGA::readTGAgray16bitsRLE (const GLubyte *data) +{ + const GLubyte *pData = data; + GLubyte *ptr = _pixels; + + while (ptr < _pixels + (_width * _height) * 2) + { + // Read first byte + GLubyte packet_header = *(pData++); + int size = 1 + (packet_header & 0x7f); + + if (packet_header & 0x80) + { + // Run-length packet + GLubyte color = *(pData++); + GLubyte alpha = *(pData++); + + for (int i = 0; i < size; ++i, ptr += 2) + { + ptr[0] = color; + ptr[1] = alpha; + } + } + else + { + // Non run-length packet + memcpy (ptr, pData, size * 2); + ptr += size * 2; + pData += size * 2; + } + } +} + + +///////////////////////////////////////////////////////////////////////////// +// +// class ImagePCX implementation. +// +///////////////////////////////////////////////////////////////////////////// + +// Pixel's component table access +int ImagePCX::rgbTable[3] = { 0, 1, 2 }; +int ImagePCX::bgrTable[3] = { 2, 1, 0 }; + + +// -------------------------------------------------------------------------- +// ImagePCX::ImagePCX +// +// Constructor. Read a PCX image from memory. +// -------------------------------------------------------------------------- + +ImagePCX::ImagePCX (const ImageBuffer &ibuff) + : _header (NULL) +{ + const GLubyte *data_ptr; + + try + { + _name = ibuff.filename (); + _standardCoordSystem = true; + + // Get pointer on file data + data_ptr = ibuff.data (); + + // Read PCX header + _header = reinterpret_cast(data_ptr); + data_ptr += sizeof (PCX_Header); + + // Check if is valid PCX file + if (_header->manufacturer != 0x0a) + throw ImageException ("Bad version number", _name); + + // Initialize image variables + _width = _header->xmax - _header->xmin + 1; + _height = _header->ymax - _header->ymin + 1; + _format = GLEW_EXT_bgra ? GL_BGR : GL_RGB; + _components = 3; + _pixels = new GLubyte[_width * _height * _components]; + + int bitcount = _header->bitsPerPixel * _header->numColorPlanes; + int palette_pos = ibuff.length () - 768; + + // Read image data + switch (bitcount) + { + case 1: + // 1 bit color index + readPCX1bit (data_ptr); + break; + + case 4: + // 4 bits color index + readPCX4bits (data_ptr); + break; + + case 8: + // 8 bits color index + readPCX8bits (data_ptr, ibuff.data () + palette_pos); + break; + + case 24: + // 24 bits + readPCX24bits (data_ptr); + break; + + default: + // Unsupported + throw ImageException ("Unhandled PCX format (bad bitcount)", _name); + } + } + catch (...) + { + delete [] _pixels; + throw; + } +} + + +// -------------------------------------------------------------------------- +// ImagePCX::readPCX1bit +// +// Read 1 bit PCX image. +// -------------------------------------------------------------------------- + +void +ImagePCX::readPCX1bit (const GLubyte *data) +{ + int rle_count = 0, rle_value = 0; + const GLubyte *pData = data; + GLubyte *ptr = _pixels; + int *compTable = GLEW_EXT_bgra ? bgrTable : rgbTable; + + for (int y = 0; y < _height; ++y) + { + ptr = &_pixels[(_height - (y + 1)) * _width * 3]; + int bytes = _header->bytesPerScanLine; + + // Decode line number y + while (bytes--) + { + if (rle_count == 0) + { + if ( (rle_value = *(pData++)) < 0xc0) + { + rle_count = 1; + } + else + { + rle_count = rle_value - 0xc0; + rle_value = *(pData++); + } + } + + rle_count--; + + // Fill height pixels chunk + for (int i = 7; i >= 0; --i, ptr += 3) + { + int colorIndex = ((rle_value & (1 << i)) > 0); + + ptr[0] = _header->palette[colorIndex * 3 + compTable[0]]; + ptr[1] = _header->palette[colorIndex * 3 + compTable[1]]; + ptr[2] = _header->palette[colorIndex * 3 + compTable[2]]; + } + } + } +} + + +// -------------------------------------------------------------------------- +// ImagePCX::readPCX4bits +// +// Read 4 bits PCX image. +// -------------------------------------------------------------------------- + +void +ImagePCX::readPCX4bits (const GLubyte *data) +{ + const GLubyte *pData = data; + GLubyte *colorIndex = NULL; + GLubyte *line = NULL; + GLubyte *ptr; + int rle_count = 0, rle_value = 0; + int *compTable = GLEW_EXT_bgra ? bgrTable : rgbTable; + + try + { + // Memory allocation for temporary buffers + colorIndex = new GLubyte[_width]; + line = new GLubyte[_header->bytesPerScanLine]; + } + catch (std::bad_alloc &err) + { + delete [] colorIndex; + delete [] line; + throw; + } + + for (int y = 0; y < _height; ++y) + { + ptr = &_pixels[(_height - (y + 1)) * _width * 3]; + memset (colorIndex, 0, _width); + + for (int c = 0; c < 4; ++c) + { + GLubyte *pLine = line; + int bytes = _header->bytesPerScanLine; + + // Decode line number y + while (bytes--) + { + if (rle_count == 0) + { + if ( (rle_value = *(pData++)) < 0xc0) + { + rle_count = 1; + } + else + { + rle_count = rle_value - 0xc0; + rle_value = *(pData++); + } + } + + rle_count--; + *(pLine++) = rle_value; + } + + // Compute line's color indexes + for (int x = 0; x < _width; ++x) + { + if (line[x / 8] & (128 >> (x % 8))) + colorIndex[x] += (1 << c); + } + } + + // Decode scanline. color index => rgb + for (int x = 0; x < _width; ++x, ptr += 3) + { + ptr[0] = _header->palette[colorIndex[x] * 3 + compTable[0]]; + ptr[1] = _header->palette[colorIndex[x] * 3 + compTable[1]]; + ptr[2] = _header->palette[colorIndex[x] * 3 + compTable[2]]; + } + } + + // Release memory + delete [] colorIndex; + delete [] line; +} + + +// -------------------------------------------------------------------------- +// ImagePCX::readPCX8bits +// +// Read 8 bits PCX image. +// -------------------------------------------------------------------------- + +void +ImagePCX::readPCX8bits (const GLubyte *data, const GLubyte *palette) +{ + const GLubyte *pData = data; + int rle_count = 0, rle_value = 0; + GLubyte *ptr; + int *compTable = GLEW_EXT_bgra ? bgrTable : rgbTable; + + // Palette should be preceded by a value of 0x0c (12)... + GLubyte magic = palette[-1]; + if (magic != 0x0c) + { + // ... but sometimes it is not + std::cerr << "Warning: PCX palette should start with " + << "a value of 0x0c (12)!" << std::endl; + } + + // Read pixel data + for (int y = 0; y < _height; ++y) + { + ptr = &_pixels[(_height - (y + 1)) * _width * 3]; + int bytes = _header->bytesPerScanLine; + + // Decode line number y + while (bytes--) + { + if (rle_count == 0) + { + if( (rle_value = *(pData++)) < 0xc0) + { + rle_count = 1; + } + else + { + rle_count = rle_value - 0xc0; + rle_value = *(pData++); + } + } + + rle_count--; + + ptr[0] = palette[rle_value * 3 + compTable[0]]; + ptr[1] = palette[rle_value * 3 + compTable[1]]; + ptr[2] = palette[rle_value * 3 + compTable[2]]; + ptr += 3; + } + } +} + + +// -------------------------------------------------------------------------- +// ImagePCX::readPCX24bits +// +// Read 24 bits PCX image. +// -------------------------------------------------------------------------- + +void +ImagePCX::readPCX24bits (const GLubyte *data) +{ + const GLubyte *pData = data; + GLubyte *ptr; + int rle_count = 0, rle_value = 0; + int *compTable = GLEW_EXT_bgra ? bgrTable : rgbTable; + + for (int y = 0; y < _height; ++y) + { + // For each color plane + for (int c = 0; c < 3; ++c) + { + ptr = &_pixels[(_height - (y + 1)) * _width * 3]; + int bytes = _header->bytesPerScanLine; + + // Decode line number y + while (bytes--) + { + if (rle_count == 0) + { + if( (rle_value = *(pData++)) < 0xc0) + { + rle_count = 1; + } + else + { + rle_count = rle_value - 0xc0; + rle_value = *(pData++); + } + } + + rle_count--; + ptr[compTable[c]] = static_cast(rle_value); + ptr += 3; + } + } + } +} + + +///////////////////////////////////////////////////////////////////////////// +// +// class ImageJPEG implementation. +// +///////////////////////////////////////////////////////////////////////////// + +// -------------------------------------------------------------------------- +// ImageJPEG::ImageJPEG +// +// Constructor. Read a JPEG image from memory, using libjpeg. +// -------------------------------------------------------------------------- + +ImageJPEG::ImageJPEG (const ImageBuffer &ibuff) +{ + jpeg_decompress_struct cinfo; + my_error_mgr jerr; + jpeg_source_mgr jsrc; + JSAMPROW j; + + try + { + _name = ibuff.filename (); + _standardCoordSystem = true; + + // Create and configure decompress object + jpeg_create_decompress (&cinfo); + cinfo.err = jpeg_std_error (&jerr.pub); + cinfo.src = &jsrc; + + // Configure error manager + jerr.pub.error_exit = errorExit_callback; + jerr.pub.output_message = outputMessage_callback; + + if (setjmp (jerr.setjmp_buffer)) + throw ImageException (jerr.errorMsg, _name); + + // Configure source manager + jsrc.next_input_byte = ibuff.data (); + jsrc.bytes_in_buffer = ibuff.length (); + + jsrc.init_source = initSource_callback; + jsrc.fill_input_buffer = fillInputBuffer_callback; + jsrc.skip_input_data = skipInputData_callback; + jsrc.resync_to_restart = jpeg_resync_to_restart; + jsrc.term_source = termSource_callback; + + // Read file's header and prepare for decompression + jpeg_read_header (&cinfo, TRUE); + jpeg_start_decompress (&cinfo); + + // Initialize image's member variables + _width = cinfo.image_width; + _height = cinfo.image_height; + _components = cinfo.num_components; + _format = (cinfo.num_components == 1) ? GL_LUMINANCE : GL_RGB; + _pixels = new GLubyte[_width * _height * _components]; + + // Read scanlines + for (int i = 0; i < _height; ++i) + { + //j = &_pixels[_width * i * _components]; + j = (_pixels + ((_height - (i + 1)) * _width * _components)); + jpeg_read_scanlines (&cinfo, &j, 1); + } + + // Finish decompression and release memory + jpeg_finish_decompress (&cinfo); + jpeg_destroy_decompress (&cinfo); + } + catch (...) + { + delete [] _pixels; + jpeg_destroy_decompress (&cinfo); + + throw; + } +} + + +// -------------------------------------------------------------------------- +// ImageJPEG::initSource_callback +// ImageJPEG::fillInputBuffer_callback +// ImageJPEG::skipInputData_callback +// ImageJPEG::termSource_callback +// +// Callback functions used by libjpeg for reading data from memory. +// -------------------------------------------------------------------------- + +void +ImageJPEG::initSource_callback (j_decompress_ptr cinfo) +{ + // Nothing to do here +} + + +boolean +ImageJPEG::fillInputBuffer_callback (j_decompress_ptr cinfo) +{ + JOCTET eoi_buffer[2] = { 0xFF, JPEG_EOI }; + struct jpeg_source_mgr *jsrc = cinfo->src; + + // Create a fake EOI marker + jsrc->next_input_byte = eoi_buffer; + jsrc->bytes_in_buffer = 2; + + return TRUE; +} + + +void +ImageJPEG::skipInputData_callback (j_decompress_ptr cinfo, long num_bytes) +{ + struct jpeg_source_mgr *jsrc = cinfo->src; + + if (num_bytes > 0) + { + while (num_bytes > static_cast(jsrc->bytes_in_buffer)) + { + num_bytes -= static_cast(jsrc->bytes_in_buffer); + fillInputBuffer_callback (cinfo); + } + + jsrc->next_input_byte += num_bytes; + jsrc->bytes_in_buffer -= num_bytes; + } +} + + +void +ImageJPEG::termSource_callback (j_decompress_ptr cinfo) +{ + // Nothing to do here +} + + +// -------------------------------------------------------------------------- +// ImageJPEG::errorExit +// ImageJPEG::outputMessage +// +// Callback functions used by libjpeg for error handling. +// -------------------------------------------------------------------------- + +void +ImageJPEG::errorExit_callback (j_common_ptr cinfo) +{ + my_error_ptr jerr = reinterpret_cast(cinfo->err); + + // Create the error message + char message[JMSG_LENGTH_MAX]; + (*cinfo->err->format_message) (cinfo, message); + jerr->errorMsg.assign (message); + + // Return control to the setjmp point + longjmp (jerr->setjmp_buffer, 1); +} + + +void +ImageJPEG::outputMessage_callback (j_common_ptr cinfo) +{ + my_error_ptr jerr = reinterpret_cast(cinfo->err); + + // Create the error message + char message[JMSG_LENGTH_MAX]; + (*cinfo->err->format_message) (cinfo, message); + jerr->errorMsg.assign (message); + + // Send it to stderr, adding a newline + std::cerr << "libjpeg: " << jerr->errorMsg << std::endl; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// class ImagePNG implementation. +// +///////////////////////////////////////////////////////////////////////////// + +// -------------------------------------------------------------------------- +// ImagePNG::ImagePNG +// +// Constructor. Read a PNG image from memory, using libpng. +// -------------------------------------------------------------------------- + +ImagePNG::ImagePNG (const ImageBuffer &ibuff) +{ + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + png_bytep *row_pointers = NULL; + int bit_depth, color_type; + my_source_mgr src_mgr (ibuff); + + try + { + _name = ibuff.filename (); + _standardCoordSystem = true; + + png_byte sig[8]; + memcpy (sig, reinterpret_cast(ibuff.data ()), 8); + + // Check for valid magic number + if (!png_check_sig (sig, 8)) + throw ImageException ("Not a valid PNG file", _name); + + // Create PNG read struct + png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, &errorMsg, + error_callback, warning_callback); + if (!png_ptr) + throw ImageException ("Failed to create png read struct", _name); + + if (setjmp (png_jmpbuf (png_ptr))) + throw ImageException (errorMsg, _name); + + // Create PNG info struct + info_ptr = png_create_info_struct (png_ptr); + if (!info_ptr) + throw ImageException ("Failed to create png info struct", _name); + + // Set "read" callback function and give source of data + png_set_read_fn (png_ptr, &src_mgr, read_callback); + + // Read png info + png_read_info (png_ptr, info_ptr); + + // Get some usefull information from header + bit_depth = png_get_bit_depth (png_ptr, info_ptr); + color_type = png_get_color_type (png_ptr, info_ptr); + + // Convert index color images to RGB images + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb (png_ptr); + + // Convert 1-2-4 bits grayscale images to 8 bits + // grayscale. + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + png_set_gray_1_2_4_to_8 (png_ptr); + + if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha (png_ptr); + + if (bit_depth == 16) + png_set_strip_16 (png_ptr); + else if (bit_depth < 8) + png_set_packing (png_ptr); + + // Update info structure to apply transformations + png_read_update_info (png_ptr, info_ptr); + + // Get updated information + png_get_IHDR (png_ptr, info_ptr, + reinterpret_cast(&_width), + reinterpret_cast(&_height), + &bit_depth, &color_type, + NULL, NULL, NULL); + + // Get image format and components per pixel + getTextureInfo (color_type); + + // Memory allocation for storing pixel data + _pixels = new GLubyte[_width * _height * _components]; + + // Pointer array. Each one points at the begening of a row. + row_pointers = new png_bytep[_height]; + + for (int i = 0; i < _height; ++i) + { + row_pointers[i] = (png_bytep)(_pixels + + ((_height - (i + 1)) * _width * _components)); + } + + // Read pixel data using row pointers + png_read_image (png_ptr, row_pointers); + + // Finish decompression and release memory + png_read_end (png_ptr, NULL); + png_destroy_read_struct (&png_ptr, &info_ptr, NULL); + + delete [] row_pointers; + } + catch (...) + { + delete [] _pixels; + delete [] row_pointers; + + if (png_ptr) + png_destroy_read_struct (&png_ptr, &info_ptr, NULL); + + throw; + } +} + + +// -------------------------------------------------------------------------- +// ImagePNG::getTextureInfo +// +// Extract OpenGL texture informations from PNG info. +// -------------------------------------------------------------------------- + +void +ImagePNG::getTextureInfo (int color_type) +{ + switch (color_type) + { + case PNG_COLOR_TYPE_GRAY: + _format = GL_LUMINANCE; + _components = 1; + break; + + case PNG_COLOR_TYPE_GRAY_ALPHA: + _format = GL_LUMINANCE_ALPHA; + _components = 2; + break; + + case PNG_COLOR_TYPE_RGB: + _format = GL_RGB; + _components = 3; + break; + + case PNG_COLOR_TYPE_RGB_ALPHA: + _format = GL_RGBA; + _components = 4; + break; + + default: + // Badness + throw ImageException ("Bad PNG color type", _name); + } +} + + +// -------------------------------------------------------------------------- +// ImagePNG::read_callback +// ImagePNG::error_callback +// ImagePNG::warning_callback +// +// Callback functions used by libpng for reading data from memory +// and error handling. +// -------------------------------------------------------------------------- + +void +ImagePNG::read_callback (png_structp png_ptr, png_bytep data, png_size_t length) +{ + my_source_ptr src = static_cast(png_ptr->io_ptr); + + // Copy data from image buffer + memcpy (data, src->pibuff->data () + src->offset, length); + + // Advance in the file + src->offset += length; +} + + +void +ImagePNG::error_callback (png_structp png_ptr, png_const_charp error_msg) +{ + static_cast(png_ptr->error_ptr)->assign (error_msg); + + longjmp (png_jmpbuf (png_ptr), 1); +} + + +void +ImagePNG::warning_callback (png_structp png_ptr, png_const_charp warning_msg) +{ + std::cerr << "libpng: " << warning_msg << std::endl; +} + +}}} //end namespace diff --git a/source/shared_lib/sources/graphics/md5/Md5Model.cpp b/source/shared_lib/sources/graphics/md5/Md5Model.cpp new file mode 100644 index 00000000..d5614289 --- /dev/null +++ b/source/shared_lib/sources/graphics/md5/Md5Model.cpp @@ -0,0 +1,1935 @@ +// ============================================================== +// This file is part of MegaGlest (www.glest.org) +// +// Md5Model.h -- Copyright (c) 2006-2007 David Henry +// changed for use with MegaGlest: Copyright (C) 2011- by Mark Vejvoda +// +// This code is licenced under the MIT license. +// +// This software is provided "as is" without express or implied +// warranties. You may freely copy and compile this source into +// applications you distribute provided that the copyright text +// below is included in the resulting source code. +// +// Implementation of MD5 Model classes. +// +///////////////////////////////////////////////////////////////////////////// + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif // _WIN32 + +#include +#include +#include +#include +#include + +#include "Texture.h" +#include "ArbProgram.h" +#include "Shader.h" +#include "Md5Model.h" + +namespace Shared { namespace Graphics { namespace md5 { + + +using std::cout; +using std::cerr; +using std::endl; + +///////////////////////////////////////////////////////////////////////////// +// +// global stuff +// +///////////////////////////////////////////////////////////////////////////// + +render_path_e Md5Model::renderPath; +ShaderProgram *Md5Model::shader=NULL; +ArbVertexProgram *Md5Model::vp=NULL; +ArbFragmentProgram *Md5Model::fp=NULL; +// Tangent uniform's location +GLint Md5Model::tangentLoc = -1; +bool Md5Model::bDrawNormals = false; + +const int kMd5Version = 10; + +// Sort functor for joints +struct SortByDepth : + public std::binary_function +{ + bool operator() (const Md5Joint_t &j1, const Md5Joint_t &j2) const { + return (j1.pos._z < j2.pos._z); + } +}; + +///////////////////////////////////////////////////////////////////////////// +// +// class Md5Skeleton implementation. +// +///////////////////////////////////////////////////////////////////////////// + +// -------------------------------------------------------------------------- +// Md5Skeleton::Md5Skeleton +// +// Constructor. Load skeleton data from a <.md5mesh> file. +// -------------------------------------------------------------------------- +Md5Skeleton::Md5Skeleton (std::ifstream &ifs, int numJoints) + throw (Md5Exception) { + string token, buffer; + + if (!ifs.is_open()) + throw Md5Exception ("Input stream not opened!", "skeleton"); + + // Read all joints + for (int i = 0; i < numJoints; ++i) + { + // NOTE: hope there isn't any comment between + // two lines of joints data... + Md5JointPtr j (new Md5Joint_t); + + ifs >> j->name; + ifs >> j->parent; + ifs >> token; // "(" + ifs >> j->pos._x; + ifs >> j->pos._y; + ifs >> j->pos._z; + ifs >> token; // ")" + ifs >> token; // "(" + ifs >> j->orient._x; + ifs >> j->orient._y; + ifs >> j->orient._z; + ifs >> token; // ")" + + // Eat up rest of the line + std::getline (ifs, buffer); + + // Compute orient quaternion's w value + j->orient.computeW (); + + // Add joint to joints vector + _joints.push_back (j); + } +} + +// -------------------------------------------------------------------------- +// Md5Skeleton::~Md5Skeleton +// +// Destructor. Free all data allocated for skeleton's joints. +// -------------------------------------------------------------------------- +Md5Skeleton::~Md5Skeleton () { +} + +// -------------------------------------------------------------------------- +// Md5Skeleton::draw +// +// Draw skeleton's bones and joints. +// -------------------------------------------------------------------------- +void Md5Skeleton::draw (const Matrix4x4f &modelView, bool labelJoints) { + // Draw each joint + glPointSize (5.0f); + glColor3f (1.0f, 0.0f, 0.0f); + glBegin (GL_POINTS); + for (unsigned int i = 0; i < _joints.size (); ++i) + glVertex3fv (_joints[i]->pos); + glEnd (); + glPointSize (1.0f); + + // Draw each bone + glColor3f (0.0f, 1.0f, 0.0f); + glBegin (GL_LINES); + for (unsigned int i = 0; i < _joints.size (); ++i) + { + if (_joints[i]->parent != -1) + { + glVertex3fv (_joints[ _joints[i]->parent ]->pos); + glVertex3fv (_joints[i]->pos); + } + } + glEnd (); + + // Label each joint + /* + if (labelJoints && font) + { + vector jointlist (_joints.size ()); + + // Copy joint's position and name + for (unsigned int i = 0; i < _joints.size (); ++i) + jointlist.push_back (*_joints[i]); + + // Sort joints about depth because of alpha blending + std::sort (jointlist.begin (), jointlist.end (), SortByDepth ()); + + glActiveTexture (GL_TEXTURE0); + glMatrixMode (GL_TEXTURE); + glLoadIdentity (); + + GLfloat mat[16]; + glMatrixMode (GL_MODELVIEW); + glGetFloatv (GL_MODELVIEW_MATRIX, mat); + + glPushMatrix (); + // Setup billboard matrix + mat[0] = 1.0f; mat[1] = 0.0f; mat[2] = 0.0f; + mat[4] = 0.0f; mat[5] = 1.0f; mat[6] = 0.0f; + mat[8] = 0.0f; mat[9] = 0.0f; mat[10]= 1.0f; + + glLoadMatrixf (mat); + + glPushAttrib (GL_POLYGON_BIT); + glFrontFace (GL_CCW); + glPolygonMode (GL_FRONT, GL_FILL); + glColor3f (1.0f, 1.0f, 1.0f); + + glLoadIdentity (); + glScalef (0.1f, 0.1f, 0.1f); + + for (unsigned int i = 0; i < _joints.size (); ++i) + { + glPushMatrix (); + // Move to joint's position + glTranslatef (jointlist[i].pos._x * 10.0f, + jointlist[i].pos._y * 10.0f, + jointlist[i].pos._z * 10.0f); + + font->printText (jointlist[i].name.c_str ()); + glPopMatrix(); + } + + // GL_POLYGON_BIT + glPopAttrib (); + glPopMatrix (); + } + */ +} + +// -------------------------------------------------------------------------- +// Md5Skeleton::setNumJoints +// +// Reserve memory to hold numJoints joints. +// -------------------------------------------------------------------------- +void Md5Skeleton::setNumJoints (int numJoints) { + _joints.reserve (numJoints); +} + +// -------------------------------------------------------------------------- +// Md5Skeleton::addJoint +// +// Add a joint to the skeleton. +// -------------------------------------------------------------------------- +void Md5Skeleton::addJoint (Md5Joint_t *thisJoint) { + _joints.push_back (Md5JointPtr (thisJoint)); +} + +// -------------------------------------------------------------------------- +// Md5Skeleton::clone +// +// Dupplicate the skeleton. +// -------------------------------------------------------------------------- +Md5Skeleton* Md5Skeleton::clone () const { + Md5Skeleton *cpy = new Md5Skeleton; + cpy->setNumJoints (_joints.size ()); + + for (size_t i = 0; i < _joints.size (); ++i) + cpy->addJoint (new Md5Joint_t (*_joints[i].get ())); + + return cpy; +} + +///////////////////////////////////////////////////////////////////////////// +// +// class Md5Mesh implementation. +// +///////////////////////////////////////////////////////////////////////////// + +// -------------------------------------------------------------------------- +// Md5Mesh::Md5Mesh +// +// Constructor. Load mesh data from a <.md5mesh> file. +// -------------------------------------------------------------------------- +Md5Mesh::Md5Mesh (std::ifstream &ifs) + throw (Md5Exception) + : _renderState (kShow), _numVerts (0), _numTris (0), _numWeights (0), + _decal (NULL), _specMap (NULL), _normalMap (NULL), _heightMap (NULL) { + string token, buffer; + + if (!ifs.is_open()) + throw Md5Exception ("Input stream not opened!", "mesh"); + + do + { + // Read first token line from file + ifs >> token; + + if (token == "shader") + { + ifs >> _shader; + + // Remove quote marks from the shader string + _shader = _shader.substr (_shader.find_first_of ('\"') + 1, + _shader.find_last_of ('\"') - 1); + + // Get mesh name from shader string + _name = _shader.c_str() + _shader.find_last_of ('/') + 1; + } + else if (token == "numverts") + { + ifs >> _numVerts; + _verts.reserve (_numVerts); + } + else if (token == "numtris") + { + ifs >> _numTris; + _tris.reserve (_numTris); + } + else if (token == "numweights") + { + ifs >> _numWeights; + _weights.reserve (_numWeights); + } + else if (token == "vert") + { + Md5VertexPtr vert (new Md5Vertex_t); + + // Read vertex data + ifs >> token; // index + ifs >> token; // "(" + ifs >> vert->st[0]; + ifs >> vert->st[1]; + ifs >> token; // ")" + ifs >> vert->startWeight; + ifs >> vert->countWeight; + + _verts.push_back (vert); + } + else if (token == "tri") + { + Md5TrianglePtr tri (new Md5Triangle_t); + + // Read triangle data + ifs >> token; // index + ifs >> tri->index[0]; + ifs >> tri->index[1]; + ifs >> tri->index[2]; + + _tris.push_back (tri); + } + else if (token == "weight") + { + Md5WeightPtr weight (new Md5Weight_t); + + // Read weight data + ifs >> token; // index + ifs >> weight->joint; + ifs >> weight->bias; + ifs >> token; // "(" + ifs >> weight->pos._x; + ifs >> weight->pos._y; + ifs >> weight->pos._z; + ifs >> token; // ")" + + _weights.push_back (weight); + } + + // Eat up rest of the line + std::getline (ifs, buffer); + + } + while ((token != "}") && !ifs.eof ()); + + // Memory allocation for vertex arrays + allocVertexArrays (); + + // Setup texture coordinates array + setupTexCoordArray (); +} + +// -------------------------------------------------------------------------- +// Md5Mesh::~Md5Mesh +// +// Destructor. Free all data allocated for the mesh, i.e. vertices, +// triangles, weights and vertex arrays. +// -------------------------------------------------------------------------- +Md5Mesh::~Md5Mesh () { +} + +// -------------------------------------------------------------------------- +// Md5Mesh::setupVertexArray +// +// Compute vertices' position, normal and tangent. +// -------------------------------------------------------------------------- +void Md5Mesh::setupVertexArrays (Md5Skeleton *skel) { + for (int i = 0; i < _numVerts; ++i) + { + Vector3f finalVertex = kZeroVectorf; + Vector3f finalNormal = kZeroVectorf; + Vector3f finalTangent = kZeroVectorf; + + // Calculate final vertex to draw with weights + for (int j = 0; j < _verts[i]->countWeight; ++j) + { + const Md5Weight_t *pWeight + = _weights[_verts[i]->startWeight + j].get (); + const Md5Joint_t *pJoint + = skel->joint (pWeight->joint); + + // Calculate transformed vertex for this weight + Vector3f wv = pWeight->pos; + pJoint->orient.rotate (wv); + + // The sum of all pWeight->bias should be 1.0 + finalVertex += (pJoint->pos + wv) * pWeight->bias; + + // Calculate transformed normal for this weight + Vector3f wn = pWeight->norm; + pJoint->orient.rotate (wn); + + finalNormal += wn * pWeight->bias; + + // Calculate transformed tangent for this weight + Vector3f wt = pWeight->tan; + pJoint->orient.rotate (wt); + + finalTangent += wt * pWeight->bias; + } + + // We can omit to normalize normal and tangent, + // because they should have been already normalized + // when they were computed. We can gain some time + // avoiding some heavy calculus. + + //finalNormal.normalize (); + //finalTangent.normalize (); + + { + // Fill in the vertex arrays with the freshly vertex, normal + // and tangent computed. + + GLfloat *vertexPointer = &_vertexArray[i * 3]; + GLfloat *normalPointer = &_normalArray[i * 3]; + GLfloat *tangentPointer = &_tangentArray[i * 3]; + + vertexPointer[0] = finalVertex._x; + vertexPointer[1] = finalVertex._y; + vertexPointer[2] = finalVertex._z; + + normalPointer[0] = finalNormal._x; + normalPointer[1] = finalNormal._y; + normalPointer[2] = finalNormal._z; + + tangentPointer[0] = finalTangent._x; + tangentPointer[1] = finalTangent._y; + tangentPointer[2] = finalTangent._z; + } + } +} + + +// -------------------------------------------------------------------------- +// Md5Mesh::computeWeightNormals +// +// der_ton said: +// +// * First you have to get the bind-pose model-space normals by calculating +// them from the model geometry in bind-pose. +// +// * Then you calculate the weight's normal (which is in bone-space) by +// invert-transforming the normal by the bone-space matrix. +// +// * So afterwards when animating, you'll transform the weight normal with +// the animated bone-space matrix and add them all up and you'll get +// back your animated vertex normal. +// -------------------------------------------------------------------------- + +void +Md5Mesh::computeWeightNormals (Md5Skeleton *skel) +{ + vector bindposeVerts (_numVerts); + vector bindposeNorms (_numVerts); + + for (int i = 0; i < _numVerts; ++i) + { + // Zero out final vertex position and final vertex normal + bindposeVerts[i] = kZeroVectorf; + bindposeNorms[i] = kZeroVectorf; + + for (int j = 0; j < _verts[i]->countWeight; ++j) + { + const Md5Weight_t *pWeight + = _weights[_verts[i]->startWeight + j].get (); + const Md5Joint_t *pJoint + = skel->joint (pWeight->joint); + + // Calculate transformed vertex for this weight + Vector3f wv = pWeight->pos; + pJoint->orient.rotate (wv); + + bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias; + } + } + + // Compute triangle normals + for (int i = 0; i < _numTris; ++i) + { + const Md5Triangle_t *pTri = _tris[i].get (); + + Vector3f triNorm (-ComputeNormal (bindposeVerts[pTri->index[0]], + bindposeVerts[pTri->index[1]], bindposeVerts[pTri->index[2]])); + + for (int j = 0; j < 3; ++j) + bindposeNorms[pTri->index[j]] += triNorm; + } + + // "Average" the surface normals, by normalizing them + for (int i = 0; i < _numVerts; ++i) + bindposeNorms[i].normalize (); + + // + // At this stage we have all vertex normals computed + // for the model geometry in bind-pos + // + + // Zero out all weight normals + for (int i = 0; i < _numWeights; ++i) + _weights[i]->norm = kZeroVectorf; + + // Compute weight normals by invert-transforming the normal + // by the bone-space matrix + for (int i = 0; i < _numVerts; ++i) + { + for (int j = 0; j < _verts[i]->countWeight; ++j) + { + Md5Weight_t *pWeight + = _weights[_verts[i]->startWeight + j].get (); + const Md5Joint_t *pJoint + = skel->joint (pWeight->joint); + + Vector3f wn = bindposeNorms[i]; + + // Compute inverse quaternion rotation + Quaternionf invRot = Inverse (pJoint->orient); + invRot.rotate (wn); + + pWeight->norm += wn; + } + } + + // Normalize all weight normals + for (int i = 0; i < _numWeights; ++i) + _weights[i]->norm.normalize (); +} + + +// -------------------------------------------------------------------------- +// Md5Mesh::computeWeightTangents +// +// Compute per-vertex tangent vectors and then, calculate the weight +// tangent. +// -------------------------------------------------------------------------- + +void +Md5Mesh::computeWeightTangents (Md5Skeleton *skel) +{ + vector bindposeVerts (_numVerts); + vector bindposeNorms (_numVerts); + vector bindposeTans (_numVerts); + + vector sTan (_numVerts); + vector tTan (_numVerts); + + // Zero out all weight tangents (thank you Valgrind) + for (int i = 0; i < _numWeights; ++i) + _weights[i]->tan = kZeroVectorf; + + // Compute bind-pose vertices and normals + for (int i = 0; i < _numVerts; ++i) + { + // Zero out final vertex position, normal and tangent + bindposeVerts[i] = kZeroVectorf; + bindposeNorms[i] = kZeroVectorf; + bindposeTans[i] = kZeroVectorf; + + // Zero s-tangents and t-tangents + sTan[i] = kZeroVectorf; + tTan[i] = kZeroVectorf; + + for (int j = 0; j < _verts[i]->countWeight; ++j) + { + const Md5Weight_t *pWeight + = _weights[_verts[i]->startWeight + j].get (); + const Md5Joint_t *pJoint + = skel->joint (pWeight->joint); + + // Calculate transformed vertex for this weight + Vector3f wv = pWeight->pos; + pJoint->orient.rotate (wv); + + bindposeVerts[i] += (pJoint->pos + wv) * pWeight->bias; + + // Calculate transformed normal for this weight + Vector3f wn = pWeight->norm; + pJoint->orient.rotate (wn); + + bindposeNorms[i] += wn * pWeight->bias; + } + } + + // Calculate s-tangeants and t-tangeants at triangle level + for (int i = 0; i < _numTris; ++i) + { + const Md5Triangle_t *pTri = _tris[i].get (); + + const Vector3f &v0 = bindposeVerts[pTri->index[0]]; + const Vector3f &v1 = bindposeVerts[pTri->index[1]]; + const Vector3f &v2 = bindposeVerts[pTri->index[2]]; + + const vec2_t &w0 = _verts[pTri->index[0]]->st; + const vec2_t &w1 = _verts[pTri->index[1]]->st; + const vec2_t &w2 = _verts[pTri->index[2]]->st; + + float x1 = v1._x - v0._x; + float x2 = v2._x - v0._x; + float y1 = v1._y - v0._y; + float y2 = v2._y - v0._y; + float z1 = v1._z - v0._z; + float z2 = v2._z - v0._z; + + float s1 = w1[0] - w0[0]; + float s2 = w2[0] - w0[0]; + float t1 = w1[1] - w0[1]; + float t2 = w2[1] - w0[1]; + + float r = (s1 * t2) - (s2 * t1); + + if (r == 0.0f) + // Prevent division by zero + r = 1.0f; + + float oneOverR = 1.0f / r; + + Vector3f sDir ((t2 * x1 - t1 * x2) * oneOverR, + (t2 * y1 - t1 * y2) * oneOverR, + (t2 * z1 - t1 * z2) * oneOverR); + Vector3f tDir ((s1 * x2 - s2 * x1) * oneOverR, + (s1 * y2 - s2 * y1) * oneOverR, + (s1 * z2 - s2 * z1) * oneOverR); + + for (int j = 0; j < 3; ++j) + { + sTan[pTri->index[j]] += sDir; + tTan[pTri->index[j]] += tDir; + } + } + + // Calculate vertex tangent + for (int i = 0; i < _numVerts; ++i) + { + const Vector3f &n = bindposeNorms[i]; + const Vector3f &t = sTan[i]; + + // Gram-Schmidt orthogonalize + bindposeTans[i] = (t - n * DotProduct (n, t)); + bindposeTans[i].normalize (); + + // Calculate handedness + if (DotProduct (CrossProduct (n, t), tTan[i]) < 0.0f) + bindposeTans[i] = -bindposeTans[i]; + + // Compute weight tangent + for (int j = 0; j < _verts[i]->countWeight; ++j) + { + Md5Weight_t *pWeight + = _weights[_verts[i]->startWeight + j].get (); + const Md5Joint_t *pJoint + = skel->joint (pWeight->joint); + + Vector3f wt = bindposeTans[i]; + + // Compute inverse quaternion rotation + Quaternionf invRot = Inverse (pJoint->orient); + invRot.rotate (wt); + + pWeight->tan += wt; + } + } + + // Normalize all weight tangents + for (int i = 0; i < _numWeights; ++i) + _weights[i]->tan.normalize (); +} + + +// -------------------------------------------------------------------------- +// Md5Mesh::computeBoundingBox +// +// Compute mesh bounding box for a given skeleton. +// -------------------------------------------------------------------------- + +void +Md5Mesh::computeBoundingBox (Md5Skeleton *skel) +{ + Vector3f max (-99999.0f, -99999.0f, -99999.0f); + Vector3f min ( 99999.0f, 99999.0f, 99999.0f); + + for (int i = 0; i < _numVerts; ++i) + { + Vector3f finalVertex = kZeroVectorf; + + // Calculate final vertex to draw with weights + for (int j = 0; j < _verts[i]->countWeight; ++j) + { + const Md5Weight_t *pWeight + = _weights[_verts[i]->startWeight + j].get (); + const Md5Joint_t *pJoint + = skel->joint (pWeight->joint); + + // Calculate transformed vertex for this weight + Vector3f wv = pWeight->pos; + pJoint->orient.rotate (wv); + + // The sum of all pWeight->bias should be 1.0 + finalVertex += (pJoint->pos + wv) * pWeight->bias; + } + + if (finalVertex._x > max._x) + max._x = finalVertex._x; + + if (finalVertex._x < min._x) + min._x = finalVertex._x; + + if (finalVertex._y > max._y) + max._y = finalVertex._y; + + if (finalVertex._y < min._y) + min._y = finalVertex._y; + + if (finalVertex._z > max._z) + max._z = finalVertex._z; + + if (finalVertex._z < min._z) + min._z = finalVertex._z; + } + + _boundingBox.min = min; + _boundingBox.max = max; +} + + +// -------------------------------------------------------------------------- +// Md5Mesh::preRenderVertexArrays +// +// Pre-rendering preparation. Setup some render-path dependent stuff, +// like tangent arrays for ARB programs and shaders. +// -------------------------------------------------------------------------- + +void +Md5Mesh::preRenderVertexArrays () const +{ + switch (Md5Model::renderPath) + { + case R_normal: + break; + + case R_ARBfp_diffuse: + case R_ARBfp_diffuse_specular: + case R_ARBfp_ds_parallax: + { + Md5Model::vp->use (); + Md5Model::fp->use (); + + glEnableVertexAttribArrayARB (TANGENT_LOC); + glVertexAttribPointerARB (TANGENT_LOC, 3, GL_FLOAT, GL_FALSE, + 0, &_tangentArray.front ()); + break; + } + + case R_shader: + { + if (Md5Model::tangentLoc == -1) + break; + + Md5Model::shader->use (); + + if (GLEW_VERSION_2_0) + { + glEnableVertexAttribArray (Md5Model::tangentLoc); + glVertexAttribPointer (Md5Model::tangentLoc, 3, GL_FLOAT, + GL_FALSE, 0, &_tangentArray.front ()); + } + else + { + glEnableVertexAttribArrayARB (Md5Model::tangentLoc); + glVertexAttribPointerARB (Md5Model::tangentLoc, 3, GL_FLOAT, + GL_FALSE, 0, &_tangentArray.front ()); + } + break; + } + } +} + + +// -------------------------------------------------------------------------- +// Md5Mesh::postRenderVertexArrays +// +// Post-rendering operations. Render-path dependent cleaning up after +// mesh rendering, like disabling tangent arrays for ARB programs and +// shaders. +// -------------------------------------------------------------------------- + +void +Md5Mesh::postRenderVertexArrays () const +{ + switch (Md5Model::renderPath) + { + case R_normal: + break; + + case R_ARBfp_diffuse: + case R_ARBfp_diffuse_specular: + case R_ARBfp_ds_parallax: + { + Md5Model::vp->unuse (); + Md5Model::fp->unuse (); + + glDisableVertexAttribArrayARB (TANGENT_LOC); + break; + } + + case R_shader: + { + if (Md5Model::tangentLoc == -1) + break; + + Md5Model::shader->unuse (); + + if (GLEW_VERSION_2_0) + glDisableVertexAttribArray (Md5Model::tangentLoc); + else + glDisableVertexAttribArrayARB (Md5Model::tangentLoc); + + break; + } + } +} + + +// -------------------------------------------------------------------------- +// Md5Mesh::renderVertexArray +// +// Render mesh with vertex array. +// -------------------------------------------------------------------------- + +void Md5Mesh::renderVertexArrays () const { + // Ensable shader/program's stuff + preRenderVertexArrays (); + + glEnableClientState (GL_VERTEX_ARRAY); + glEnableClientState (GL_NORMAL_ARRAY); + glEnableClientState (GL_TEXTURE_COORD_ARRAY); + + // Upload mesh data to OpenGL + glVertexPointer (3, GL_FLOAT, 0, &_vertexArray.front ()); + glNormalPointer (GL_FLOAT, 0, &_normalArray.front ()); + glTexCoordPointer (2, GL_FLOAT, 0, &_texCoordArray.front ()); + + // Bind to mesh's textures + setupTexture(_heightMap, GL_TEXTURE3); + setupTexture(_normalMap, GL_TEXTURE2); + setupTexture(_specMap, GL_TEXTURE1); + setupTexture(_decal, GL_TEXTURE0); + + // Draw the mesh + glDrawElements (GL_TRIANGLES, _numTris * 3, + GL_UNSIGNED_INT, &_vertIndices.front ()); + + resetReversedTexture(_heightMap, GL_TEXTURE3); + resetReversedTexture(_normalMap, GL_TEXTURE2); + resetReversedTexture(_specMap, GL_TEXTURE1); + resetReversedTexture(_decal, GL_TEXTURE0); + + + glDisableClientState (GL_TEXTURE_COORD_ARRAY); + glDisableClientState (GL_NORMAL_ARRAY); + glDisableClientState (GL_VERTEX_ARRAY); + + // Disable shader/program's stuff + postRenderVertexArrays (); +} + + +// -------------------------------------------------------------------------- +// Md5Mesh::drawNormals +// +// Draw mesh's vertex normals and tangents. +// -------------------------------------------------------------------------- + +void +Md5Mesh::drawNormals () const +{ + Vector3f thisVertex, thisNormal, thisTangent; + + glPushAttrib (GL_ENABLE_BIT); + glDisable (GL_LIGHTING); + glDisable (GL_TEXTURE_2D); + + // Blue + glColor3f (0.0, 0.0, 1.0); + + // Draw normals + glBegin (GL_LINES); + for (int i = 0; i < _numVerts * 3; i += 3) + { + thisVertex._x = _vertexArray[i + 0]; + thisVertex._y = _vertexArray[i + 1]; + thisVertex._z = _vertexArray[i + 2]; + + thisNormal._x = _normalArray[i + 0]; + thisNormal._y = _normalArray[i + 1]; + thisNormal._z = _normalArray[i + 2]; + + Vector3f normVec (thisVertex + thisNormal); + + glVertex3fv (thisVertex); + glVertex3fv (normVec); + } + glEnd (); + + // Magenta + glColor3f (1.0, 0.0, 1.0); + + // Draw tangents + glBegin (GL_LINES); + for (int i = 0; i < _numVerts * 3; i += 3) + { + thisVertex._x = _vertexArray[i + 0]; + thisVertex._y = _vertexArray[i + 1]; + thisVertex._z = _vertexArray[i + 2]; + + thisTangent._x = _tangentArray[i + 0]; + thisTangent._y = _tangentArray[i + 1]; + thisTangent._z = _tangentArray[i + 2]; + + Vector3f tanVec (thisVertex + thisTangent); + + glVertex3fv (thisVertex); + glVertex3fv (tanVec); + } + glEnd (); + + // GL_ENABLE_BIT + glPopAttrib (); +} + + +// -------------------------------------------------------------------------- +// Md5Mesh::allocVertexArrays +// +// Allocate memory for vertex arrays. NOTE: we need to have triangle +// data first! (_tris) +// -------------------------------------------------------------------------- + +void +Md5Mesh::allocVertexArrays () +{ + _vertexArray.reserve (_numVerts * 3); + _normalArray.reserve (_numVerts * 3); + _tangentArray.reserve (_numVerts * 3); + _texCoordArray.reserve (_numVerts * 2); + _vertIndices.reserve (_numTris * 3); + + // We can already initialize the vertex index array (we won't have + // to do it each time we want to draw) + for (int i = 0; i < _numTris; ++i) + { + for (int j = 0; j < 3; ++j) + _vertIndices.push_back (_tris[i]->index[j]); + } +} + + +// -------------------------------------------------------------------------- +// Md5Mesh::setupTexCoordArray +// +// Compute texture coordinate array. +// -------------------------------------------------------------------------- + +void +Md5Mesh::setupTexCoordArray () +{ + for (int i = 0, j = 0; i < _numVerts; ++i, j += 2) + { + // j = i * 2; + _texCoordArray[j + 0] = _verts[i]->st[0]; + _texCoordArray[j + 1] = _verts[i]->st[1]; + } +} + + +// -------------------------------------------------------------------------- +// Md5Mesh::setupTexture +// +// Setup texture 'tex' for the given texture unit. +// -------------------------------------------------------------------------- + +void Md5Mesh::setupTexture(const Texture2D *tex, GLenum texUnit) const { + if(!tex) { + // Disable texture and return + glActiveTexture (texUnit); + glBindTexture (GL_TEXTURE_2D, 0); + } + else { + tex->bind (texUnit); + + // Doom 3 doesn't use the OpenGL standard coordinate system + // for images, i.e. image data "starts" at the upper-left + // corner instead of the lower-left corner. + + // We must reverse the t component if the texture + // has been built with OpenGL's coord. system + if (tex->stdCoordSystem ()) { + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glScalef(1.0f, -1.0f, 1.0f); + glTranslatef(0.0f, -1.0f, 0.0f); + glMatrixMode(GL_MODELVIEW); + } + } +} + +void Md5Mesh::resetReversedTexture(const Texture2D *tex, GLenum texUnit) const { + if(tex) { + tex->bind(texUnit); + + // Doom 3 doesn't use the OpenGL standard coordinate system + // for images, i.e. image data "starts" at the upper-left + // corner instead of the lower-left corner. + + // We must UNDO the reversing of the t component if the texture + // has been built with OpenGL's coord. system + if(tex->stdCoordSystem ()) { + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glTranslatef(0.0f, 0.0f, 0.0f); + glScalef(1.0f, 1.0f, 1.0f); + glMatrixMode(GL_MODELVIEW); + } + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// class Md5Model implementation. +// +///////////////////////////////////////////////////////////////////////////// + +// -------------------------------------------------------------------------- +// Md5Model::Md5Model +// +// Constructor. Load MD5 mesh from file. +// -------------------------------------------------------------------------- + +Md5Model::Md5Model (const string &filename) + throw (Md5Exception) + : _numJoints (0), _numMeshes (0) +{ + // Open file + std::ifstream ifs (filename.c_str(), std::ios::in); + + if (ifs.fail ()) + throw Md5Exception ("Couldn't open md5model file: " + filename, filename); + + while (!ifs.eof ()) + { + string token, buffer; + int version; + + // Read next token + ifs >> token; + + if (token == "//") + { + // This is the begining of a comment + // Eat up rest of the line + std::getline (ifs, buffer); + } + else if (token == "MD5Version") + { + ifs >> version; + + if (version != kMd5Version) + throw Md5Exception ("Bad ifs version", filename); + } + else if (token == "numJoints") + { + ifs >> _numJoints; + } + else if (token == "numMeshes") + { + ifs >> _numMeshes; + _meshes.reserve (_numMeshes); + } + else if (token == "joints") + { + // Base skeleton data + ifs >> token; // "{" + + Md5Skeleton *skel = new Md5Skeleton (ifs, _numJoints); + _baseSkeleton = Md5SkeletonPtr (skel); + + ifs >> token; // "}" + } + else if (token == "mesh") + { + ifs >> token; // "{" + + // Create and load a new model mesh + Md5MeshPtr mesh = Md5MeshPtr (new Md5Mesh (ifs)); + + // Compute bounding box in bind-pose + mesh->computeBoundingBox (_baseSkeleton.get ()); + + // Compute weight normals + mesh->computeWeightNormals (_baseSkeleton.get ()); + + // Compute weight tangents + mesh->computeWeightTangents (_baseSkeleton.get ()); + + _meshes.push_back (mesh); + } + } + + ifs.close (); + + // Compute the bounding box in bind-pose + computeBindPoseBoundingBox (); +} + + +// -------------------------------------------------------------------------- +// Md5Model::~Md5Model +// +// Destructor. Free all data allocated for the model. +// -------------------------------------------------------------------------- + +Md5Model::~Md5Model () +{ +} + + +// -------------------------------------------------------------------------- +// Md5Model::drawModel +// +// Draw each mesh of the model. +// -------------------------------------------------------------------------- + +void +Md5Model::drawModel () const +{ + for (int i = 0; i < _numMeshes; ++i) + { + if (_meshes[i]->show ()) + { + _meshes[i]->renderVertexArrays (); + + if (Md5Model::bDrawNormals) + _meshes[i]->drawNormals (); + } + } +} + + +// -------------------------------------------------------------------------- +// Md5Model::prepare +// +// Prepare each mesh of the model for drawing, i.e. compute final vertex +// positions, normals and other vertex's related data. +// -------------------------------------------------------------------------- + +void Md5Model::prepare (Md5Skeleton *skel) { + for (int i = 0; i < _numMeshes; ++i) { + if (!_meshes[i]->hiden ()) { + // Prepare for drawing with interpolated skeleton + _meshes[i]->setupVertexArrays(skel); + } + } +} + + +// -------------------------------------------------------------------------- +// Md5Model::addAnim +// +// Load a MD5 animation and add it to the animation list. Return true if +// the animation has been loaded successfully. +// -------------------------------------------------------------------------- + +bool +Md5Model::addAnim (const string &filename) +{ + Md5Animation *pAnim = new Md5Animation (filename); + + // Check for compatibility + if (!validityCheck (pAnim)) + { + cerr << filename << " isn't a valid animation" + << " for this model!" << endl; + delete pAnim; + return false; + } + + const string name (pAnim->name ()); + + // If there is already an animation with same name, + // delete it + AnimMap::iterator itor = _animList.find (name); + if (itor != _animList.end()) + _animList.erase (itor); + + // Insert the new animation + _animList.insert (AnimMap::value_type (name, Md5AnimationPtr (pAnim))); + + return true; +} + + +// -------------------------------------------------------------------------- +// Md5Model::setMeshRenderState +// Md5Model::setMeshDecalMap +// Md5Model::setMeshSpecularMap +// Md5Model::setMeshNormalMap +// Md5Model::setMeshHeightMap +// +// Setup mesh's state or texture. +// -------------------------------------------------------------------------- + +void +Md5Model::setMeshRenderState (const string &name, int state) +{ + if (Md5Mesh *mesh = getMeshByName (name)) + mesh->setState (state); +} + + +void +Md5Model::setMeshDecalMap (const string &name, const Texture2D *tex) +{ + if (Md5Mesh *mesh = getMeshByName (name)) + mesh->setDecalMap (tex); +} + + +void +Md5Model::setMeshSpecularMap (const string &name, const Texture2D *tex) +{ + if (Md5Mesh *mesh = getMeshByName (name)) + mesh->setSpecularMap (tex); +} + + +void +Md5Model::setMeshNormalMap (const string &name, const Texture2D *tex) +{ + if (Md5Mesh *mesh = getMeshByName (name)) + mesh->setNormalMap (tex); +} + + +void +Md5Model::setMeshHeightMap (const string &name, const Texture2D *tex) +{ + if (Md5Mesh *mesh = getMeshByName (name)) + mesh->setHeightMap (tex); +} + + +// -------------------------------------------------------------------------- +// Md5Model::anim +// +// Accessor. Return animation from list. +// -------------------------------------------------------------------------- + +const Md5Animation* +Md5Model::anim (const string &name) const +{ + AnimMap::const_iterator itor = _animList.find (name); + if (itor != _animList.end ()) + return itor->second.get (); + + return NULL; +} + + +// -------------------------------------------------------------------------- +// Md5Model::computeBindPoseBoundingBox +// +// Compute model's bounding box in bind-pose. +// -------------------------------------------------------------------------- + +void +Md5Model::computeBindPoseBoundingBox () +{ + Vector3f max (-99999.0f, -99999.0f, -99999.0f); + Vector3f min ( 99999.0f, 99999.0f, 99999.0f); + + // Get the min and the max from all mesh bounding boxes + for (int i = 0; i < _numMeshes; ++i) + { + const BoundingBox_t &meshBox = _meshes[i]->boundingBox (); + + if (meshBox.max._x > max._x) + max._x = meshBox.max._x; + + if (meshBox.min._x < min._x) + min._x = meshBox.min._x; + + if (meshBox.max._y > max._y) + max._y = meshBox.max._y; + + if (meshBox.min._y < min._y) + min._y = meshBox.min._y; + + if (meshBox.max._z > max._z) + max._z = meshBox.max._z; + + if (meshBox.min._z < min._z) + min._z = meshBox.min._z; + } + + _bindPoseBox.min = min; + _bindPoseBox.max = max; +} + + +// -------------------------------------------------------------------------- +// Md5Model::validityCheck +// +// Check if an animation is valid for this model or not. A valid +// animation must have a skeleton with the same number of joints with +// model's skeleton and for each skeleton joint, name and parent Id must +// match. +// -------------------------------------------------------------------------- + +bool +Md5Model::validityCheck (Md5Animation *anim) const +{ + if (!anim) + return false; + + if (_numJoints != anim->frame (0)->numJoints()) + return false; + + for (int i = 0; i < _numJoints; ++i) + { + const Md5Joint_t *modelJoint = _baseSkeleton->joint (i); + const Md5Joint_t *animJoint = anim->frame (0)->joint (i); + + if (modelJoint->name != animJoint->name) + return false; + + if (modelJoint->parent != animJoint->parent) + return false; + } + + return true; +} + + +// -------------------------------------------------------------------------- +// Md5Model::getMeshByName +// +// Get pointer to a mesh given its name. Return NULL if there is no mesh +// with such a name. +// -------------------------------------------------------------------------- + +Md5Mesh* +Md5Model::getMeshByName (const string &name) const +{ + for (int i = 0; i < _numMeshes; ++i) + { + if (_meshes[i]->name () == name) + return _meshes[i].get (); + } + + return NULL; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// class Md5Animation implementation. +// +///////////////////////////////////////////////////////////////////////////// + +// -------------------------------------------------------------------------- +// Md5Animation::Md5Animation +// +// Constructor. Load MD5 animation from <.md5anim> file. +// -------------------------------------------------------------------------- + +Md5Animation::Md5Animation (const string &filename) + throw (Md5Exception) + : _numFrames (0), _frameRate (0) +{ + vector jointInfos; + vector baseFrame; + vector animFrameData; + int numJoints = 0; + int numAnimatedComponents = 0; + + // Open file + std::ifstream ifs (filename.c_str(), std::ios::in); + + if (ifs.fail()) + throw Md5Exception ("Couldn't open md5anim file: " + filename, filename); + + while (!ifs.eof ()) + { + string token, buffer; + int version; + int i; + + // Read next token + ifs >> token; + + if (token == "//") + { + // This is the begining of a comment + // Eat up rest of the line + std::getline (ifs, buffer); + } + else if (token == "MD5Version") + { + ifs >> version; + + if (version != kMd5Version) + throw Md5Exception ("Bad file version", filename); + } + else if (token == "numFrames") + { + ifs >> _numFrames; + _skelframes.reserve (_numFrames); + _bboxes.reserve (_numFrames); + } + else if (token == "numJoints") + { + ifs >> numJoints; + jointInfos.reserve (numJoints); + baseFrame.reserve (numJoints); + } + else if (token == "frameRate") + { + ifs >> _frameRate; + } + else if (token == "numAnimatedComponents") + { + ifs >> numAnimatedComponents; + animFrameData.reserve (numAnimatedComponents); + } + else if (token == "hierarchy") + { + // Read all joint infos + ifs >> token; // "{" + + // Read all joint infos + for (i = 0; i < numJoints; ++i) + { + JointInfo jinfo; + + ifs >> jinfo.name; + ifs >> jinfo.parent; + ifs >> jinfo.flags.value; + ifs >> jinfo.startIndex; + + jointInfos.push_back (jinfo); + + // Eat up rest of the line + std::getline (ifs, buffer); + } + + ifs >> token; // "}" + } + else if (token == "bounds") + { + ifs >> token; // "{" + + // Read frame bounds + for (int i = 0; i < _numFrames; ++i) + { + BoundingBoxPtr bbox (new BoundingBox_t); + + ifs >> token; // "(" + ifs >> bbox->min._x; + ifs >> bbox->min._y; + ifs >> bbox->min._z; + ifs >> token; // ")" + ifs >> token; // "(" + ifs >> bbox->max._x; + ifs >> bbox->max._y; + ifs >> bbox->max._z; + ifs >> token; // ")" + + _bboxes.push_back (bbox); + } + + ifs >> token; // "}" + } + else if (token == "baseframe") + { + // We should have an opening bracket for the baseframe joint list + ifs >> token; // "{" + + // Read baseframe data + for (i = 0; i < numJoints; ++i) + { + BaseFrameJoint bfj; + + ifs >> token; // "(" + ifs >> bfj.pos._x; + ifs >> bfj.pos._y; + ifs >> bfj.pos._z; + ifs >> token; // ")" + ifs >> token; // "(" + ifs >> bfj.orient._x; + ifs >> bfj.orient._y; + ifs >> bfj.orient._z; + ifs >> token; // ")" + + baseFrame.push_back (bfj); + + // Eat up rest of the line + std::getline (ifs, buffer); + } + + ifs >> token; // "}" + } + else if (token == "frame") + { + int frameIndex; + ifs >> frameIndex; + ifs >> token; // "{" + + animFrameData.clear (); + + // Read all frame data + float afvalue; + for (i = 0; i < numAnimatedComponents; ++i) + { + // NOTE about coding style: beeuuarg *vomit* + ifs >> afvalue; + animFrameData.push_back (afvalue); + } + + ifs >> token; // "}" + + // Build skeleton for this frame + buildFrameSkeleton (jointInfos, baseFrame, animFrameData); + } + } + + ifs.close (); + + // Extract animation's name + string szfile = filename; + string::size_type start = szfile.find_last_of ('/'); + string::size_type end = szfile.find_last_of (".md5anim"); + _name = szfile.substr (start + 1, end - start - 8); +} + + +// -------------------------------------------------------------------------- +// Md5Animation::~Md5Animation +// +// Destructor. Free all data allocated for the animation. +// -------------------------------------------------------------------------- + +Md5Animation::~Md5Animation () +{ +} + + +// -------------------------------------------------------------------------- +// Md5Animation::buildFrameSkeleton +// +// Build a skeleton for a particular frame. The skeleton is transformed +// by the given modelview matrix so that it is possible to obtain the +// skeleton in absolute coordinates. +// -------------------------------------------------------------------------- + +void +Md5Animation::buildFrameSkeleton (vector &jointInfos, + vector &baseFrame, + vector &animFrameData) +{ + // Allocate memory for this frame + Md5SkeletonPtr skelframe (new Md5Skeleton); + _skelframes.push_back (skelframe); + + skelframe->setNumJoints (jointInfos.size ()); + + // Setup all joints for this frame + for (unsigned int i = 0; i < jointInfos.size (); ++i) + { + BaseFrameJoint *baseJoint = &baseFrame[i]; + Vector3f animatedPos = baseJoint->pos; + Quaternionf animatedOrient = baseJoint->orient; + int j = 0; + + if (jointInfos[i].flags.tx) // Tx + { + animatedPos._x = animFrameData[jointInfos[i].startIndex + j]; + ++j; + } + + if (jointInfos[i].flags.ty) // Ty + { + animatedPos._y = animFrameData[jointInfos[i].startIndex + j]; + ++j; + } + + if (jointInfos[i].flags.tz) // Tz + { + animatedPos._z = animFrameData[jointInfos[i].startIndex + j]; + ++j; + } + + if (jointInfos[i].flags.qx) // Qx + { + animatedOrient._x = animFrameData[jointInfos[i].startIndex + j]; + ++j; + } + + if (jointInfos[i].flags.qy) // Qy + { + animatedOrient._y = animFrameData[jointInfos[i].startIndex + j]; + ++j; + } + + if (jointInfos[i].flags.qz) // Qz + { + animatedOrient._z = animFrameData[jointInfos[i].startIndex + j]; + ++j; + } + + // Compute orient quaternion's w value + animatedOrient.computeW (); + + // NOTE: we assume that this joint's parent has + // already been calculated, i.e. joint's ID should + // never be smaller than its parent ID. + Md5Joint_t *thisJoint = new Md5Joint_t; + skelframe->addJoint (thisJoint); + + int parent = jointInfos[i].parent; + thisJoint->parent = parent; + thisJoint->name = jointInfos[i].name; + + // has parent? + if (thisJoint->parent < 0) + { + thisJoint->pos = animatedPos; + thisJoint->orient = animatedOrient; + } + else + { + const Md5Joint_t *parentJoint = skelframe->joint (parent); + + parentJoint->orient.rotate (animatedPos); + thisJoint->pos = animatedPos + parentJoint->pos; + + thisJoint->orient = parentJoint->orient * animatedOrient; + thisJoint->orient.normalize (); + } + } +} + + +// -------------------------------------------------------------------------- +// Md5Animation::interpolate +// +// Build an interpolated skeleton given two frame indexes and an +// interpolation percentage. 'out' must be non-null. +// -------------------------------------------------------------------------- + +void +Md5Animation::interpolate (int frameA, int frameB, + float interp, Md5Skeleton *out) const +{ + for (int i = 0; i < out->numJoints (); ++i) + { + const Md5Joint_t *pJointA = _skelframes[frameA]->joint (i); + const Md5Joint_t *pJointB = _skelframes[frameB]->joint (i); + Md5Joint_t *pFinalJoint = out->joint (i); + + pFinalJoint->parent = pJointA->parent; + pFinalJoint->pos = pJointA->pos + interp * (pJointB->pos - pJointA->pos); + pFinalJoint->orient = Slerp (pJointA->orient, pJointB->orient, interp); + } +} + + +///////////////////////////////////////////////////////////////////////////// +// +// class Md5Object implementation. +// +///////////////////////////////////////////////////////////////////////////// + +// -------------------------------------------------------------------------- +// Md5Object::Md5Object +// +// Constructor. +// -------------------------------------------------------------------------- + +Md5Object::Md5Object (Md5Model *model) + : _model (NULL), _animatedSkeleton (NULL), _softwareTransformation (false), + _currAnim (NULL), _currFrame (0), _nextFrame (1), _last_time (0.0), + _max_time (0.0), _renderFlags (kDrawModel) +{ + setMd5Model (model); +} + + +// -------------------------------------------------------------------------- +// Md5Object::~Md5Object +// +// Destructor. Free all data allocated for the object. +// -------------------------------------------------------------------------- + +Md5Object::~Md5Object () +{ + delete _animatedSkeleton; +} + + +// -------------------------------------------------------------------------- +// Md5Object::setMd5Model +// +// Attach MD5 Model to Object. +// -------------------------------------------------------------------------- + +void +Md5Object::setMd5Model (Md5Model *model) +{ + if (_model != model) + { + _model = model; // Link to the model + + // Delete previous skeletons because the new + // model is different and its skeleton can hold + // more joints. + delete _animatedSkeleton; + + // Copy skeleton joints name + _animatedSkeleton = _model->baseSkeleton ()->clone (); + + // Reset animation + _currAnim = NULL; + _currAnimName.clear (); + } +} + + +// -------------------------------------------------------------------------- +// Md5Object::setAnim +// +// Set the current animation to play. +// -------------------------------------------------------------------------- + +void +Md5Object::setAnim (const string &name) +{ + if (_model) + { + // Retrieve animation from model's animation list + if ((_currAnim = _model->anim (name))) + { + _currAnimName = _currAnim->name (); + + // Compute max frame time and reset _last_time + _max_time = 1.0 / static_cast(_currAnim->frameRate ()); + _last_time = 0.0; + + // Reset current and next frames + _currFrame = 0; + _nextFrame = (_currAnim->maxFrame () > 0) ? 1 : 0; + } + else + { + delete _animatedSkeleton; + _currAnimName.clear (); + + // Rebuild animated skeleton with model's base skeleton + _animatedSkeleton = _model->baseSkeleton ()->clone (); + } + } +} + + +// -------------------------------------------------------------------------- +// Md5Object::animate +// +// Compute current and next frames for model's animation. +// -------------------------------------------------------------------------- + +void +Md5Object::animate (double dt) +{ + // Animate only if there is an animation... + if (_currAnim) + { + _last_time += dt; + + // Move to next frame? + if (_last_time >= _max_time) + { + _currFrame++; + _nextFrame++; + _last_time = 0.0f; + + unsigned int maxFrame = _currAnim->maxFrame (); + + if (_currFrame > maxFrame) + _currFrame = 0; + + if (_nextFrame > maxFrame) + _nextFrame = 0; + } + } +} + + +// -------------------------------------------------------------------------- +// Md5Object::computeBoundingBox +// +// Compute object's oriented bounding box. +// -------------------------------------------------------------------------- + +void +Md5Object::computeBoundingBox () +{ + BoundingBox_t bbox; + + if (_currAnim) + { + // Interpolate frames' bounding box in order + // to get animated AABB in object space + const BoundingBox_t *boxA, *boxB; + boxA = _currAnim->frameBounds (_currFrame); + boxB = _currAnim->frameBounds (_nextFrame); + + float interp = _last_time * _currAnim->frameRate (); + + bbox.min = boxA->min + (boxB->min - boxA->min) * interp; + bbox.max = boxA->max + (boxB->max - boxA->max) * interp; + } + else + { + // Get bind-pose model's bouding box + bbox = _model->bindPoseBoundingBox (); + } + + // Compute oriented bounding box + _bbox.world = _modelView; + _bbox.center = Vector3f ((bbox.max._x + bbox.min._x) * 0.5f, + (bbox.max._y + bbox.min._y) * 0.5f, + (bbox.max._z + bbox.min._z) * 0.5f); + _bbox.extent = Vector3f (bbox.max._x - _bbox.center._x, + bbox.max._y - _bbox.center._y, + bbox.max._z - _bbox.center._z); +} + + +// -------------------------------------------------------------------------- +// Md5Object::prepare +// +// Prepare object to be drawn. Interpolate skeleton frames if the +// object is animated, or copy model's base skeleton to current +// skeleton if not. +// Apply model view transformation if asked. +// -------------------------------------------------------------------------- + +void Md5Object::prepare(bool softwareTransformation) { + _softwareTransformation = softwareTransformation; + + if (_renderFlags & kDrawModel) + { + if (_currAnim) + { + // Interpolate current and next frame skeletons + float interp = _last_time * _currAnim->frameRate (); + _currAnim->interpolate (_currFrame, _nextFrame, + interp, _animatedSkeleton); + } + else + { + // If there is no animated skeleton, fall to + // model's base skeleton + delete _animatedSkeleton; + + _animatedSkeleton = _model->baseSkeleton()->clone (); + } + + if (_softwareTransformation || _renderFlags & kDrawJointLabels) + { + // Force software transformation if joint labels have + // to be drawn + _softwareTransformation = true; + + Quaternionf rot; + rot.fromMatrix (_modelView); + + // Applly Model-View transformation for each joint + for (int i = 0; i < _animatedSkeleton->numJoints (); ++i) + { + Md5Joint_t *thisJoint = _animatedSkeleton->joint (i); + + thisJoint->pos = _modelView * thisJoint->pos; + thisJoint->orient = rot * thisJoint->orient; + } + } + + // Setup vertex arrays + _model->prepare (_animatedSkeleton); + } +} + +// -------------------------------------------------------------------------- +// Md5Object::render +// +// Draw the object. +// -------------------------------------------------------------------------- + +void Md5Object::render () const { + glPushMatrix (); + //glTranslatef( 0.0f, -60.0f, 0.0f ); + //glRotatef( -90.0, 1.0, 0.0, 0.0 ); + //glRotatef( -90.0, 0.0, 0.0, 1.0 ); + //glTranslatef( 0.0f, -60.0f, 0.0f ); + glRotatef( -20.0, 1.0, 0.0, 0.0 ); + glRotatef( -20.0, 0.0, 1.0, 0.0 ); + //glRotatef( 50.0, 1.0, 0.0, 0.0 ); + //glTranslatef( 5.0f, -2.0f, -3.0f ); + glTranslatef(-1.4f, -1.4f, -7.5f); + glScalef(1/4.0f, 1/4.0f, 1); + + if (!_softwareTransformation) { + glMultMatrixf (_modelView._m); + } + glPushAttrib (GL_POLYGON_BIT | GL_ENABLE_BIT); + glFrontFace (GL_CW); + + if (_renderFlags & kDrawModel) { + _model->drawModel (); + } + if (_renderFlags & kDrawSkeleton) { + glDisable (GL_TEXTURE_2D); + glDisable (GL_LIGHTING); + + _animatedSkeleton->draw (_modelView,_renderFlags & kDrawJointLabels); + } + + // GL_POLYGON_BIT | GL_ENABLE_BIT + glPopAttrib (); + glPopMatrix (); +} + +}}} //end namespace diff --git a/source/shared_lib/sources/graphics/md5/Shader.cpp b/source/shared_lib/sources/graphics/md5/Shader.cpp new file mode 100644 index 00000000..dab5f543 --- /dev/null +++ b/source/shared_lib/sources/graphics/md5/Shader.cpp @@ -0,0 +1,454 @@ +// ============================================================== +// This file is part of MegaGlest (www.glest.org) +// +// Shader.cpp -- Copyright (c) 2006-2007 David Henry +// changed for use with MegaGlest: Copyright (C) 2011- by Mark Vejvoda +// +// This code is licenced under the MIT license. +// +// This software is provided "as is" without express or implied +// warranties. You may freely copy and compile this source into +// applications you distribute provided that the copyright text +// below is included in the resulting source code. +// +// Implementation of GLSL shader related classes. +// +///////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "Shader.h" +#include "GlErrors.h" + +namespace Shared { namespace Graphics { namespace md5 { + +using std::cout; +using std::cerr; +using std::endl; + + +///////////////////////////////////////////////////////////////////////////// +// +// Global shader related functions. +// +///////////////////////////////////////////////////////////////////////////// + +static GLboolean GLSLCapable = GL_FALSE; + +// -------------------------------------------------------------------------- +// hasShaderSupport +// +// Return true if the host has GLSL, so that we can use shaders. +// -------------------------------------------------------------------------- +GLboolean hasShaderSupport () { + return GLSLCapable; +} + +// -------------------------------------------------------------------------- +// checkExtensionPresence +// +// Check if an extension is present on the host OpenGL implementation. +// Increment @missing if the extension is missing. +// -------------------------------------------------------------------------- +static void checkExtensionPresence (const string &name, int &missing) { + if (!glewIsSupported (name.c_str ())) + { + cerr << "* missing " << name << " extension" << endl; + missing++; + } +} + +// -------------------------------------------------------------------------- +// initShaderHandling +// +// Initialize variables and extensions needed for using GLSL. This +// function should be called before any shader usage (at application +// initialization for example). +// -------------------------------------------------------------------------- +void initShaderHandling () { + int missing = 0; + + // Check for extensions needed for GLSL support on host + checkExtensionPresence ("GL_ARB_shader_objects", missing); + checkExtensionPresence ("GL_ARB_shading_language_100", missing); + checkExtensionPresence ("GL_ARB_vertex_shader", missing); + checkExtensionPresence ("GL_ARB_fragment_shader", missing); + + // Disable GLSL if one extension is missing + if (missing > 0) + GLSLCapable = GL_FALSE; + else + GLSLCapable = GL_TRUE; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// class Shader implementation. +// +///////////////////////////////////////////////////////////////////////////// + +// -------------------------------------------------------------------------- +// Shader::Shader +// +// Constructor. +// -------------------------------------------------------------------------- +Shader::Shader (const string &filename) + : _name (filename), _handle (0), _compiled (0) { +} + +// -------------------------------------------------------------------------- +// Shader::~Shader +// +// Destructor. Destroy the shader handle. +// -------------------------------------------------------------------------- +Shader::~Shader () { + if (GLEW_VERSION_2_0) + { + if (glIsShader (_handle)) + glDeleteShader (_handle); + } + else + { + GLint type; + glGetObjectParameterivARB (_handle, GL_OBJECT_TYPE_ARB, &type); + + if (GL_SHADER_OBJECT_ARB == type) + glDeleteObjectARB (_handle); + } +} + +// ------------------------------------------------------------------------- +// Shader::printInfoLog +// +// Print log info about a vertex or a fragment shader. +// ------------------------------------------------------------------------- +void Shader::printInfoLog () const { + GLint infologLength = 0; + + // First check for previous OpenGL errors... + checkOpenGLErrors (__FILE__, __LINE__); + + // Get log's length + if (GLEW_VERSION_2_0) + glGetShaderiv (_handle, GL_INFO_LOG_LENGTH, &infologLength); + else + glGetObjectParameterivARB (_handle, GL_OBJECT_INFO_LOG_LENGTH_ARB, + &infologLength); + + // If log is empty, quit + if (infologLength <= 1) + return; + + try + { + GLchar *infoLog = new GLchar[infologLength]; + + // Get the log... + if (GLEW_VERSION_2_0) + glGetShaderInfoLog (_handle, infologLength, NULL, infoLog); + else + glGetInfoLogARB (_handle, infologLength, NULL, infoLog); + + // ...and print it to standard output + cout << "Shader \"" << _name << "\" InfoLog (" + << infologLength << "):" << endl << infoLog << endl; + + delete [] infoLog; + } + catch (std::bad_alloc &err) + { + cerr << "Error: memory allocation failed for shader info log" + << endl << " Reason: " << err.what () << endl; + } +} + +// ------------------------------------------------------------------------- +// Shader::compile +// +// Create and compile the shader. +// ------------------------------------------------------------------------- +void Shader::compile () + throw (std::runtime_error) { + const GLchar *code = _code.c_str (); + + if (GLEW_VERSION_2_0) + { + // Create a shader object + _handle = glCreateShader (shaderType ()); + + // Upload shader code to OpenGL + glShaderSource (_handle, 1, &code, NULL); + + // Compile shader + glCompileShader (_handle); + glGetShaderiv (_handle, GL_COMPILE_STATUS, &_compiled); + printInfoLog (); + + // Check for success + if (GL_FALSE == _compiled) + throw std::runtime_error ("Compilation failed"); + } + else + { + // Create a shader object + _handle = glCreateShaderObjectARB (shaderType ()); + + // Upload shader code to OpenGL + glShaderSourceARB (_handle, 1, &code, NULL); + + // Compile shader + glCompileShaderARB (_handle); + glGetObjectParameterivARB (_handle, GL_OBJECT_COMPILE_STATUS_ARB, &_compiled); + printInfoLog (); + + // Check for success + if (GL_FALSE == _compiled) + throw std::runtime_error ("Compilation failed"); + } +} + +// ------------------------------------------------------------------------- +// Shader::loadShaderFile +// +// Load shader's GLSL code from file. The code is stored into the +// _code string member variable. +// ------------------------------------------------------------------------- +void Shader::loadShaderFile (const string &filename) throw (std::runtime_error) { + // Open the file + std::ifstream ifs (filename.c_str (), std::ios::in | std::ios::binary); + + if (ifs.fail ()) + throw std::runtime_error ("Couldn't open shader file: " + filename); + + // Read whole file into string + _code.assign (std::istreambuf_iterator(ifs), + std::istreambuf_iterator()); + + // Close file + ifs.close (); +} + + +///////////////////////////////////////////////////////////////////////////// +// +// class VertexShader implementation. +// +///////////////////////////////////////////////////////////////////////////// + +// -------------------------------------------------------------------------- +// VertexShader::VertexShader +// +// Constructor. Read vertex shader code from file and compile it. +// -------------------------------------------------------------------------- +VertexShader::VertexShader (const string &filename) + : Shader (filename) { + try + { + // Load shader code from file + loadShaderFile (filename); + + // Compile the shader + compile (); + + cout << "* Vertex shader \"" << _name << "\" compiled" << endl; + } + catch (std::runtime_error &err) + { + cerr << "Error: Faild to create vertex shader from " << _name << endl; + cerr << "Reason: " << err.what () << endl; + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// class FragmentShader implementation. +// +///////////////////////////////////////////////////////////////////////////// + +// -------------------------------------------------------------------------- +// FragmentShader::FragmentShader +// +// Constructor. Read fragment shader code from file and compile it. +// -------------------------------------------------------------------------- +FragmentShader::FragmentShader (const string &filename) + : Shader (filename) { + try + { + // Load shader code from file + loadShaderFile (filename); + + // Compile the shader + compile (); + + cout << "* Fragment shader \"" << _name << "\" compiled" << endl; + } + catch (std::runtime_error &err) + { + cerr << "Error: Faild to create fragment shader from " << _name << endl; + cerr << "Reason: " << err.what () << endl; + } +} +///////////////////////////////////////////////////////////////////////////// +// +// class ShaderProgram implementation. +// +///////////////////////////////////////////////////////////////////////////// + +// -------------------------------------------------------------------------- +// ShaderProgram::ShaderProgram +// +// Constructor. Link vertex and fragment shader. If the vertex shader +// or the fragment shader is invalid (has failed to compile), the +// shader program creation is aborted. +// -------------------------------------------------------------------------- + +ShaderProgram::ShaderProgram (const string &filename, + const VertexShader &vertexShader, + const FragmentShader &fragmentShader) + : _name (filename), _handle (0), _linked (0) { + try { + if (vertexShader.fail ()) + throw std::runtime_error ("Invalid vertex shader"); + + if (fragmentShader.fail ()) + throw std::runtime_error ("Invalid fragment shader"); + + if (GLEW_VERSION_2_0) + { + // Create program and attach vertex and fragment shaders + _handle = glCreateProgram (); + glAttachShader (_handle, vertexShader.handle ()); + glAttachShader (_handle, fragmentShader.handle ()); + + // Perform link stage + glLinkProgram (_handle); + glGetProgramiv (_handle, GL_LINK_STATUS, &_linked); + + // Validate program + glValidateProgram (_handle); + printInfoLog (); + + // Check for success + if (GL_FALSE == _linked) + throw std::runtime_error ("Link stage failed"); + } + else + { + // Create program and attach vertex and fragment shaders + _handle = glCreateProgramObjectARB (); + glAttachObjectARB (_handle, vertexShader.handle ()); + glAttachObjectARB (_handle, fragmentShader.handle ()); + + // Perform link stage + glLinkProgramARB (_handle); + glGetObjectParameterivARB (_handle, GL_OBJECT_LINK_STATUS_ARB, &_linked); + + // Validate program + glValidateProgramARB (_handle); + printInfoLog (); + + // Check for success + if (GL_FALSE == _linked) + throw std::runtime_error ("Link stage failed"); + } + + cout << "* Shader \"" << _name << "\" successfully linked" << endl; + } + catch (std::runtime_error &err) + { + cerr << "Error: Faild to create shader " << _name << endl; + cerr << "Reason: " << err.what () << endl; + } +} + +// -------------------------------------------------------------------------- +// ShaderProgram::~ShaderProgram +// +// Destructor. Destroy the shader program handle. +// -------------------------------------------------------------------------- +ShaderProgram::~ShaderProgram () { + if (GLEW_VERSION_2_0) + { + if (glIsProgram (_handle)) + glDeleteProgram (_handle); + } + else + { + GLint type; + glGetObjectParameterivARB (_handle, GL_OBJECT_TYPE_ARB, &type); + + if (GL_PROGRAM_OBJECT_ARB == type) + glDeleteObjectARB (_handle); + } +} + + +// ------------------------------------------------------------------------- +// ShaderProgram::use +// ShaderProgram::unuse +// +// Bind/unbind the shader. +// ------------------------------------------------------------------------- +void ShaderProgram::use () const { + if (GLEW_VERSION_2_0) + glUseProgram (_handle); + else + glUseProgramObjectARB (_handle); +} + +void ShaderProgram::unuse () const { + if (GLEW_VERSION_2_0) + glUseProgram (0); + else + glUseProgramObjectARB (0); +} + +// ------------------------------------------------------------------------- +// ShaderProgram::printInfoLog +// +// Print log info about a shader program. +// ------------------------------------------------------------------------- +void ShaderProgram::printInfoLog () const { + GLint infologLength = 0; + + // First check for previous OpenGL errors... + checkOpenGLErrors (__FILE__, __LINE__); + + // Get log's length + if (GLEW_VERSION_2_0) + glGetProgramiv (_handle, GL_INFO_LOG_LENGTH, &infologLength); + else + glGetObjectParameterivARB (_handle, GL_OBJECT_INFO_LOG_LENGTH_ARB, + &infologLength); + + + // If log is empty, quit + if (infologLength <= 1) + return; + + try + { + GLchar *infoLog = new GLchar[infologLength]; + + // Get the log... + if (GLEW_VERSION_2_0) + glGetProgramInfoLog (_handle, infologLength, NULL, infoLog); + else + glGetInfoLogARB (_handle, infologLength, NULL, infoLog); + + // ...and print it to standard output + cout << "Program \"" << _name << "\" InfoLog (" + << infologLength << "):" << endl << infoLog << endl; + + delete [] infoLog; + } + catch (std::bad_alloc &err) + { + cerr << "Error: memory allocation failed for shader program " + << "info log" << endl << " Reason: " << err.what () << endl; + } +} + +}}} //end namespace diff --git a/source/shared_lib/sources/graphics/md5/Texture.cpp b/source/shared_lib/sources/graphics/md5/Texture.cpp new file mode 100644 index 00000000..b5948fc7 --- /dev/null +++ b/source/shared_lib/sources/graphics/md5/Texture.cpp @@ -0,0 +1,516 @@ +// ============================================================== +// This file is part of MegaGlest (www.glest.org) +// +// Texture.cpp -- Copyright (c) 2006 David Henry +// changed for use with MegaGlest: Copyright (C) 2011- by Mark Vejvoda +// +// This code is licenced under the MIT license. +// +// This software is provided "as is" without express or implied +// warranties. You may freely copy and compile this source into +// applications you distribute provided that the copyright text +// below is included in the resulting source code. +// +// Implementation of an OpenGL texture classes. +// +///////////////////////////////////////////////////////////////////////////// + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif // _WIN32 + +#include +#include +#include + +#include "GlErrors.h" +#include "Texture.h" +#include "Image.h" + +namespace Shared { namespace Graphics { namespace md5 { + +using std::cout; +using std::cerr; +using std::endl; + +///////////////////////////////////////////////////////////////////////////// +// +// class Texture implementation. +// +///////////////////////////////////////////////////////////////////////////// + +// -------------------------------------------------------------------------- +// Texture::Texture +// +// Constructor. +// -------------------------------------------------------------------------- +Texture::Texture () + : _handle (0), _flags (kDefault), + _standardCoordSystem (true), _fail (true) { + // Inhibit possible previous OpenGL error + checkOpenGLErrors (__FILE__, __LINE__); +} + +// -------------------------------------------------------------------------- +// Texture::~Texture +// +// Destructor. Delete texture object. +// -------------------------------------------------------------------------- +Texture::~Texture () { + // Delete texture object + if (glIsTexture(_handle)) + glDeleteTextures (1, &_handle); +} + +// -------------------------------------------------------------------------- +// Texture::bind +// +// Bind texture to the active texture unit. +// -------------------------------------------------------------------------- +void Texture::bind () const { + glBindTexture (target (), _handle); +} + +void Texture::bind (GLenum texUnit) const { + glActiveTexture (texUnit); + glBindTexture (target (), _handle); +} + + +// -------------------------------------------------------------------------- +// Texture::getCompressionFormat +// +// Return the corresponding format for a compressed image given +// image's internal format (pixel components). +// -------------------------------------------------------------------------- +GLint Texture::getCompressionFormat (GLint internalFormat) { + if (!GLEW_EXT_texture_compression_s3tc || + !GLEW_ARB_texture_compression) + // No compression possible on this target machine + return internalFormat; + + switch (internalFormat) + { + case 1: + return GL_COMPRESSED_LUMINANCE; + + case 2: + return GL_COMPRESSED_LUMINANCE_ALPHA; + + case 3: + return GL_COMPRESSED_RGB; + + case 4: + return GL_COMPRESSED_RGBA; + + default: + // Error! + throw std::invalid_argument ("Texture::getCompressionFormat: " + "Bad internal format"); + } +} + + +// -------------------------------------------------------------------------- +// Texture::getInternalFormat +// +// Return texture's internal format depending to whether compression +// is used or not. +// -------------------------------------------------------------------------- +GLint Texture::getInternalFormat (GLint components) { + if (_flags & kCompress) + return getCompressionFormat (components); + else + return components; +} + +///////////////////////////////////////////////////////////////////////////// +// +// class Texture2D implementation. +// +///////////////////////////////////////////////////////////////////////////// + +// -------------------------------------------------------------------------- +// Texture2D::Texture2D +// +// Constructors. Try to load a texture 2D. Don't throw any exception +// if it fails to create the texture; the program can still run whithout +// textures. +// -------------------------------------------------------------------------- +Texture2D::Texture2D () : Texture () { +} + +Texture2D::Texture2D (const string &filename, TextureFlags flags) { + try + { + // Load image file into a buffer + ImageBuffer ibuff (filename); + auto_ptr img (ImageFactory::createImage (ibuff)); + + // Create texture from image buffer + create (img.get (), flags); + } + catch (std::exception &err) + { + cerr << "Error: Couldn't create texture 2D from " << filename + << endl << "Reason: " << err.what () << endl; + } +} + + +Texture2D::Texture2D (const Image *img, TextureFlags flags) +{ + try + { + // Create texture from image buffer + create (img, flags); + } + catch (std::exception &err) + { + cerr << "Error: Couldn't create texture 2D from " << img->name () + << endl << "Reason: " << err.what () << endl; + } +} + + +// -------------------------------------------------------------------------- +// Texture2D::create +// +// Create a texture 2D from an image. +// -------------------------------------------------------------------------- + +void +Texture2D::create (const Image *img, TextureFlags flags) +{ + if (!img->pixels ()) + throw std::runtime_error ("Invalid image data"); + + // Fill texture's vars + _name = img->name (); + _flags = flags; + _standardCoordSystem = img->stdCoordSystem (); + + // Generate a texture name + glGenTextures (1, &_handle); + glBindTexture (target (), _handle); + + // Setup texture filters + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + if (img->isCompressed ()) + { + // Image use S3 compression. Only S3TC DXT1, DXT3 + // and DXT5 formats are supported. + GLsizei mipWidth = img->width (); + GLsizei mipHeight = img->height (); + int offset = 0; + int blockSize = (img->format () == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 8 : 16; + + // Upload mipmaps to video memory + for (GLint mipLevel = 0; mipLevel < img->numMipmaps (); ++mipLevel) + { + GLsizei mipSize = ((mipWidth + 3) / 4) * ((mipHeight + 3) / 4) * blockSize; + + glCompressedTexImage2D (GL_TEXTURE_2D, mipLevel, img->format (), + mipWidth, mipHeight, 0, mipSize, + img->pixels () + offset); + + mipWidth = std::max (mipWidth >> 1, 1); + mipHeight = std::max (mipHeight >> 1, 1); + + offset += mipSize; + } + } + else + { + // Build the texture and generate mipmaps + if (GLEW_SGIS_generate_mipmap && img->isPowerOfTwo ()) + { + // Hardware mipmap generation + glTexParameteri (GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE); + glHint (GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST); + + glTexImage2D (GL_TEXTURE_2D, 0, getInternalFormat (img->components ()), + img->width (), img->height (), 0, img->format (), + GL_UNSIGNED_BYTE, img->pixels ()); + } + else + { + // No hardware mipmap generation support, fall back to the + // good old gluBuild2DMipmaps function + gluBuild2DMipmaps (GL_TEXTURE_2D, getInternalFormat (img->components ()), + img->width (), img->height (), img->format (), + GL_UNSIGNED_BYTE, img->pixels ()); + } + } + + // Does texture creation succeeded? + if (GL_NO_ERROR == checkOpenGLErrors (__FILE__, __LINE__)) + _fail = false; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// class TextureRectangle implementation. +// +///////////////////////////////////////////////////////////////////////////// + +// -------------------------------------------------------------------------- +// TextureRectangle::TextureRectangle +// +// Constructors. Try to load a texture rectangle. Don't throw any +// exception if it fails to create the texture; the program can still +// run whithout textures. +// -------------------------------------------------------------------------- + +TextureRectangle::TextureRectangle (const string &filename, TextureFlags flags) + : _width (0), _height (0) +{ + try + { + // Check if is not a DDS image + if (filename.find (".dds") != string::npos) + throw ImageException ("Compressed textures are not supported for" + " texture rectangles!", filename); + + // Load image file into a buffer + ImageBuffer ibuff (filename); + auto_ptr img (ImageFactory::createImage (ibuff)); + + // Create texture from image buffer + create (img.get (), flags); + } + catch (std::exception &err) + { + cerr << "Error: Couldn't create texture rectangle from " + << filename << endl << "Reason: " << err.what () << endl; + } +} + + +TextureRectangle::TextureRectangle (const Image *img, TextureFlags flags) + : _width (0), _height (0) +{ + try + { + // Check if is not a compressed DDS image + if (img->isCompressed ()) + throw ImageException ("Compressed textures are not supported for" + " texture rectangles!", img->name ()); + + // Create texture from image buffer + create (img, flags); + } + catch (std::exception &err) + { + cerr << "Error: Couldn't create texture rectangle from " + << img->name () << endl << "Reason: " << err.what () << endl; + } +} + + +// -------------------------------------------------------------------------- +// TextureRectangle::create +// +// Create a texture rectangle from an image. No mipmap +// filtering is permitted for texture rectangles. +// -------------------------------------------------------------------------- + +void +TextureRectangle::create (const Image *img, TextureFlags flags) +{ + if (!img->pixels ()) + throw std::runtime_error ("Invalid image data"); + + // Get image info + _standardCoordSystem = img->stdCoordSystem (); + _width = img->width (); + _height = img->height (); + _name = img->name (); + _flags = flags; + + // Generate a texture name + glGenTextures (1, &_handle); + glBindTexture (target (), _handle); + + // Setup texture filters + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + // Create texture from image + glTexImage2D (GL_TEXTURE_RECTANGLE_ARB, 0, + getInternalFormat (img->components ()), + img->width (), img->height (), 0, img->format (), + GL_UNSIGNED_BYTE, img->pixels ()); + + // Does texture creation succeeded? + if (GL_NO_ERROR == checkOpenGLErrors (__FILE__, __LINE__)) + _fail = false; +} + + +///////////////////////////////////////////////////////////////////////////// +// +// class TextureCubeMap implementation. +// +///////////////////////////////////////////////////////////////////////////// + +// -------------------------------------------------------------------------- +// TextureCubeMap::TextureCubeMap +// +// Constructors. Try to load a texture cube map. Don't throw any +// exception if it fails to create the texture; the program can still +// run whithout textures. +// -------------------------------------------------------------------------- + +TextureCubeMap::TextureCubeMap (const string &basename, const vector &files, + TextureFlags flags) +{ + try + { + _name = basename; + + vector faces; + faces.reserve(6); + + // Load images + for (int i = 0; i < 6; ++i) + { + ImageBuffer ibuff (files[i]); + faces.push_back (ImagePtr (ImageFactory::createImage (ibuff))); + } + + // Create texture from faces + create (faces, flags); + } + catch (ImageException &err) + { + cerr << "Error: Couldn't create texture cube map from " << basename << endl; + cerr << "Reason: " << err.what () << " (" << err.which () << ")" << endl; + } + catch (std::exception &err) + { + cerr << "Error: Couldn't create texture cube map from " << basename << endl; + cerr << "Reason: " << err.what () << endl; + } +} + + +TextureCubeMap::TextureCubeMap (const string &basename, const vector &faces, + TextureFlags flags) +{ + try + { + _name = basename; + + // Create texture from image buffer + create (faces, flags); + } + catch (ImageException &err) + { + cerr << "Error: Couldn't create texture cube map from " << basename << endl; + cerr << "Reason: " << err.what () << " (" << err.which () << ")" << endl; + } + catch (std::exception &err) + { + cerr << "Error: Couldn't create texture cube map from " << basename << endl; + cerr << "Reason: " << err.what () << endl; + } +} + + +// -------------------------------------------------------------------------- +// TextureCubeMap::create +// +// Create a cube map texture from images. +// -------------------------------------------------------------------------- + +void +TextureCubeMap::create (const vector &faces, TextureFlags flags) +{ + // Create a list of image buffers and associate a target + // for each one + typedef map TexTarget; + typedef TexTarget::value_type TexPair; + TexTarget texImages; + + texImages.insert (TexPair (faces[0].get(), GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB)); + texImages.insert (TexPair (faces[1].get(), GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB)); + texImages.insert (TexPair (faces[2].get(), GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB)); + texImages.insert (TexPair (faces[3].get(), GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB)); + texImages.insert (TexPair (faces[4].get(), GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB)); + texImages.insert (TexPair (faces[5].get(), GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB)); + + _standardCoordSystem = faces[0]->stdCoordSystem (); + _flags = flags; + + // Generate a texture name + glGenTextures (1, &_handle); + glBindTexture (GL_TEXTURE_CUBE_MAP_ARB, _handle); + + // Setup texture filters + glTexParameteri (GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri (GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri (GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + + // Load each side of the cube + for (TexTarget::iterator itor = texImages.begin (); + itor != texImages.end (); ++itor) + { + // Get image data + const Image *img = itor->first; + + if (!img->pixels ()) + throw std::runtime_error ("Invalid image data"); + + if (img->isCompressed ()) + { + // Image use S3 compression + GLsizei mipWidth = img->width (); + GLsizei mipHeight = img->height (); + int offset = 0; + int blockSize = + (img->format () == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 8 : 16; + + // Upload mipmaps to video memory + for (GLint mipLevel = 0; mipLevel < img->numMipmaps (); ++mipLevel) + { + GLsizei mipSize = ((mipWidth + 3) / 4) * + ((mipHeight + 3) / 4) * blockSize; + + glCompressedTexImage2D (itor->second, mipLevel, img->format (), + mipWidth, mipHeight, 0, mipSize, + img->pixels () + offset); + + mipWidth = std::max (mipWidth >> 1, 1); + mipHeight = std::max (mipHeight >> 1, 1); + + offset += mipSize; + } + } + else + { + // No hardware mipmap generation support for texture cube + // maps, use gluBuild2DMipmaps function instead + gluBuild2DMipmaps (itor->second, getInternalFormat (img->components ()), + img->width (), img->height (), img->format (), + GL_UNSIGNED_BYTE, img->pixels ()); + } + } + + // Does texture creation succeeded? + if (GL_NO_ERROR == checkOpenGLErrors (__FILE__, __LINE__)) + _fail = false; +} + +}}} //end namespace diff --git a/source/shared_lib/sources/graphics/md5/md5util.cpp b/source/shared_lib/sources/graphics/md5/md5util.cpp new file mode 100644 index 00000000..0c0d2149 --- /dev/null +++ b/source/shared_lib/sources/graphics/md5/md5util.cpp @@ -0,0 +1,569 @@ +// ============================================================== +// This file is part of MegaGlest (www.glest.org) +// +// Copyright (C) 2011- by Mark Vejvoda +// +// You can redistribute this code 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 +// ============================================================== + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "GlErrors.h" +#include "Mathlib.h" +#include "Md5Model.h" +#include "TextureManager.h" +#include "ArbProgram.h" +#include "Shader.h" +#include "md5util.h" + +namespace Shared { namespace Graphics { namespace md5 { + + +using std::cout; +using std::cerr; +using std::endl; +using std::string; +using std::vector; + +// All vertex and fragment programs +ArbVertexProgram *vp_bump = NULL; +ArbVertexProgram *vp_bump_parallax = NULL; + +ArbFragmentProgram *fp_diffuse = NULL; +ArbFragmentProgram *fp_diffuse_specular = NULL; +ArbFragmentProgram *fp_ds_parallax = NULL; + +// Tangent uniform's location +GLint tangentLoc = -1; + +int renderFlags = Md5Object::kDrawModel; + +bool bAnimate = true; +bool bTextured = true; +bool bCullFace = true; +bool bBounds = false; +bool bParallax = false; +bool bLight = true; +bool bSmooth = true; +bool bWireframe = false; +bool bDrawNormals = false; + +vector animations; + +// Camera +Vector3f rot, eye; + +// ------------------------------------------------------------------------- +// cleanupMD5OpenGL +// +// Application cleanup. +// ------------------------------------------------------------------------- +void cleanupMD5OpenGL() { + //delete model; + //delete object; + //delete font; + //delete shader; + + delete Md5Model::shader; + Md5Model::shader=NULL; + delete vp_bump; + delete vp_bump_parallax; + delete fp_diffuse; + delete fp_diffuse_specular; + delete fp_ds_parallax; + + Texture2DManager::kill (); +} + +// ------------------------------------------------------------------------- +// initShader +// +// Shader's uniform variables initialization. +// ------------------------------------------------------------------------- +void initShader () { + if (NULL == Md5Model::shader) + return; + + Md5Model::shader->use(); + + if (GLEW_VERSION_2_0) { + GLuint prog = Md5Model::shader->handle (); + + // Set uniform parameters + glUniform1i (glGetUniformLocation (prog, "decalMap"), 0); + glUniform1i (glGetUniformLocation (prog, "glossMap"), 1); + glUniform1i (glGetUniformLocation (prog, "normalMap"), 2); + glUniform1i (glGetUniformLocation (prog, "heightMap"), 3); + glUniform1i (glGetUniformLocation (prog, "parallaxMapping"), bParallax); + + // Get attribute location + Md5Model::tangentLoc = glGetAttribLocation (prog, "tangent"); + } + else { + GLhandleARB prog = Md5Model::shader->handle(); + + // Set uniform parameters + glUniform1iARB (glGetUniformLocationARB (prog, "decalMap"), 0); + glUniform1iARB (glGetUniformLocationARB (prog, "glossMap"), 1); + glUniform1iARB (glGetUniformLocationARB (prog, "normalMap"), 2); + glUniform1iARB (glGetUniformLocationARB (prog, "heightMap"), 3); + glUniform1iARB (glGetUniformLocationARB (prog, "parallaxMapping"), bParallax); + + // Get attribute location + Md5Model::tangentLoc = glGetAttribLocationARB (prog, "tangent"); + } + + Md5Model::shader->unuse(); + + // Warn ff we fail to get tangent location... We'll can still use + // the shader, but without tangents + if(Md5Model::tangentLoc == -1) + cerr << "Warning! No \"tangent\" uniform found in shader!" << endl; +} + +// ------------------------------------------------------------------------- +// announceRenderPath +// +// Print info about a render path. +// ------------------------------------------------------------------------- +void announceRenderPath (render_path_e path) { + cout << "Render path: "; + switch (path) + { + case R_normal: + cout << "no bump mapping (fixed pipeline)" << endl; + break; + + case R_ARBfp_diffuse: + cout << "bump mapping, diffuse only " + << "(ARB vp & fp)" << endl; + break; + + case R_ARBfp_diffuse_specular: + cout << "bump mapping, diffuse and specular " + << "(ARB vp & fp)" << endl; + break; + + case R_ARBfp_ds_parallax: + cout << "bump mapping with parallax " + << "(ARB fp & fp)" << endl; + break; + + case R_shader: + cout << "bump mapping with parallax " + << "(GLSL)" << endl; + break; + } +} + +// ------------------------------------------------------------------------- +// initMD5OpenGL +// +// OpenGL initialization. +// ------------------------------------------------------------------------- +void initMD5OpenGL(string shaderPath) { + //glClearColor (0.5f, 0.5f, 0.5f, 0.0f); + //glShadeModel (GL_SMOOTH); + //glCullFace (GL_BACK); + //glEnable (GL_DEPTH_TEST); + + // Initialize GLEW + GLenum err = glewInit (); + if(GLEW_OK != err) { + // Problem: glewInit failed, something is seriously wrong. + cerr << "Error: " << glewGetErrorString (err) << endl; + cleanupMD5OpenGL(); + } + + // Print some infos about user's OpenGL implementation + cout << "OpenGL Version String: " << glGetString (GL_VERSION) << endl; + cout << "GLU Version String: " << gluGetString (GLU_VERSION) << endl; + cout << "GLEW Version String: " << glewGetString (GLEW_VERSION) << endl; + + // Initialize ARB vertex/fragment program support + initArbProgramHandling(); + + // Initialize GLSL shader support + initShaderHandling(); + + if(hasArbVertexProgramSupport () && + hasArbFragmentProgramSupport ()) { + // Load ARB programs + vp_bump = new ArbVertexProgram(shaderPath + "bump.vp"); + vp_bump_parallax = new ArbVertexProgram(shaderPath + "bumpparallax.vp"); + + fp_diffuse = new ArbFragmentProgram(shaderPath + "bumpd.fp"); + fp_diffuse_specular = new ArbFragmentProgram(shaderPath + "bumpds.fp"); + fp_ds_parallax = new ArbFragmentProgram(shaderPath + "bumpdsp.fp"); + + // Current ARB programs will be bump mapping with diffuse + // and specular components + Md5Model::vp = vp_bump; + Md5Model::fp = fp_diffuse_specular; + } + + if(hasShaderSupport ()) { + // Load shader + VertexShader vs(shaderPath + "bump.vert"); + FragmentShader fs(shaderPath + "bump.frag"); + Md5Model::shader = new ShaderProgram(shaderPath + "bump mapping", vs, fs); + + // Initialize shader's uniforms + initShader(); + } + + // Announce avalaible render paths, select the best + cout << endl << "Available render paths:" << endl; + + cout << " [F3] - No bump mapping (fixed pipeline)" << endl; + Md5Model::renderPath = R_normal; + + if (vp_bump && fp_diffuse) { + cout << " [F4] - Bump mapping, diffuse only " + << "(ARB vp & fp)" << endl; + Md5Model::renderPath = R_ARBfp_diffuse; + } + + if (vp_bump && fp_diffuse_specular) { + cout << " [F5] - Bump mapping, diffuse and specular " + << "(ARB vp & fp)" << endl; + Md5Model::renderPath = R_ARBfp_diffuse_specular; + } + + if (vp_bump_parallax && fp_ds_parallax) { + cout << " [F6] - Bump mapping with parallax " + << "(ARB vp & fp)" << endl; + } + + if (Md5Model::shader) { + cout << " [F7] - Bump mapping with parallax " + << "(GLSL)" << endl; + Md5Model::renderPath = R_shader; + } + + // Announce which path has been chosen by default + cout << endl; + announceRenderPath(Md5Model::renderPath); + + checkOpenGLErrors (__FILE__, __LINE__); +} + +// ------------------------------------------------------------------------- +// extractFromQuotes +// +// Extract a string from quotes. +// ------------------------------------------------------------------------- +inline const string extractFromQuotes (const string &str) { + string::size_type start = str.find_first_of ('\"') + 1; + string::size_type end = str.find_first_of ('\"', start) - 2; + return str.substr (start, end); +} + +// ------------------------------------------------------------------------- +// getMD5ObjectFromLoaderScript +// +// Parse a script file for loading md5mesh and animations. +// ------------------------------------------------------------------------- +Md5Object * getMD5ObjectFromLoaderScript(const string &filename) { + // Open the file to parse + std::ifstream file (filename.c_str(), std::ios::in); + + if (file.fail ()) { + cerr << "Couldn't open " << filename << endl; + cleanupMD5OpenGL(); + } + + // Get texture manager + Texture2DManager *texMgr = Texture2DManager::getInstance(); + + Md5Model *model = NULL; + Md5Object *object = NULL; + + while (!file.eof ()) { + string token, buffer; + string meshFile, animFile, textureFile; + string meshName, animName; + + // Peek next token + file >> token; + + if (token == "model") { + std::getline (file, buffer); + meshFile = extractFromQuotes (buffer); + + // Delete previous model and object if existing + delete model; + delete object; + + // Load mesh model + model = new Md5Model(meshFile); + object = new Md5Object(model); + } + else if (token == "anim") { + std::getline (file, buffer); + animFile = extractFromQuotes (buffer); + + try { + // Load animation + if (model) { + model->addAnim(animFile); + } + } + catch (Md5Exception &err) { + cerr << "Failed to load animation " + << animFile << endl; + cerr << "Reason: " << err.what () + << " (" << err.which () << ")" << endl; + } + } + else if (token == "hide") { + std::getline (file, buffer); + meshName = extractFromQuotes (buffer); + + // Set mesh's render state + if (model) { + model->setMeshRenderState (meshName, Md5Mesh::kHide); + } + } + else if ((token == "decalMap") || + (token == "specularMap") || + (token == "normalMap") || + (token == "heightMap")) { + // Get the next token and extract the mesh name + file >> buffer; + long start = buffer.find_first_of ('\"') + 1; + long end = buffer.find_first_of ('\"', start) - 1; + meshName = buffer.substr (start, end); + + // Get the rest of line and extract texture's filename + std::getline (file, buffer); + textureFile = extractFromQuotes (buffer); + + // If the model has been loaded, setup + // the texture to the desired mesh + if (model) { + Texture2D *tex = texMgr->load (textureFile); + if (tex->fail ()) + cerr << "failed to load " << textureFile << endl; + + if (token == "decalMap") + model->setMeshDecalMap (meshName, tex); + else if (token == "specularMap") + model->setMeshSpecularMap (meshName, tex); + else if (token == "normalMap") + model->setMeshNormalMap (meshName, tex); + else if (token == "heightMap") + model->setMeshHeightMap (meshName, tex); + } + } + else if (token == "setAnim") { + std::getline (file, buffer); + animName = extractFromQuotes (buffer); + + // Set object's default animation + object->setAnim (animName); + } + } + + file.close (); + + if (!model || !object) + throw Md5Exception ("No mesh found!", filename); + + return object; +} + +// ------------------------------------------------------------------------- +// setupLight +// +// Setup light position and enable light0. +// ------------------------------------------------------------------------- +void setupLight(GLfloat x, GLfloat y, GLfloat z) { + GLfloat lightPos[4]; + lightPos[0] = x; + lightPos[1] = y; + lightPos[2] = z; + lightPos[3] = 1.0f; + + glDisable (GL_LIGHTING); + glDisable (GL_LIGHT0); + + if (bLight) + { + glPushMatrix (); + glLoadIdentity (); + glLightfv (GL_LIGHT0, GL_POSITION, lightPos); + glPopMatrix (); + + glEnable (GL_LIGHTING); + glEnable (GL_LIGHT0); + } +} + + +// ------------------------------------------------------------------------- +// drawObb +// +// Draw an Oriented Bouding Box. +// ------------------------------------------------------------------------- +void drawObb(const OBBox_t &obb) { + Vector3f corners[8]; + + corners[0] = Vector3f (-obb.extent._x, -obb.extent._y, -obb.extent._z); + corners[1] = Vector3f ( obb.extent._x, -obb.extent._y, -obb.extent._z); + corners[2] = Vector3f ( obb.extent._x, obb.extent._y, -obb.extent._z); + corners[3] = Vector3f (-obb.extent._x, obb.extent._y, -obb.extent._z); + corners[4] = Vector3f (-obb.extent._x, -obb.extent._y, obb.extent._z); + corners[5] = Vector3f ( obb.extent._x, -obb.extent._y, obb.extent._z); + corners[6] = Vector3f ( obb.extent._x, obb.extent._y, obb.extent._z); + corners[7] = Vector3f (-obb.extent._x, obb.extent._y, obb.extent._z); + + glPushAttrib (GL_ENABLE_BIT); + glDisable (GL_TEXTURE_2D); + glDisable (GL_LIGHTING); + + for (int i = 0; i < 8; ++i) { + corners[i] += obb.center; + obb.world.transform (corners[i]); + } + + GLuint indices[24] = + { + 0, 1, 1, 2, 2, 3, 3, 0, + 4, 5, 5, 6, 6, 7, 7, 4, + 0, 4, 1, 5, 2, 6, 3, 7 + }; + + glColor3f (1.0, 0.0, 0.0); + + glEnableClientState (GL_VERTEX_ARRAY); + glVertexPointer (3, GL_FLOAT, 0, corners); + glDrawElements (GL_LINES, 24, GL_UNSIGNED_INT, indices); + glDisableClientState (GL_VERTEX_ARRAY); + + // GL_ENABLE_BIT + glPopAttrib(); +} + +// ------------------------------------------------------------------------- +// drawAxes +// +// Draw the X, Y and Z axes at the center of world. +// ------------------------------------------------------------------------- +void drawAxes(const Matrix4x4f &modelView) { + // Setup world model view matrix + glLoadIdentity (); + glMultMatrixf (modelView._m); + + // Draw the three axes + glBegin (GL_LINES); + // X-axis in red + glColor3f (1.0f, 0.0f, 0.0f); + glVertex3fv (kZeroVectorf._v); + glVertex3fv (kZeroVectorf + Vector3f (10.0f, 0.0f, 0.0)); + + // Y-axis in green + glColor3f (0.0f, 1.0f, 0.0f); + glVertex3fv (kZeroVectorf._v); + glVertex3fv (kZeroVectorf + Vector3f (0.0f, 10.0f, 0.0)); + + // Z-axis in blue + glColor3f (0.0f, 0.0f, 1.0f); + glVertex3fv (kZeroVectorf._v); + glVertex3fv (kZeroVectorf + Vector3f (0.0f, 0.0f, 10.0)); + glEnd (); +} + +// ------------------------------------------------------------------------- +// renderMD5Object +// +// Render the 3D part of the scene. +// ------------------------------------------------------------------------- +void renderMD5Object(Md5Object *object, double anim, Matrix4x4f *modelViewMatrix) { + if(!object) { + return; + } + + // Clear the window + //glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glMatrixMode (GL_MODELVIEW); + glLoadIdentity (); + + // Camera rotation + Matrix4x4f camera; + +#if 0 + camera.identity (); + + glTranslated (-eye._x, -eye._y, -eye._z); + glRotated (rot._x, 1.0f, 0.0f, 0.0f); + glRotated (rot._y, 0.0f, 1.0f, 0.0f); + glRotated (rot._z, 0.0f, 0.0f, 1.0f); +#else + camera.fromEulerAngles (degToRad (rot._x), + degToRad (rot._y), + degToRad (rot._z)); + camera.setTranslation (-eye); +#endif + + Matrix4x4f axisRotation + = RotationMatrix (kXaxis, -kPiOver2) + * RotationMatrix (kZaxis, -kPiOver2); + + Matrix4x4f final = camera * axisRotation; + //glMultMatrixf (final._m); + if(modelViewMatrix) { + final = *modelViewMatrix; + } + + // Setup scene lighting + setupLight (0.0f, 20.0f, 100.0f); + + // Enable/disable texture mapping (fixed pipeline) + if (bTextured) + glEnable (GL_TEXTURE_2D); + else + glDisable (GL_TEXTURE_2D); + + // Enable/disable backface culling + if (bCullFace) + glEnable (GL_CULL_FACE); + else + glDisable (GL_CULL_FACE); + + // Setup polygon mode and shade model + glPolygonMode (GL_FRONT_AND_BACK, bWireframe ? GL_LINE : GL_FILL); + glShadeModel (bSmooth ? GL_SMOOTH : GL_FLAT); + + // Draw object + object->setModelViewMatrix (final); + object->setRenderFlags (renderFlags); + //object->animate (bAnimate ? timer.deltaTime () : 0.0f); + object->animate (anim); + object->computeBoundingBox (); + object->prepare (false); + object->render (); + + if (bBounds) + drawObb (object->boundingBox ()); + + glDisable (GL_LIGHTING); + glDisable (GL_TEXTURE_2D); + + drawAxes (final); +} + +}}} //end namespace diff --git a/source/shared_lib/sources/platform/miniupnpc/LICENCE b/source/shared_lib/sources/platform/miniupnpc/LICENCE deleted file mode 100644 index a8cfb5ef..00000000 --- a/source/shared_lib/sources/platform/miniupnpc/LICENCE +++ /dev/null @@ -1,26 +0,0 @@ -Copyright (c) 2005-2008, Thomas BERNARD -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -