Added support for non power of two textures on newer graphics cards.

This commit is contained in:
Chris Laurel 2006-04-27 17:15:18 +00:00
parent a8d9356d97
commit 29b04dbb7e
3 changed files with 47 additions and 13 deletions

View file

@ -85,6 +85,13 @@ using namespace std;
#endif // PNG_SUPPORT
// All rows are padded to a size that's a multiple of 4 bytes
static int pad(int n)
{
return (n + 3) & ~0x3;
}
static int formatComponents(int fmt)
{
switch (fmt)
@ -131,7 +138,7 @@ static int calcMipLevelSize(int fmt, int w, int h, int mip)
// 4x4 blocks, 16 bytes per block
return ((w + 3) / 4) * ((h + 3) / 4) * 16;
default:
return h * w * formatComponents(fmt);
return h * pad(w * formatComponents(fmt));
}
}
@ -146,6 +153,8 @@ Image::Image(int fmt, int w, int h, int mips) :
components = formatComponents(fmt);
assert(components != 0);
pitch = pad(w * components);
size = 0;
for (int i = 0; i < mipLevels; i++)
size += calcMipLevelSize(fmt, w, h, i);
@ -172,6 +181,12 @@ int Image::getHeight() const
}
int Image::getPitch() const
{
return pitch;
}
int Image::getMipLevelCount() const
{
return mipLevels;
@ -213,7 +228,7 @@ unsigned char* Image::getPixelRow(int mip, int row)
if (isCompressed())
return NULL;
return getMipLevel(mip) + row * w * components;
return getMipLevel(mip) + row * pitch;
}
@ -292,6 +307,7 @@ Image* Image::computeNormalMap(float scale, bool wrap) const
return NULL;
unsigned char* nmPixels = normalMap->getPixels();
int nmPitch = normalMap->getPitch();
// Compute normals using differences between adjacent texels.
for (int i = 0; i < height; i++)
@ -327,9 +343,9 @@ Image* Image::computeNormalMap(float scale, bool wrap) const
}
}
int h00 = (int) pixels[(i0 * width + j0) * components];
int h10 = (int) pixels[(i0 * width + j1) * components];
int h01 = (int) pixels[(i1 * width + j0) * components];
int h00 = (int) pixels[i0 * pitch + j0 * components];
int h10 = (int) pixels[i0 * pitch + j1 * components];
int h01 = (int) pixels[i1 * pitch + j0 * components];
float dx = (float) (h10 - h00) * (1.0f / 255.0f) * scale;
float dy = (float) (h01 - h00) * (1.0f / 255.0f) * scale;
@ -337,7 +353,7 @@ Image* Image::computeNormalMap(float scale, bool wrap) const
float mag = (float) sqrt(dx * dx + dy * dy + 1.0f);
float rmag = 1.0f / mag;
int n = (i * width + j) * 4;
int n = i * nmPitch + j * 4;
nmPixels[n] = (unsigned char) (128 + 127 * dx * rmag);
nmPixels[n + 1] = (unsigned char) (128 + 127 * dy * rmag);
nmPixels[n + 2] = (unsigned char) (128 + 127 * rmag);

View file

@ -27,6 +27,7 @@ class Image
int getWidth() const;
int getHeight() const;
int getPitch() const;
int getMipLevelCount() const;
int getFormat() const;
int getComponents() const;
@ -50,6 +51,7 @@ class Image
private:
int width;
int height;
int pitch;
int mipLevels;
int components;
int format;

View file

