632 lines
14 KiB
C++
632 lines
14 KiB
C++
/*
|
|
* Texture manipulation routines
|
|
*
|
|
* Copyright (c) Mark J. Kilgard, 1997.
|
|
* Code added in april 2003 by Erik Hofman
|
|
*
|
|
* This program is freely distributable without licensing fees
|
|
* and is provided without guarantee or warrantee expressed or
|
|
* implied. This program is -not- in the public domain.
|
|
*
|
|
* $Id$
|
|
*/
|
|
|
|
#include <simgear/compiler.h>
|
|
#ifdef WIN32
|
|
# include <windows.h>
|
|
#endif
|
|
#include <GL/glu.h>
|
|
#include <zlib.h>
|
|
|
|
#include "texture.hxx"
|
|
#include "colours.h"
|
|
|
|
SGTexture::SGTexture()
|
|
: texture_id(0),
|
|
texture_data(0),
|
|
num_colors(3)
|
|
{
|
|
}
|
|
|
|
SGTexture::SGTexture(unsigned int width, unsigned int height)
|
|
: texture_id(0)
|
|
{
|
|
texture_data = new GLubyte[ width * height * 3 ];
|
|
}
|
|
|
|
SGTexture::~SGTexture()
|
|
{
|
|
if ( texture_data ) {
|
|
delete texture_data;
|
|
}
|
|
|
|
if ( texture_id != 0 ) {
|
|
free_id();
|
|
}
|
|
}
|
|
|
|
void
|
|
SGTexture::bind()
|
|
{
|
|
bool gen = false;
|
|
if (!texture_id) {
|
|
#ifdef GL_VERSION_1_1
|
|
glGenTextures(1, &texture_id);
|
|
|
|
#elif GL_EXT_texture_object
|
|
glGenTexturesEXT(1, &texture_id);
|
|
#endif
|
|
gen = true;
|
|
}
|
|
|
|
#ifdef GL_VERSION_1_1
|
|
glBindTexture(GL_TEXTURE_2D, texture_id);
|
|
|
|
#elif GL_EXT_texture_object
|
|
glBindTextureEXT(GL_TEXTURE_2D, texture_id);
|
|
#endif
|
|
|
|
if (gen) {
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A function to resize the OpenGL window which will be used by
|
|
* the dynamic texture generating routines.
|
|
*
|
|
* @param width The width of the new window
|
|
* @param height The height of the new window
|
|
*/
|
|
void
|
|
SGTexture::resize(unsigned int width, unsigned int height)
|
|
{
|
|
GLfloat aspect;
|
|
|
|
// Make sure that we don't get a divide by zero exception
|
|
if (height == 0)
|
|
height = 1;
|
|
|
|
// Set the viewport for the OpenGL window
|
|
glViewport(0, 0, width, height);
|
|
|
|
// Calculate the aspect ratio of the window
|
|
aspect = width/height;
|
|
|
|
// Go to the projection matrix, this gets modified by the perspective
|
|
// calulations
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
|
|
// Do the perspective calculations
|
|
gluPerspective(45.0, aspect, 1.0, 400.0);
|
|
|
|
// Return to the modelview matrix
|
|
glMatrixMode(GL_MODELVIEW);
|
|
}
|
|
|
|
/**
|
|
* A function to prepare the OpenGL state machine for dynamic
|
|
* texture generation.
|
|
*
|
|
* @param width The width of the texture
|
|
* @param height The height of the texture
|
|
*/
|
|
void
|
|
SGTexture::prepare(unsigned int width, unsigned int height) {
|
|
|
|
texture_width = width;
|
|
texture_height = height;
|
|
|
|
// Resize the OpenGL window to the size of our dynamic texture
|
|
resize(texture_width, texture_height);
|
|
|
|
// Clear the contents of the screen buffer to blue
|
|
glClearColor(0.0, 0.0, 1.0, 1.0);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
// Turn off texturing (don't want the torus to be texture);
|
|
glDisable(GL_TEXTURE_2D);
|
|
}
|
|
|
|
/**
|
|
* A function to generate the dynamic texture.
|
|
*
|
|
* The actual texture can be accessed by calling get_texture()
|
|
*
|
|
* @param width The width of the previous OpenGL window
|
|
* @param height The height of the previous OpenGL window
|
|
*/
|
|
void
|
|
SGTexture::finish(unsigned int width, unsigned int height) {
|
|
// If a texture hasn't been created then it gets created, and the contents
|
|
// of the frame buffer gets copied into it. If the texture has already been
|
|
// created then its contents just get updated.
|
|
bind();
|
|
if (!texture_data)
|
|
{
|
|
// Copies the contents of the frame buffer into the texture
|
|
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0,
|
|
texture_width, texture_height, 0);
|
|
|
|
} else {
|
|
// Copies the contents of the frame buffer into the texture
|
|
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0,
|
|
texture_width, texture_height);
|
|
}
|
|
|
|
// Set the OpenGL window back to its previous size
|
|
resize(width, height);
|
|
|
|
// Clear the window back to black
|
|
glClearColor(0.0, 0.0, 0.0, 1.0);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
}
|
|
|
|
|
|
void
|
|
SGTexture::read_alpha_texture(const char *name)
|
|
{
|
|
GLubyte *lptr;
|
|
SGTexture::ImageRec *image;
|
|
int y;
|
|
|
|
if (texture_data)
|
|
delete texture_data;
|
|
|
|
image = ImageOpen(name);
|
|
if(!image) {
|
|
return;
|
|
}
|
|
|
|
texture_width = image->xsize;
|
|
texture_height = image->ysize;
|
|
|
|
// printf("image->zsize = %d\n", image->zsize);
|
|
|
|
if (image->zsize != 1) {
|
|
ImageClose(image);
|
|
return;
|
|
}
|
|
|
|
texture_data = new GLubyte[ image->xsize * image->ysize ];
|
|
if (!texture_data)
|
|
return;
|
|
|
|
lptr = texture_data;
|
|
for(y=0; y<image->ysize; y++) {
|
|
ImageGetRow(image,lptr,y,0);
|
|
lptr += image->xsize;
|
|
}
|
|
ImageClose(image);
|
|
}
|
|
|
|
void
|
|
SGTexture::read_rgb_texture(const char *name)
|
|
{
|
|
GLubyte *ptr;
|
|
GLubyte *rbuf, *gbuf, *bbuf, *abuf;
|
|
SGTexture::ImageRec *image;
|
|
int y;
|
|
|
|
if (texture_data)
|
|
delete texture_data;
|
|
|
|
image = ImageOpen(name);
|
|
if(!image)
|
|
return;
|
|
|
|
texture_width = image->xsize;
|
|
texture_height = image->ysize;
|
|
if (image->zsize != 3 && image->zsize != 4) {
|
|
ImageClose(image);
|
|
return;
|
|
}
|
|
|
|
texture_data = new GLubyte[ image->xsize * image->ysize * 3 ];
|
|
rbuf = new GLubyte[ image->xsize ];
|
|
gbuf = new GLubyte[ image->xsize ];
|
|
bbuf = new GLubyte[ image->xsize ];
|
|
abuf = new GLubyte[ image->xsize ];
|
|
if(!texture_data || !rbuf || !gbuf || !bbuf || !abuf) {
|
|
delete texture_data;
|
|
delete rbuf;
|
|
delete gbuf;
|
|
delete bbuf;
|
|
delete abuf;
|
|
return;
|
|
}
|
|
|
|
ptr = texture_data;
|
|
for(y=0; y<image->ysize; y++) {
|
|
if(image->zsize == 4) {
|
|
ImageGetRow(image,rbuf,y,0);
|
|
ImageGetRow(image,gbuf,y,1);
|
|
ImageGetRow(image,bbuf,y,2);
|
|
ImageGetRow(image,abuf,y,3); // discard
|
|
rgbtorgb(rbuf,gbuf,bbuf,ptr,image->xsize);
|
|
ptr += (image->xsize * 3);
|
|
} else {
|
|
ImageGetRow(image,rbuf,y,0);
|
|
ImageGetRow(image,gbuf,y,1);
|
|
ImageGetRow(image,bbuf,y,2);
|
|
rgbtorgb(rbuf,gbuf,bbuf,ptr,image->xsize);
|
|
ptr += (image->xsize * 3);
|
|
}
|
|
}
|
|
|
|
ImageClose(image);
|
|
delete rbuf;
|
|
delete gbuf;
|
|
delete bbuf;
|
|
delete abuf;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
SGTexture::read_rgba_texture(const char *name)
|
|
{
|
|
GLubyte *ptr;
|
|
GLubyte *rbuf, *gbuf, *bbuf, *abuf;
|
|
SGTexture::ImageRec *image;
|
|
int y;
|
|
|
|
if (texture_data)
|
|
delete texture_data;
|
|
|
|
image = ImageOpen(name);
|
|
if(!image)
|
|
return;
|
|
|
|
texture_width = image->xsize;
|
|
texture_height = image->ysize;
|
|
if (image->zsize != 3 && image->zsize != 4) {
|
|
ImageClose(image);
|
|
return;
|
|
}
|
|
|
|
texture_data = new GLubyte[ image->xsize * image->ysize * 4 ];
|
|
rbuf = new GLubyte[ image->xsize ];
|
|
gbuf = new GLubyte[ image->xsize ];
|
|
bbuf = new GLubyte[ image->xsize ];
|
|
abuf = new GLubyte[ image->xsize ];
|
|
if(!texture_data || !rbuf || !gbuf || !bbuf || !abuf) {
|
|
delete texture_data;
|
|
delete rbuf;
|
|
delete gbuf;
|
|
delete bbuf;
|
|
delete abuf;
|
|
return;
|
|
}
|
|
|
|
ptr = texture_data;
|
|
memset(abuf, 255, image->xsize);
|
|
for(y=0; y<image->ysize; y++) {
|
|
if(image->zsize == 4) {
|
|
ImageGetRow(image,rbuf,y,0);
|
|
ImageGetRow(image,gbuf,y,1);
|
|
ImageGetRow(image,bbuf,y,2);
|
|
ImageGetRow(image,abuf,y,3);
|
|
rgbatorgba(rbuf,gbuf,bbuf,abuf,ptr,image->xsize);
|
|
ptr += (image->xsize * 4);
|
|
} else {
|
|
ImageGetRow(image,rbuf,y,0);
|
|
ImageGetRow(image,gbuf,y,1);
|
|
ImageGetRow(image,bbuf,y,2);
|
|
rgbatorgba(rbuf,gbuf,bbuf,abuf,ptr,image->xsize);
|
|
ptr += (image->xsize * 3);
|
|
}
|
|
}
|
|
|
|
ImageClose(image);
|
|
delete rbuf;
|
|
delete gbuf;
|
|
delete bbuf;
|
|
delete abuf;
|
|
}
|
|
|
|
void
|
|
SGTexture::read_raw_texture(const char *name)
|
|
{
|
|
GLubyte *ptr;
|
|
SGTexture::ImageRec *image;
|
|
int y;
|
|
|
|
if (texture_data)
|
|
delete texture_data;
|
|
|
|
image = RawImageOpen(name);
|
|
|
|
if(!image)
|
|
return;
|
|
|
|
texture_width = 256;
|
|
texture_height = 256;
|
|
|
|
texture_data = new GLubyte[ 256 * 256 * 3 ];
|
|
if(!texture_data)
|
|
return;
|
|
|
|
ptr = texture_data;
|
|
for(y=0; y<256; y++) {
|
|
gzread(image->file, ptr, 256*3);
|
|
ptr+=256*3;
|
|
}
|
|
ImageClose(image);
|
|
}
|
|
|
|
void
|
|
SGTexture::read_r8_texture(const char *name)
|
|
{
|
|
unsigned char c[1];
|
|
GLubyte *ptr;
|
|
SGTexture::ImageRec *image;
|
|
int xy;
|
|
|
|
if (texture_data)
|
|
delete texture_data;
|
|
|
|
//it wouldn't make sense to write a new function ...
|
|
image = RawImageOpen(name);
|
|
|
|
if(!image)
|
|
return;
|
|
|
|
texture_width = 256;
|
|
texture_height = 256;
|
|
|
|
texture_data = new GLubyte [ 256 * 256 * 3 ];
|
|
if(!texture_data)
|
|
return;
|
|
|
|
ptr = texture_data;
|
|
for(xy=0; xy<(256*256); xy++) {
|
|
gzread(image->file, c, 1);
|
|
|
|
//look in the table for the right colours
|
|
ptr[0]=msfs_colour[c[0]][0];
|
|
ptr[1]=msfs_colour[c[0]][1];
|
|
ptr[2]=msfs_colour[c[0]][2];
|
|
|
|
ptr+=3;
|
|
}
|
|
ImageClose(image);
|
|
}
|
|
|
|
|
|
void
|
|
SGTexture::set_pixel(GLuint x, GLuint y, sgVec3 &c)
|
|
{
|
|
if (!texture_data)
|
|
return;
|
|
|
|
unsigned int pos = (x + y*texture_width)*3;
|
|
texture_data[pos] = (unsigned char)(c[0] * 256);
|
|
texture_data[pos+1] = (unsigned char)(c[1] * 256);
|
|
texture_data[pos+2] = (unsigned char)(c[2] * 256);
|
|
}
|
|
|
|
|
|
float *
|
|
SGTexture::get_pixel(GLuint x, GLuint y)
|
|
{
|
|
static sgVec3 c;
|
|
|
|
sgSetVec3(c, 0, 0, 0);
|
|
if (!texture_data)
|
|
return c;
|
|
|
|
unsigned int pos = (x + y*texture_width)*3;
|
|
|
|
sgSetVec3(c, texture_data[pos], texture_data[pos+1], texture_data[pos+2]);
|
|
|
|
return c;
|
|
}
|
|
|
|
SGTexture::ImageRec *
|
|
SGTexture::ImageOpen(const char *fileName)
|
|
{
|
|
union {
|
|
int testWord;
|
|
char testByte[4];
|
|
} endianTest;
|
|
|
|
SGTexture::ImageRec *image;
|
|
int swapFlag;
|
|
int x;
|
|
|
|
endianTest.testWord = 1;
|
|
if (endianTest.testByte[0] == 1) {
|
|
swapFlag = 1;
|
|
} else {
|
|
swapFlag = 0;
|
|
}
|
|
|
|
image = new SGTexture::ImageRec;
|
|
if (image == 0) {
|
|
// fprintf(stderr, "Out of memory!\n");
|
|
return 0;
|
|
}
|
|
if ((image->file = gzopen(fileName, "rb")) == 0) {
|
|
return 0;
|
|
}
|
|
|
|
gzread(image->file, image, 12);
|
|
|
|
if (swapFlag) {
|
|
ConvertShort(&image->imagic, 6);
|
|
}
|
|
|
|
image->tmp = new GLubyte[ image->xsize * 256 ];
|
|
if (image->tmp == 0) {
|
|
// fprintf(stderr, "\nOut of memory!\n");
|
|
return 0;
|
|
}
|
|
|
|
if ((image->type & 0xFF00) == 0x0100) {
|
|
x = image->ysize * image->zsize * (int) sizeof(unsigned);
|
|
image->rowStart = new unsigned[x];
|
|
image->rowSize = new int[x];
|
|
if (image->rowStart == 0 || image->rowSize == 0) {
|
|
// fprintf(stderr, "\nOut of memory!\n");
|
|
return 0;
|
|
}
|
|
image->rleEnd = 512 + (2 * x);
|
|
gzseek(image->file, 512, SEEK_SET);
|
|
gzread(image->file, image->rowStart, x);
|
|
gzread(image->file, image->rowSize, x);
|
|
if (swapFlag) {
|
|
ConvertUint(image->rowStart, x/(int) sizeof(unsigned));
|
|
ConvertUint((unsigned *)image->rowSize, x/(int) sizeof(int));
|
|
}
|
|
}
|
|
return image;
|
|
}
|
|
|
|
|
|
void
|
|
SGTexture::ImageClose(SGTexture::ImageRec *image) {
|
|
gzclose(image->file);
|
|
delete image->tmp;
|
|
delete image;
|
|
}
|
|
|
|
|
|
SGTexture::ImageRec *
|
|
SGTexture::RawImageOpen(const char *fileName)
|
|
{
|
|
union {
|
|
int testWord;
|
|
char testByte[4];
|
|
} endianTest;
|
|
|
|
SGTexture::ImageRec *image;
|
|
int swapFlag;
|
|
|
|
endianTest.testWord = 1;
|
|
if (endianTest.testByte[0] == 1) {
|
|
swapFlag = 1;
|
|
} else {
|
|
swapFlag = 0;
|
|
}
|
|
|
|
image = new SGTexture::ImageRec;
|
|
if (image == 0) {
|
|
// fprintf(stderr, "Out of memory!\n");
|
|
return 0;
|
|
}
|
|
if ((image->file = gzopen(fileName, "rb")) == 0) {
|
|
return 0;
|
|
}
|
|
|
|
gzread(image->file, image, 12);
|
|
|
|
if (swapFlag) {
|
|
ConvertShort(&image->imagic, 6);
|
|
}
|
|
|
|
|
|
//just allocate a pseudo value as I'm too lazy to change ImageClose()...
|
|
image->tmp = new GLubyte;
|
|
|
|
if (image->tmp == 0) {
|
|
// fprintf(stderr, "\nOut of memory!\n");
|
|
return 0;
|
|
}
|
|
|
|
return image;
|
|
}
|
|
|
|
void
|
|
SGTexture::ImageGetRow(SGTexture::ImageRec *image, GLubyte *buf, int y, int z) {
|
|
GLubyte *iPtr, *oPtr, pixel;
|
|
int count;
|
|
|
|
if ((image->type & 0xFF00) == 0x0100) {
|
|
gzseek(image->file, (long) image->rowStart[y+z*image->ysize], SEEK_SET);
|
|
gzread(image->file, image->tmp,
|
|
(unsigned int)image->rowSize[y+z*image->ysize]);
|
|
|
|
iPtr = image->tmp;
|
|
oPtr = buf;
|
|
for (;;) {
|
|
pixel = *iPtr++;
|
|
count = (int)(pixel & 0x7F);
|
|
if (!count) {
|
|
return;
|
|
}
|
|
if (pixel & 0x80) {
|
|
while (count--) {
|
|
*oPtr++ = *iPtr++;
|
|
}
|
|
} else {
|
|
pixel = *iPtr++;
|
|
while (count--) {
|
|
*oPtr++ = pixel;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
gzseek(image->file, 512+(y*image->xsize)+(z*image->xsize*image->ysize),
|
|
SEEK_SET);
|
|
gzread(image->file, buf, image->xsize);
|
|
}
|
|
}
|
|
|
|
void
|
|
SGTexture::rgbtorgb(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *l, int n) {
|
|
while(n--) {
|
|
l[0] = r[0];
|
|
l[1] = g[0];
|
|
l[2] = b[0];
|
|
l += 3; r++; g++; b++;
|
|
}
|
|
}
|
|
|
|
void
|
|
SGTexture::rgbatorgba(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *a,
|
|
GLubyte *l, int n) {
|
|
while(n--) {
|
|
l[0] = r[0];
|
|
l[1] = g[0];
|
|
l[2] = b[0];
|
|
l[3] = a[0];
|
|
l += 4; r++; g++; b++; a++;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
SGTexture::ConvertShort(unsigned short *array, unsigned int length) {
|
|
unsigned short b1, b2;
|
|
unsigned char *ptr;
|
|
|
|
ptr = (unsigned char *)array;
|
|
while (length--) {
|
|
b1 = *ptr++;
|
|
b2 = *ptr++;
|
|
*array++ = (b1 << 8) | (b2);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
SGTexture::ConvertUint(unsigned *array, unsigned int length) {
|
|
unsigned int b1, b2, b3, b4;
|
|
unsigned char *ptr;
|
|
|
|
ptr = (unsigned char *)array;
|
|
while (length--) {
|
|
b1 = *ptr++;
|
|
b2 = *ptr++;
|
|
b3 = *ptr++;
|
|
b4 = *ptr++;
|
|
*array++ = (b1 << 24) | (b2 << 16) | (b3 << 8) | (b4);
|
|
}
|
|
}
|
|
|