mirror of
https://github.com/glest/glest-source.git
synced 2025-09-25 23:19:07 +02:00
537 lines
16 KiB
C++
537 lines
16 KiB
C++
// ==============================================================
|
|
// 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 licensed under the MIT license:
|
|
// http://www.opensource.org/licenses/mit-license.php
|
|
//
|
|
// Open Source Initiative OSI - The MIT License (MIT):Licensing
|
|
//
|
|
// The MIT License (MIT)
|
|
// Copyright (c) <year> <copyright holders>
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and associated documentation files (the "Software"),
|
|
// to deal in the Software without restriction, including without limitation
|
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
// and/or sell copies of the Software, and to permit persons to whom the
|
|
// Software is furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.//
|
|
//
|
|
// Implementation of an OpenGL texture classes.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifdef _WIN32
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
#endif // _WIN32
|
|
|
|
#include <GL/glew.h>
|
|
#include <iostream>
|
|
#include <stdexcept>
|
|
|
|
#include "GlErrors.h"
|
|
#include "md5Texture.h"
|
|
#include "Image.h"
|
|
|
|
namespace Shared { namespace Graphics { namespace md5 {
|
|
|
|
using std::cout;
|
|
using std::cerr;
|
|
using std::endl;
|
|
using std::max;
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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<Image> 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 = max (mipWidth >> 1, 1);
|
|
mipHeight = 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<Image> 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<string> &files,
|
|
TextureFlags flags)
|
|
{
|
|
try
|
|
{
|
|
_name = basename;
|
|
|
|
vector<ImagePtr> 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<ImagePtr> &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<ImagePtr> &faces, TextureFlags flags)
|
|
{
|
|
// Create a list of image buffers and associate a target
|
|
// for each one
|
|
typedef map<const Image*, GLenum> 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 = max (mipWidth >> 1, 1);
|
|
mipHeight = 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
|