@ -101,6 +101,7 @@ struct TextureCaps
bool autoMipMapSupported;
bool maxLevelSupported;
GLint maxTextureSize;
bool nonPow2Supported;
};
static TextureCaps texCaps;
@ -147,6 +148,7 @@ static const TextureCaps& GetTextureCaps()
texCaps.autoMipMapSupported = ExtensionSupported("GL_SGIS_generate_mipmap");
texCaps.maxLevelSupported = testMaxLevel();
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texCaps.maxTextureSize);
texCaps.nonPow2Supported = ExtensionSupported("GL_ARB_texture_non_power_of_two");
}
return texCaps;
@ -404,7 +406,7 @@ ImageTexture::ImageTexture(Image& img,
Texture(img.getWidth(), img.getHeight()),
glName(0)
{
glGenTextures(1, (GLuint*)&glName);
glGenTextures(1, (GLuint*) &glName);
glBindTexture(GL_TEXTURE_2D, glName);
bool mipmap = mipMapMode == DefaultMipMaps;
@ -417,7 +419,7 @@ ImageTexture::ImageTexture(Image& img,
precomputedMipMaps = true;
// We can't automatically generate mipmaps for compressed textures.
// If a precomputed mipmap set isn't provided, turn of mipmapping entirely.
// If a precomputed mipmap set isn't provided, turn off mipmapping entirely.
if (!precomputedMipMaps && img.isCompressed())
mipmap = false;
@ -426,7 +428,7 @@ ImageTexture::ImageTexture(Image& img,
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texAddress);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
mipmap ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
mipMapMode == NoMipMaps ? GL_LINEAR : GL_LINEAR_MIPMAP_LINEAR);
if (mipMapMode == AutoMipMaps)
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
@ -445,6 +447,7 @@ ImageTexture::ImageTexture(Image& img,
}
else
{
clog << "gluBuild2DMipmaps\n";
gluBuild2DMipmaps(GL_TEXTURE_2D,
internalFormat,
getWidth(), getHeight(),
@ -513,7 +516,7 @@ TiledTexture::TiledTexture(Image& img,
alpha = img.hasAlpha();
bool mipmap = mipMapMode == DefaultMipMaps;
bool mipmap = mipMapMode != NoMipMaps;
bool precomputedMipMaps = false;
// Require a complete set of mipmaps
@ -745,8 +748,9 @@ CubeMap::CubeMap(Image* faces[]) :
glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MIN_FILTER,
mipmap ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
// mipMapMode == NoMipMaps ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
int internalFormat = getInternalFormat(format);
@ -955,6 +959,7 @@ static Texture* CreateTextureFromImage(Image& img,
Texture::AddressMode addressMode,
Texture::MipMapMode mipMode)
{
#if 0
// Require texture dimensions to be powers of two. Even though the
// OpenGL driver will automatically rescale textures with non-power of
// two sizes, some quality loss may result. The power of two requirement
@ -966,23 +971,34 @@ static Texture* CreateTextureFromImage(Image& img,
clog << "Texture has non-power of two dimensions.\n";
return NULL;
}
#endif
// If non power of two textures are supported switch mipmap generation to
// automatic. gluBuildMipMaps may rescale the texture to a power of two
// on some drivers even when the hardware supports non power of two textures,
// whereas auto mipmap generation will properly deal with the dimensions.
if (GetTextureCaps().nonPow2Supported)
{
if (mipMode == Texture::DefaultMipMaps)
mipMode = Texture::AutoMipMaps;
}
bool splittingAllowed = true;
Texture* tex = NULL;
int maxDim = GetTextureCaps().maxTextureSize;
//int maxDim = 256;
if ((img.getWidth() > maxDim || img.getHeight() > maxDim) &&
splittingAllowed)
{
// The texture is too large; we need to split it.
int uSplit = max(1, img.getWidth() / maxDim);
int vSplit = max(1, img.getHeight() / maxDim);
clog << "Creating tiled texture. Width=" << img.getWidth() << ", max=" << maxDim << "\n";
tex = new TiledTexture(img, uSplit, vSplit, mipMode);
}
else
{
clog << "Creating ordinary texture: " << img.getWidth() << "x" << img.getHeight() << "\n";
// The image is small enough to fit in a single texture; or, splitting
// was disallowed so we'll scale the large image down to fit in
// an ordinary texture.