/* wavefront.cpp
*/
#include “headers.h”
#include “gpuProgram.h”
#include “linalg.h”
#include
#include
#include
#ifdef HAVE_PNG
#include
#include “wavefront.h”
bool wfModel::newGroupWithNewMaterial = false;
bool wfModel::verticesAreCW = false;
unsigned char wfMaterial::defaultTexmap[] = { 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255 };
/* Read a Wavefront model into this structure. See ObjectFile.html
* for a description of the Wavefront file format. This code is from
* the Nate Robins GLM library.
*/
void wfModel::read( char *filename )
{
FILE* file;
char buf[1000];
float x, y, z;
wfGroup *currentGroup;
wfMaterial *currentMaterial;
int nextGroupNum = 0;
// Counts of different vertex formats
int numVTN = 0;
int numVT = 0;
int numVN = 0;
int numV = 0;
/* init */
vertices.clear();
normals.clear();
texcoords.clear();
facetnorms.clear();
materials.clear();
groups.clear();
pathname = strdup(filename);
groups.add( new wfGroup( “default” ) );
currentGroup = groups[0];
materials.add( new wfMaterial( “default” ) );
currentMaterial = materials[0];
currentGroup->material = currentMaterial;
/* open the file */
file = fopen(filename, “r”);
if (!file) {
cerr << "wfModel::read() failed: can't open data file '" << filename << "'." << endl;
exit(-1);
}
/* process each line */
lineNum = 0;
while(fscanf(file, "%s", buf) != EOF) {
lineNum++;
if (strncmp( buf, "transform", 9 ) == 0) {
for (int r=0; r<4; r++)
for (int c=0; c<4; c++) {
float val;
fscanf( file, "%f", &val );
objToWorldTransform[r][c] = val;
}
} else {
int v = 0, n = 0, t = 0;
wfTriangle *tri, *prevTri;
switch(buf[0]) {
case '#': /* comment */
/* eat up rest of line */
fgets(buf, sizeof(buf), file);
break;
case 's': /* smoothing group ... ignore */
/* eat up rest of line */
fgets(buf, sizeof(buf), file);
break;
case 'v': /* v, vn, vt */
switch(buf[1]) {
case '\0': /* vertex */
fscanf(file, "%f %f %f", &x, &y, &z );
vertices.add( vec3(x,y,z) );
break;
case 'n': /* normal */
fscanf(file, "%f %f %f", &x, &y, &z );
normals.add( vec3(x,y,z).normalize() );
break;
case 't': /* texcoord */
fscanf(file, "%f %f", &x, &y );
texcoords.add( vec3(x,y,0) );
fgets(buf, sizeof(buf), file); // skip rest of line
break;
}
break;
case 'm': /* mtllib filename */
fgets(buf, sizeof(buf), file);
sscanf(buf, "%s %s", buf, buf);
mtllibname = strdup(buf);
readMaterialLibrary( buf );
break;
case 'u': /* usemtl name */
if (newGroupWithNewMaterial) {
char buffer[100];
sprintf( buffer, "g%d", nextGroupNum++ );
currentGroup = findGroup( buffer );
}
fgets(buf, sizeof(buf), file);
sscanf(buf, "%s %s", buf, buf);
currentGroup->material = currentMaterial = findMaterial( buf );
break;
case ‘g’: /* group */
/* eat up rest of line */
fgets(buf, sizeof(buf), file);
sscanf(buf, “%s”, buf);
if (buf[0] == ‘\n’)
currentGroup = findGroup( “default” );
else
currentGroup = findGroup( buf );
currentGroup->material = currentMaterial;
break;
case ‘f’: /* face */
fscanf(file, “%s”, buf);
/* can be one of %d, %d//%d, %d/%d, or %d/%d/%d */
tri = new wfTriangle();
if (strstr(buf, “//”)) { /* v//n */
numVN++;
/* First three vertices define a triangle */
sscanf(buf, “%d//%d”, &v, &n); v–; checkVindex(v); n–; tri->vindices[0] = v; tri->nindices[0] = n;
fscanf(file, “%d//%d”, &v, &n); v–; checkVindex(v); n–; tri->vindices[1] = v; tri->nindices[1] = n;
fscanf(file, “%d//%d”, &v, &n); v–; checkVindex(v); n–; tri->vindices[2] = v; tri->nindices[2] = n;
currentGroup->triangles.add( tri );
/* More vertices (a convex polygon) are converted to a fan of triangles: */
while(fscanf(file, “%d//%d”, &v, &n) > 0) {
v–; checkVindex(v); n–;
prevTri = tri;
tri = new wfTriangle();
tri->vindices[0] = prevTri->vindices[0];
tri->nindices[0] = prevTri->nindices[0];
tri->vindices[1] = prevTri->vindices[2];
tri->nindices[1] = prevTri->nindices[2];
tri->vindices[2] = v;
tri->nindices[2] = n;
currentGroup->triangles.add( tri );
}
} else if (sscanf(buf, “%d/%d/%d”, &v, &t, &n) == 3) { /* v/t/n */
numVTN++;
v–; checkVindex(v); n–; t–;
tri->vindices[0] = v;
tri->tindices[0] = t;
tri->nindices[0] = n;
fscanf(file, “%d/%d/%d”, &v, &t, &n); v–; checkVindex(v); n–; t–;
tri->vindices[1] = v;
tri->tindices[1] = t;
tri->nindices[1] = n;
fscanf(file, “%d/%d/%d”, &v, &t, &n); v–; checkVindex(v); n–; t–;
tri->vindices[2] = v;
tri->tindices[2] = t;
tri->nindices[2] = n;
currentGroup->triangles.add( tri );
while(fscanf(file, “%d/%d/%d”, &v, &t, &n) > 0) {
v–; checkVindex(v); n–; t–;
prevTri = tri;
tri = new wfTriangle();
tri->vindices[0] = prevTri->vindices[0];
tri->tindices[0] = prevTri->tindices[0];
tri->nindices[0] = prevTri->nindices[0];
tri->vindices[1] = prevTri->vindices[2];
tri->tindices[1] = prevTri->tindices[2];
tri->nindices[1] = prevTri->nindices[2];
tri->vindices[2] = v;
tri->tindices[2] = t;
tri->nindices[2] = n;
currentGroup->triangles.add( tri );
}
} else if (sscanf(buf, “%d/%d”, &v, &t) == 2) { /* v/t */
numVT++;
v–; checkVindex(v); t–;
tri->vindices[0] = v;
tri->tindices[0] = t;
fscanf(file, “%d/%d”, &v, &t); v–; checkVindex(v); t–;
tri->vindices[1] = v;
tri->tindices[1] = t;
fscanf(file, “%d/%d”, &v, &t); v–; checkVindex(v); t–;
tri->vindices[2] = v;
tri->tindices[2] = t;
currentGroup->triangles.add( tri );
while(fscanf(file, “%d/%d”, &v, &t) > 0) {
v–; checkVindex(v); t–;
prevTri = tri;
tri = new wfTriangle();
tri->vindices[0] = prevTri->vindices[0];
tri->tindices[0] = prevTri->tindices[0];
tri->vindices[1] = prevTri->vindices[2];
tri->tindices[1] = prevTri->tindices[2];
tri->vindices[2] = v;
tri->tindices[2] = t;
currentGroup->triangles.add( tri );
}
} else { /* v */
numV++;
sscanf(buf, “%d”, &v); v–; checkVindex(v);
tri->vindices[0] = v;
fscanf(file, “%d”, &v); v–; checkVindex(v);
tri->vindices[1] = v;
fscanf(file, “%d”, &v); v–; checkVindex(v);
tri->vindices[2] = v;
currentGroup->triangles.add( tri );
while(fscanf(file, “%d”, &v) > 0) {
v–; checkVindex(v);
prevTri = tri;
tri = new wfTriangle();
tri->vindices[0] = prevTri->vindices[0];
tri->vindices[1] = prevTri->vindices[2];
tri->vindices[2] = v;
currentGroup->triangles.add( tri );
}
}
break;
default:
cerr << "Warning: unrecognized Wavefront command on line " << lineNum << ": " << buf << endl;
break;
}
}
}
// Determine a consistent format for each vertex
hasVertexNormals = (numVTN > 0 || numVN > 0);
hasVertexTexCoords = (numVTN > 0 || numVT > 0);
// Compute all face normals
for (int g=0; g
wfTriangle &tri = *groups[g]->triangles[i];
vec3 d01 = vertices[ tri.vindices[1] ] – vertices[ tri.vindices[0] ];
vec3 d02 = vertices[ tri.vindices[2] ] – vertices[ tri.vindices[0] ];
vec3 n;
if (verticesAreCW)
n = (d02 ^ d01).normalize();
else
n = (d01 ^ d02).normalize();
tri.findex = facetnorms.size();
facetnorms.add( n );
}
// Find bounding box
min = vec3(MAXFLOAT,MAXFLOAT,MAXFLOAT);
max = vec3(-MAXFLOAT,-MAXFLOAT,-MAXFLOAT);
vec3 sum(0,0,0);
for (int i=0; i
max.x = vertices[i].x;
if (vertices[i].y > max.y)
max.y = vertices[i].y;
if (vertices[i].z > max.z)
max.z = vertices[i].z;
}
centre = 0.5 * (min + max);
radius = 0.5 * (max – min).length();
}
void wfModel::readMaterialLibrary( char *name )
{
FILE* file;
char buf[1000];
wfMaterial *currentMaterial;
int i;
/* prepend path to the directory of the model file */
char *dir = new char[ strlen( pathname )+1 ];
strcpy( dir, pathname );
char *s = strrchr(dir, ‘/’);
if (s)
s[1] = ‘\0’;
else
dir[0] = ‘\0’;
char *filename = new char[ strlen(dir) + strlen(name) + 1 ];
strcpy(filename, dir);
strcat(filename, name);
/* open the file */
file = fopen(filename, “r”);
if (!file) {
cerr << "wfModel::readMaterialLibrary() couldn't open file '" << filename << "'" << endl;
exit(-1);
}
/* set the default material */
if (materials.size() == 0)
materials.add( new wfMaterial("default") );
currentMaterial = materials[0];
/* now, read in the data */
while(fscanf(file, "%s", buf) != EOF) {
switch(buf[0]) {
case '#': /* comment */
/* eat up rest of line */
fgets(buf, sizeof(buf), file);
break;
case 'n': /* newmtl */
fgets(buf, sizeof(buf), file);
sscanf(buf, "%s %s", buf, buf);
for (i=0; i
break;
if (i
/* wavefront shininess is from [0, 1000], so scale for OpenGL */
//currentMaterial->shininess /= 1000.0;
//currentMaterial->shininess *= 128.0;
break;
case ‘K’:
switch(buf[1]) {
case ‘d’:
fscanf( file, “%f %f %f”,
¤tMaterial->diffuse[0],
¤tMaterial->diffuse[1],
¤tMaterial->diffuse[2] );
break;
case ‘s’:
fscanf( file, “%f %f %f”,
¤tMaterial->specular[0],
¤tMaterial->specular[1],
¤tMaterial->specular[2] );
break;
case ‘a’:
fscanf( file, “%f %f %f”,
¤tMaterial->ambient[0],
¤tMaterial->ambient[1],
¤tMaterial->ambient[2]);
break;
default:
/* eat up rest of line */
fgets(buf, sizeof(buf), file);
break;
}
break;
case ‘m’: /* map_Kd filename */
fgets(buf, sizeof(buf), file);
sscanf(buf, “%s %s”, buf, buf);
{
// prepend path to the directory of the model file
char *dir = new char[ strlen(pathname) ];
strcpy( dir, pathname );
char *s = strrchr(dir, ‘/’);
if (s != NULL) s[1] = ‘\0’; else dir[0] = ‘\0’;
char *filename = new char[ strlen(dir) + strlen(buf) + 1 ];
strcpy(filename, dir);
strcat(filename, buf);
// load the texture
currentMaterial->loadTexmap( filename );
delete [] dir;
delete [] filename;
}
break;
default:
/* eat up rest of line */
fgets(buf, sizeof(buf), file);
break;
}
}
delete [] dir;
delete [] filename;
}
/* read a ppm texture map into the material
*/
void wfMaterial::loadTexmap( char *filename )
{
char *p = strrchr( filename, ‘.’ );
if (p == NULL || strcmp( p, “.ppm” ) == 0)
texmap = readP6( filename );
else if (strcmp( p, “.png” ) == 0)
texmap = readPNG( filename );
else {
cerr << "Cannot read " << filename << ". Only ppm and png files are handled." << endl;
texmap = NULL;
}
}
wfMaterial* wfModel::findMaterial( char *name )
{
int i;
for (i=0; i
break;
if (i < materials.size())
return materials[i];
cerr << "Error: Can't find material '" << name << "'" << endl;
return materials[0];
}
wfGroup* wfModel::findGroup( char *name )
{
int i;
for (i=0; i
break;
if (i < groups.size())
return groups[i];
// create a new group of this name
groups.add( new wfGroup(name) );
return groups[ groups.size() - 1 ];
}
class VertexSignature {
public:
unsigned int sig[3];
bool operator == (const VertexSignature p) {
return sig[0] == p.sig[0] && sig[1] == p.sig[1] && sig[2] == p.sig[2];
}
};
void wfModel::setupVAO( TextureMode textureMode )
{
// Note that positions, normals, and texture coordinates can all be
// indexed differently in a Wavefront file. But OpenGL permits only
// one index per vertex, and the OpenGL vertex encapsulates all
// attributes, including position, normal, and texture coordinates.
//
// So we have to create *another* array of vertices where each
// vertex stores position, normal, and texture coordinates and the
// face indices index into this new array.
unsigned int vertexSize = 3;
if (hasVertexNormals)
vertexSize += 3;
if (hasVertexTexCoords)
vertexSize += 2;
// Process each group separately
for (int i=0; i
if (numTriangles > 0) {
GLfloat *vertexBuffer = new GLfloat[ numTriangles * 3 * vertexSize ];
GLuint *faceIndexBuffer = new GLuint[ numTriangles * 3 ];
unsigned int nVerts = 0;
int nFaces = 0;
VertexSignature *vertSig = new VertexSignature[ numTriangles * 3 ];
for (int j=0; j
wfTriangle *tri = thisGroup->triangles[j];
for (int k=0; k<3; k++) {
// Find an already-stored vertex with this signature (brute force)
VertexSignature vs;
vs.sig[0] = tri->vindices[k];
vs.sig[1] = tri->nindices[k];
vs.sig[2] = tri->tindices[k];
unsigned int l;
for (l=0; l
if (hasVertexNormals)
* (vec3*) &vertexBuffer[nVerts*vertexSize+3] = normals[ tri->nindices[k] ];
if (hasVertexTexCoords) {
if (hasVertexNormals)
* (vec2*) &vertexBuffer[nVerts*vertexSize+6] = * (vec2*) &texcoords[ tri->tindices[k] ];
else
* (vec2*) &vertexBuffer[nVerts*vertexSize+3] = * (vec2*) &texcoords[ tri->tindices[k] ];
}
vertSig[ nVerts ] = vs;
nVerts++;
}
// Store this vertex index
faceIndexBuffer[ nFaces * 3 + k ] = l;
}
nFaces++;
}
// Set up the VAO
glGenVertexArrays( 1, &thisGroup->VAO );
glBindVertexArray( thisGroup->VAO );
GLuint bufferIDs[2];
glGenBuffers( 2, bufferIDs );
// store faces
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, bufferIDs[0] );
glBufferData( GL_ELEMENT_ARRAY_BUFFER, nFaces * 3 * sizeof(GLuint), faceIndexBuffer, GL_STATIC_DRAW );
// store vertices
glBindBuffer( GL_ARRAY_BUFFER, bufferIDs[1] );
glBufferData( GL_ARRAY_BUFFER, nVerts * vertexSize * sizeof(GLfloat), vertexBuffer, GL_STATIC_DRAW );
// define attributes
int attribIndex = 0;
unsigned long int accumulatedOffset = 0;
// position = attribute 0
glEnableVertexAttribArray( attribIndex );
glVertexAttribPointer( attribIndex, 3, GL_FLOAT, GL_FALSE, vertexSize * sizeof(GLfloat), (const GLvoid*) accumulatedOffset );
attribIndex++;
accumulatedOffset += 3 * sizeof( float );
// normals?
if (hasVertexNormals) {
glEnableVertexAttribArray( attribIndex );
glVertexAttribPointer( attribIndex, 3, GL_FLOAT, GL_FALSE, vertexSize * sizeof(GLfloat), (const GLvoid*) accumulatedOffset );
attribIndex++;
accumulatedOffset += 3 * sizeof( float );
}
// texture coordinates?
if (hasVertexTexCoords) {
glEnableVertexAttribArray( attribIndex );
glVertexAttribPointer( attribIndex, 2, GL_FLOAT, GL_FALSE, vertexSize * sizeof(GLfloat), (const GLvoid*) accumulatedOffset );
attribIndex++;
accumulatedOffset += 2 * sizeof( float );
}
thisGroup->VAOinitialized = true;
delete [] vertexBuffer;
delete [] faceIndexBuffer;
delete [] vertSig;
glBindVertexArray( 0 );
}
}
initTextures( textureMode );
}
void wfModel::draw( GPUProgram * gpuProg )
{
for (int i=0; i
// Set up material properties
groups[i]->material->setMaterial( true, true, gpuProg );
// Render
glBindVertexArray( groups[i]->VAO );
glDrawElements( GL_TRIANGLES, 3 * groups[i]->triangles.size(), GL_UNSIGNED_INT, 0 );
glBindVertexArray( 0 );
groups[i]->material->unsetMaterial( true, true, gpuProg );
}
}
void wfMaterial::setMaterial( bool useTextures, bool useMaterial, GPUProgram * gpuProg )
{
if (useMaterial) {
gpuProg->setVec3( “kd”, vec3( &diffuse[0] ) );
gpuProg->setVec3( “ks”, vec3( &specular[0] ) );
gpuProg->setVec3( “Ia”, vec3( &ambient[0] ) );
gpuProg->setVec3( “Ie”, vec3( &emissive[0] ) );
gpuProg->setFloat( “shininess”, shininess );
} else {
gpuProg->setVec3( “kd”, vec3(1,1,1) );
gpuProg->setVec3( “ks”, vec3(0,0,0) );
gpuProg->setVec3( “Ia”, vec3(0,0,0) );
gpuProg->setVec3( “Ie”, vec3(0,0,0) );
gpuProg->setFloat( “shininess”, 400 );
}
if (useTextures && texmap != NULL) {
glActiveTexture( GL_TEXTURE0 );
glBindTexture( GL_TEXTURE_2D, textureID );
gpuProg->setInt( “objTexture”, 0 );
gpuProg->setInt( “texturing”, 1 );
} else
gpuProg->setInt( “texturing”, 0 );
}
void wfMaterial::unsetMaterial( bool useTextures, bool useMaterial, GPUProgram * gpuProg )
{
return;
if (useTextures && texmap != NULL) {
// Free texture unit 0
glActiveTexture( GL_TEXTURE0 );
glBindTexture( GL_TEXTURE_2D, 0 );
}
gpuProg->setInt( “texturing”, 0 );
glDisable(GL_BLEND);
}
void wfMaterial::storeTexture( TextureMode textureMode )
{
// Register it with OpenGL
glActiveTexture( GL_TEXTURE0 );
glBindTexture( GL_TEXTURE_2D, textureID );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
// set texture lookup mode
if (textureMode == NEAREST) {
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
} else if (textureMode == LINEAR) {
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
} else if (textureMode == MIPMAP_NEAREST) {
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST );
} else if (textureMode == MIPMAP_LINEAR) {
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
} else {
cerr << "Unknown texture mode: " << textureMode << endl;
exit(1);
}
// Store the texture
glTexImage2D( GL_TEXTURE_2D, 0, (hasAlpha ? GL_RGBA : GL_RGB), width, height, 0,
(hasAlpha ? GL_RGBA : GL_RGB), GL_UNSIGNED_BYTE, texmap );
// Build mipmaps
glGenerateMipmap( GL_TEXTURE_2D );
}
/* Initialize the textures by assigning each an OpenGL ID and storing
* each with OpenGL.
*/
void wfModel::initTextures( TextureMode textureMode )
{
// Assign IDs to any textures without IDs
for (int i=0; i
glGenTextures(1, &(groups[i]->material->textureID) );
}
// Store the textures
for (int i=0; i
groups[i]->material->storeTexture( textureMode );
}
/* Read a texture from a P6 PPM file
*/
unsigned char *wfMaterial::readP6( char *filename )
{
char buffer[1000];
int i, xdim, ydim;
unsigned char *a, *b, *pa, *pb;
FILE *f = fopen( filename, “r” );
if (!f) {
cerr << "Open of `" << filename << "' failed.\n";
exit(1);
}
// first line
do {
i = 0;
do
fread(&buffer[i],1,1,f);
while (buffer[i++] != '\n');
} while (buffer[0] == '#');
if (strncmp( buffer, "P6", 2 ) != 0) {
cerr << filename << " is not a P6 file.\n";
exit(1);
}
// second line
do {
i = 0;
do
fread(&buffer[i],1,1,f);
while (buffer[i++] != '\n');
} while (buffer[0] == '#');
buffer[i] = '\0';
sscanf( buffer, "%d %d", &xdim, &ydim );
width = xdim;
height = ydim;
// third line
do {
i = 0;
do
fread(&buffer[i],1,1,f);
while (buffer[i++] != '\n');
} while (buffer[0] == '#');
if (strncmp( buffer, "255", 3 ) != 0) {
cerr << filename << " is not a 24-bit file.\n";
exit(1);
}
// read the data (stored top-to-bottom, left-to-right)
a = new unsigned char[ xdim * ydim * 3 ];
fread( a, xdim*ydim*3, 1, f );
// flip the image vertically (stored bottom-to-top, left-to-right)
b = new unsigned char[ xdim * ydim * 3 ];
for (int i=0; i
#endif
#define PNG_BYTES_TO_CHECK 8
unsigned char *wfMaterial::readPNG( char *filename )
{
unsigned char *b;
#ifndef HAVE_PNG
cerr << "Trying to read PNG file \"" << filename << "\", but the program wasn't compiled with -DHAVE_PNG." << endl;
exit(-1);
return b;
#else
png_structp png_ptr;
png_infop info_ptr;
unsigned int sig_read = 0;
int bit_depth, color_type, interlace_type;
char header[PNG_BYTES_TO_CHECK];
// Open file
FILE *fp = fopen(filename, "rb");
if (!fp) {
cerr << "Can't open PNG texture file '" << filename << "'." << endl;
exit(-1);
}
// Check header
fread( header, 1, PNG_BYTES_TO_CHECK, fp );
bool is_png = !png_sig_cmp( (png_byte*) &header[0], 0, PNG_BYTES_TO_CHECK);
if (!is_png) {
cerr << "Texture file '" << filename << "' is not in PNG format." << endl;
exit(-1);
}
/* Create and initialize the png_struct with the desired error handler
* functions. If you want to use the default stderr and longjump method,
* you can supply NULL for the last three parameters. We also supply the
* the compiler header file version, so that we know if the application
* was compiled with a compatible version of the library. REQUIRED
*/
png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL );
if (png_ptr == NULL) {
cerr << "Can't initialize PNG file for reading: " << filename << endl;
fclose(fp);
exit(-1);
}
/* Allocate/initialize the memory for image information. REQUIRED. */
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL)
{
fclose(fp);
png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
cerr << "Can't allocate memory to read PNG file: " << filename << endl;
exit(-1);
}
/* Set error handling if you are using the setjmp/longjmp method (this is
* the normal method of doing things with libpng). REQUIRED unless you
* set up your own error handlers in the png_create_read_struct() earlier.
*/
if (setjmp(png_jmpbuf(png_ptr))) {
/* Free all of the memory associated with the png_ptr and info_ptr */
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
fclose(fp);
/* If we get here, we had a problem reading the file */
cerr << "Exception occurred while reading PNG file: " << filename << endl;
exit(-1);
}
/* Set up the input control if you are using standard C streams */
png_init_io(png_ptr, fp);
/* If we have already read some of the signature */
png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK);
// Warning: the following does NOT convert grey to RGB:
png_read_png( png_ptr, info_ptr, PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND, NULL );
// Store in texmap
int numChannels = png_get_channels(png_ptr, info_ptr);
if (png_get_bit_depth(png_ptr, info_ptr) != 8) {
cerr << "Can't handle PNG files with bit depth other than 8. '" << filename
<< "' has " << png_get_bit_depth(png_ptr, info_ptr) << " bits per pixel." << endl;
exit(-1);
}
width = png_get_image_width(png_ptr,info_ptr);
height = png_get_image_height(png_ptr,info_ptr);
int imageSize;
if (numChannels == 4)
imageSize = 4 * width * height;
else
imageSize = 3 * width * height;
b = pb = new unsigned char[ imageSize ];
for (int r=(int)info_ptr->height – 1; r >= 0; r–) {
png_bytep row = info_ptr->row_pointers[r];
int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
for (int c=0; c < rowbytes; c++)
switch (numChannels) {
case 1:
*(pb)++ = row[c];
*(pb)++ = row[c];
*(pb)++ = row[c];
break;
case 2:
cerr << "Can't handle a two-channel PNG file: " << filename << endl;
exit(-1);
break;
case 3:
case 4:
*(pb)++ = row[c];
break;
}
}
hasAlpha = (numChannels == 4);
// Clean up PNG stuff
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
fclose(fp);
return b;
#endif
}