Commit d5ac0f68 authored by Janis Born's avatar Janis Born
Browse files

add functions for loading 2D / 3D / cubemap textures from DDS files

parent 98e6a4d3
......@@ -21,7 +21,13 @@ namespace ACGL{
namespace OpenGL{
//! loads the texture and creates mip maps
SharedTexture2D loadTexture2D( const std::string &_filename );
SharedTexture2D loadTexture2D(const std::string& _filename);
//! loads the texture including mipmaps from a DDS file
//! supports DXT1, DXT3 and DXT5 compression
SharedTexture2D loadTexture2DFromDDS (const std::string& _filename);
SharedTexture3D loadTexture3DFromDDS (const std::string& _filename);
SharedTextureCubeMap loadTextureCubeMapFromDDS(const std::string& _filename);
}
}
#ifndef __NV_DDS_H__
#define __NV_DDS_H__
#ifndef NVDDS_PROJECT //defined if building nv_dds library
#if _MSC_VER >= 1300
#ifdef _DLL
#pragma message("Note: including lib: nv_dds.lib\n")
#pragma comment(lib, "nv_dds.lib")
#else
#error "Your project doesn't use the Multithreaded DLL Runtime"
#endif
#endif
#endif
#if defined(WIN32)
# include <windows.h>
#endif
#include <string>
#include <deque>
#include <assert.h>
#if defined(MACOS)
#include <OpenGL/gl.h>
#include <OpenGL/glext.h>
#else
#include <GL/gl.h>
#include <GL/glext.h>
#endif
namespace nv_dds
{
// surface description flags
const uint32_t DDSF_CAPS = 0x00000001l;
const uint32_t DDSF_HEIGHT = 0x00000002l;
const uint32_t DDSF_WIDTH = 0x00000004l;
const uint32_t DDSF_PITCH = 0x00000008l;
const uint32_t DDSF_PIXELFORMAT = 0x00001000l;
const uint32_t DDSF_MIPMAPCOUNT = 0x00020000l;
const uint32_t DDSF_LINEARSIZE = 0x00080000l;
const uint32_t DDSF_DEPTH = 0x00800000l;
// pixel format flags
const uint32_t DDSF_ALPHAPIXELS = 0x00000001l;
const uint32_t DDSF_FOURCC = 0x00000004l;
const uint32_t DDSF_RGB = 0x00000040l;
const uint32_t DDSF_RGBA = 0x00000041l;
// dwCaps1 flags
const uint32_t DDSF_COMPLEX = 0x00000008l;
const uint32_t DDSF_TEXTURE = 0x00001000l;
const uint32_t DDSF_MIPMAP = 0x00400000l;
// dwCaps2 flags
const uint32_t DDSF_CUBEMAP = 0x00000200l;
const uint32_t DDSF_CUBEMAP_POSITIVEX = 0x00000400l;
const uint32_t DDSF_CUBEMAP_NEGATIVEX = 0x00000800l;
const uint32_t DDSF_CUBEMAP_POSITIVEY = 0x00001000l;
const uint32_t DDSF_CUBEMAP_NEGATIVEY = 0x00002000l;
const uint32_t DDSF_CUBEMAP_POSITIVEZ = 0x00004000l;
const uint32_t DDSF_CUBEMAP_NEGATIVEZ = 0x00008000l;
const uint32_t DDSF_CUBEMAP_ALL_FACES = 0x0000FC00l;
const uint32_t DDSF_VOLUME = 0x00200000l;
// compressed texture types
const uint32_t FOURCC_DXT1 = 0x31545844l; //(MAKEFOURCC('D','X','T','1'))
const uint32_t FOURCC_DXT3 = 0x33545844l; //(MAKEFOURCC('D','X','T','3'))
const uint32_t FOURCC_DXT5 = 0x35545844l; //(MAKEFOURCC('D','X','T','5'))
struct DXTColBlock
{
uint16_t col0;
uint16_t col1;
uint8_t row[4];
};
struct DXT3AlphaBlock
{
uint16_t row[4];
};
struct DXT5AlphaBlock
{
uint8_t alpha0;
uint8_t alpha1;
uint8_t row[6];
};
struct DDS_PIXELFORMAT
{
uint32_t dwSize;
uint32_t dwFlags;
uint32_t dwFourCC;
uint32_t dwRGBBitCount;
uint32_t dwRBitMask;
uint32_t dwGBitMask;
uint32_t dwBBitMask;
uint32_t dwABitMask;
};
struct DDS_HEADER
{
uint32_t dwSize;
uint32_t dwFlags;
uint32_t dwHeight;
uint32_t dwWidth;
uint32_t dwPitchOrLinearSize;
uint32_t dwDepth;
uint32_t dwMipMapCount;
uint32_t dwReserved1[11];
DDS_PIXELFORMAT ddspf;
uint32_t dwCaps1;
uint32_t dwCaps2;
uint32_t dwReserved2[3];
};
enum TextureType
{
TextureNone,
TextureFlat, // 1D, 2D, and rectangle textures
Texture3D,
TextureCubemap
};
class CSurface
{
public:
CSurface();
CSurface(unsigned int w, unsigned int h, unsigned int d, unsigned int imgsize, const unsigned char *pixels);
CSurface(const CSurface &copy);
CSurface &operator= (const CSurface &rhs);
virtual ~CSurface();
operator unsigned char*() const;
virtual void create(unsigned int w, unsigned int h, unsigned int d, unsigned int imgsize, const unsigned char *pixels);
virtual void clear();
inline unsigned int get_width() const { return m_width; }
inline unsigned int get_height() const { return m_height; }
inline unsigned int get_depth() const { return m_depth; }
inline unsigned int get_size() const { return m_size; }
private:
unsigned int m_width;
unsigned int m_height;
unsigned int m_depth;
unsigned int m_size;
unsigned char *m_pixels;
};
class CTexture : public CSurface
{
friend class CDDSImage;
public:
CTexture();
CTexture(unsigned int w, unsigned int h, unsigned int d, unsigned int imgsize, const unsigned char *pixels);
CTexture(const CTexture &copy);
CTexture &operator= (const CTexture &rhs);
~CTexture();
void create(unsigned int w, unsigned int h, unsigned int d, unsigned int imgsize, const unsigned char *pixels);
void clear();
inline const CSurface &get_mipmap(unsigned int index) const
{
assert(!m_mipmaps.empty());
assert(index < m_mipmaps.size());
return m_mipmaps[index];
}
inline void add_mipmap(const CSurface &mipmap)
{
m_mipmaps.push_back(mipmap);
}
inline unsigned int get_num_mipmaps() const { return (unsigned int)m_mipmaps.size(); }
protected:
inline CSurface &get_mipmap(unsigned int index)
{
assert(!m_mipmaps.empty());
assert(index < m_mipmaps.size());
return m_mipmaps[index];
}
private:
std::deque<CSurface> m_mipmaps;
};
class CDDSImage
{
public:
CDDSImage();
~CDDSImage();
void create_textureFlat(unsigned int format, unsigned int components, const CTexture &baseImage);
void create_texture3D(unsigned int format, unsigned int components, const CTexture &baseImage);
void create_textureCubemap(unsigned int format, unsigned int components,
const CTexture &positiveX, const CTexture &negativeX,
const CTexture &positiveY, const CTexture &negativeY,
const CTexture &positiveZ, const CTexture &negativeZ);
void clear();
bool load(std::string filename, bool flipImage = true);
bool save(std::string filename, bool flipImage = true);
/*
bool upload_texture1D();
bool upload_texture2D(unsigned int imageIndex = 0, GLenum target = GL_TEXTURE_2D);
bool upload_texture3D();
bool upload_textureRectangle();
bool upload_textureCubemap();
*/
inline operator unsigned char*()
{
assert(m_valid);
assert(!m_images.empty());
return m_images[0];
}
inline unsigned int get_width()
{
assert(m_valid);
assert(!m_images.empty());
return m_images[0].get_width();
}
inline unsigned int get_height()
{
assert(m_valid);
assert(!m_images.empty());
return m_images[0].get_height();
}
inline unsigned int get_depth()
{
assert(m_valid);
assert(!m_images.empty());
return m_images[0].get_depth();
}
inline unsigned int get_size()
{
assert(m_valid);
assert(!m_images.empty());
return m_images[0].get_size();
}
inline unsigned int get_num_mipmaps()
{
assert(m_valid);
assert(!m_images.empty());
return m_images[0].get_num_mipmaps();
}
inline const CSurface &get_mipmap(unsigned int index) const
{
assert(m_valid);
assert(!m_images.empty());
assert(index < m_images[0].get_num_mipmaps());
return m_images[0].get_mipmap(index);
}
inline const CTexture &get_cubemap_face(unsigned int face) const
{
assert(m_valid);
assert(!m_images.empty());
assert(m_images.size() == 6);
assert(m_type == TextureCubemap);
assert(face < 6);
return m_images[face];
}
inline unsigned int get_components() { return m_components; }
inline unsigned int get_format() { return m_format; }
inline TextureType get_type() { return m_type; }
inline bool is_compressed()
{
if ((m_format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ||
(m_format == GL_COMPRESSED_RGBA_S3TC_DXT3_EXT) ||
(m_format == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT))
return true;
else
return false;
}
inline bool is_cubemap() { return (m_type == TextureCubemap); }
inline bool is_volume() { return (m_type == Texture3D); }
inline bool is_valid() { return m_valid; }
inline bool is_dword_aligned()
{
assert(m_valid);
int dwordLineSize = get_dword_aligned_linesize(get_width(), m_components*8);
int curLineSize = get_width() * m_components;
return (dwordLineSize == curLineSize);
}
private:
unsigned int clamp_size(unsigned int size);
unsigned int size_dxtc(unsigned int width, unsigned int height);
unsigned int size_rgb(unsigned int width, unsigned int height);
inline void swap_endian(void *val);
// calculates 4-byte aligned width of image
inline unsigned int get_dword_aligned_linesize(unsigned int width, unsigned int bpp)
{
return ((width * bpp + 31) & -32) >> 3;
}
void flip(CSurface &surface);
void flip_texture(CTexture &texture);
void swap(void *byte1, void *byte2, unsigned int size);
void flip_blocks_dxtc1(DXTColBlock *line, unsigned int numBlocks);
void flip_blocks_dxtc3(DXTColBlock *line, unsigned int numBlocks);
void flip_blocks_dxtc5(DXTColBlock *line, unsigned int numBlocks);
void flip_dxt5_alpha(DXT5AlphaBlock *block);
void write_texture(const CTexture &texture, FILE *fp);
unsigned int m_format;
unsigned int m_components;
TextureType m_type;
bool m_valid;
std::deque<CTexture> m_images;
};
}
#endif
/***********************************************************************
* Copyright 2011-2012 Computer Graphics Group RWTH Aachen University. *
* All rights reserved. *
* Distributed under the terms of the MIT License (see LICENSE.TXT). *
**********************************************************************/
#include <ACGL/OpenGL/GL.hh>
#include <ACGL/OpenGL/Data/TextureLoadStore.hh>
#include <nv_dds/nv_dds.h>
#include <fstream>
using namespace ACGL;
using namespace ACGL::OpenGL;
using namespace ACGL::Utils;
using namespace nv_dds;
namespace {
GLenum getDDSInternalFormat(bool compressed, GLenum format)
{
GLenum internal_format = format;
// Internal format for uncompressed color formats
// For compressed textures, internal_format == image.get_format(), i.e. no conversion
if(format == GL_BGRA) internal_format = GL_RGBA;
if(format == GL_BGR) internal_format = GL_RGB;
if(format == GL_RED) internal_format = GL_RED;
return internal_format;
}
// To prevent code duplication, wrap glTexImage calls to take a compressed parameter
void texImage2D(bool compressed, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid* data)
{
if(compressed)
glCompressedTexImage2D(target, level, internalformat, width, height, 0, imageSize, data);
else
glTexImage2D(target, level, internalformat, width, height, 0, format, GL_UNSIGNED_BYTE, data);
}
void texImage3D(bool compressed, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid* data)
{
if(compressed)
glCompressedTexImage3D(target, level, internalformat, width, height, depth, 0, imageSize, data);
else
glTexImage3D(target, level, internalformat, width, height, depth, 0, format, GL_UNSIGNED_BYTE, data);
}
}
namespace ACGL {
namespace OpenGL {
SharedTexture2D loadTexture2DFromDDS(const std::string& _filename)
{
CDDSImage image;
SharedTexture2D texture;
if(image.load(_filename))
{
GLenum internal_format = getDDSInternalFormat(image.is_compressed(), image.get_format());
if(image.is_volume() || image.is_cubemap())
{
ACGL::Utils::error() << _filename << " is not a 2D texture" << std::endl;
}
else
{
// It is a 2D texture
texture = SharedTexture2D(new Texture2D(glm::uvec2(image.get_width(), image.get_height()), internal_format));
texture->bind();
texImage2D(image.is_compressed(), texture->getTarget(), 0, internal_format, image.get_width(), image.get_height(), image.get_format(), image.get_size(), image);
for(unsigned int i = 0; i < image.get_num_mipmaps(); i++)
{
const CSurface& mipmap = image.get_mipmap(i);
texImage2D(image.is_compressed(), texture->getTarget(), i+1, internal_format, mipmap.get_width(), mipmap.get_height(), image.get_format(), mipmap.get_size(), mipmap);
}
texture->setMaxLevel(image.get_num_mipmaps());
}
}
else
{
ACGL::Utils::error() << "could not open " << _filename << std::endl;
}
return texture;
}
SharedTexture3D loadTexture3DFromDDS(const std::string& _filename)
{
CDDSImage image;
SharedTexture3D texture;
if(image.load(_filename))
{
GLenum internal_format = getDDSInternalFormat(image.is_compressed(), image.get_format());
if(image.is_volume())
{
// It is a 3D texture
texture = SharedTexture3D(new Texture3D(glm::uvec3(image.get_width(), image.get_height(), image.get_depth()), internal_format));
texture->bind();
texImage3D(image.is_compressed(), texture->getTarget(), 0, internal_format, image.get_width(), image.get_height(), image.get_depth(), image.get_format(), image.get_size(), image);
for(unsigned int i = 0; i < image.get_num_mipmaps(); i++)
{
const CSurface& mipmap = image.get_mipmap(i);
texImage3D(image.is_compressed(), texture->getTarget(), i+1, internal_format, mipmap.get_width(), mipmap.get_height(), mipmap.get_depth(), image.get_format(), mipmap.get_size(), mipmap);
}
texture->setMaxLevel(image.get_num_mipmaps());
}
else
{
ACGL::Utils::error() << _filename << " is not a 3D texture" << std::endl;
}
}
else
{
ACGL::Utils::error() << "could not open " << _filename << std::endl;
}
return texture;
}
SharedTextureCubeMap loadTextureCubeMapFromDDS(const std::string& _filename)
{
CDDSImage image;
SharedTextureCubeMap texture;
if(image.load(_filename))
{
GLenum internal_format = getDDSInternalFormat(image.is_compressed(), image.get_format());
if(image.is_cubemap())
{
// It is a cube map texture
texture = SharedTextureCubeMap(new TextureCubeMap(glm::uvec2(image.get_width(), image.get_height()), internal_format));
texture->bind();
unsigned int numMipmapLevels = 1000;
for(unsigned int i = 0; i < 6; ++i)
{
GLenum target = GL_INVALID_ENUM;
switch(i)
{
case 0: target = GL_TEXTURE_CUBE_MAP_POSITIVE_X; break;
case 1: target = GL_TEXTURE_CUBE_MAP_NEGATIVE_X; break;
case 2: target = GL_TEXTURE_CUBE_MAP_POSITIVE_Y; break;
case 3: target = GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; break;
case 4: target = GL_TEXTURE_CUBE_MAP_POSITIVE_Z; break;
case 5: target = GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; break;
}
const CTexture& face = image.get_cubemap_face(i);
texImage2D(image.is_compressed(), target, 0, internal_format, face.get_width(), face.get_height(), image.get_format(), face.get_size(), face);
numMipmapLevels = std::min(numMipmapLevels, face.get_num_mipmaps());
for(unsigned int j = 0; j < face.get_num_mipmaps(); j++)
{
const CSurface& mipmap = face.get_mipmap(j);
texImage2D(image.is_compressed(), texture->getTarget(), j+1, internal_format, mipmap.get_width(), mipmap.get_height(), image.get_format(), mipmap.get_size(), mipmap);
}
}
texture->setMaxLevel(numMipmapLevels);
}
else
{
ACGL::Utils::error() << _filename << " is not a cube map texture" << std::endl;
}
}
else
{
ACGL::Utils::error() << "could not open " << _filename << std::endl;
}
return texture;
}
}
}
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment