/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. ### File Specific: main-programmer: Benjamin Grauer co-programmer: ... TGA-code: borrowed from nehe-Tutorials */ #include "material.h" /** \brief creates a default Material with no Name normally you call this to create a material List (for an obj-file) and then append with addMaterial() */ Material::Material() { init(); setName (""); } /** \brief creates a Material. \param mtlName Name of the Material to be added to the Material List */ Material::Material (char* mtlName) { init(); setName (mtlName); } /** \brief deletes a Material */ Material::~Material() { if (name) delete []name; if (diffuseTextureSet) glDeleteTextures (1, &diffuseTexture); if (verbose >= 2) printf ("delete Material %s.\n", name); if (nextMat != NULL) delete nextMat; } /** \brief adds a new Material to the List. this Function will append a new Material to the end of a Material List. \param mtlName The name of the Material to be added. */ Material* Material::addMaterial(char* mtlName) { if (verbose >=2) printf ("adding Material %s.\n", mtlName); Material* newMat = new Material(mtlName); Material* tmpMat = this; while (tmpMat->nextMat != NULL) { tmpMat = tmpMat->nextMat; } tmpMat->nextMat = newMat; return newMat; } /** \brief initializes a new Material with its default Values */ void Material::init(void) { if (verbose >= 3) printf ("initializing new Material.\n"); nextMat = NULL; setIllum(1); setDiffuse(0,0,0); setAmbient(0,0,0); setSpecular(.5,.5,.5); setShininess(2.0); setTransparency(0.0); diffuseTextureSet = false; ambientTextureSet = false; specularTextureSet = false; } /** \brief Search for a Material called mtlName \param mtlName the Name of the Material to search for \returns Material named mtlName if it is found. NULL otherwise. */ Material* Material::search (char* mtlName) { if (verbose >=3) printf ("Searching for material %s", mtlName); Material* searcher = this; while (searcher != NULL) { if (verbose >= 3) printf ("."); if (!strcmp (searcher->getName(), mtlName)) { if (verbose >= 3) printf ("found.\n"); return searcher; } searcher = searcher->nextMat; } if (verbose >=3) printf ("not found\n"); return NULL; } /** \brief sets the material with which the following Faces will be painted */ bool Material::select (void) { // setting diffuse color // glColor3f (diffuse[0], diffuse[1], diffuse[2]); glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse); // setting ambient color glMaterialfv(GL_FRONT, GL_AMBIENT, ambient); // setting up Sprecular glMaterialfv(GL_FRONT, GL_SPECULAR, specular); // setting up Shininess glMaterialf(GL_FRONT, GL_SHININESS, shininess); // setting illumination Model if (illumModel == 1) glShadeModel(GL_FLAT); else if (illumModel >= 2) glShadeModel(GL_SMOOTH); if (diffuseTextureSet) glBindTexture(GL_TEXTURE_2D, diffuseTexture); else glBindTexture(GL_TEXTURE_2D, 0); } /** \brief Set the Name of the Material. (Important for searching) \param mtlName the Name of the Material to be set. */ void Material::setName (char* mtlName) { if (verbose >= 3) printf("setting Material Name to %s.\n", mtlName); name = new char [strlen(mtlName)]; strcpy(name, mtlName); // printf ("adding new Material: %s, %p\n", this->getName(), this); } /** \returns The Name of The Material */ char* Material::getName (void) { return name; } /** \brief Sets the Material Illumination Model. \brief illu illumination Model in int form */ void Material::setIllum (int illum) { if (verbose >= 3) printf("setting illumModel of Material %s to %i", name, illum); illumModel = illum; // printf ("setting illumModel to: %i\n", illumModel); } /** \brief Sets the Material Illumination Model. \brief illu illumination Model in char* form */void Material::setIllum (char* illum) { setIllum (atoi(illum)); } /** \brief Sets the Material Diffuse Color. \param r Red Color Channel. \param g Green Color Channel. \param b Blue Color Channel. */ void Material::setDiffuse (float r, float g, float b) { if (verbose >= 3) printf ("setting Diffuse Color of Material %s to r=%f g=%f b=%f.\n", name, r, g, b); diffuse[0] = r; diffuse[1] = g; diffuse[2] = b; diffuse[3] = 1.0; } /** \brief Sets the Material Diffuse Color. \param rgb The red, green, blue channel in char format (with spaces between them) */ void Material::setDiffuse (char* rgb) { char r[20],g[20],b[20]; sscanf (rgb, "%s %s %s", r, g, b); setDiffuse (atof(r), atof(g), atof(b)); } /** \brief Sets the Material Ambient Color. \param r Red Color Channel. \param g Green Color Channel. \param b Blue Color Channel. */ void Material::setAmbient (float r, float g, float b) { if (verbose >=3) printf ("setting Ambient Color of Material %s to r=%f g=%f b=%f.\n", name, r, g, b); ambient[0] = r; ambient[1] = g; ambient[2] = b; ambient[3] = 1.0; } /** \brief Sets the Material Ambient Color. \param rgb The red, green, blue channel in char format (with spaces between them) */ void Material::setAmbient (char* rgb) { char r[20],g[20],b[20]; sscanf (rgb, "%s %s %s", r, g, b); setAmbient (atof(r), atof(g), atof(b)); } /** \brief Sets the Material Specular Color. \param r Red Color Channel. \param g Green Color Channel. \param b Blue Color Channel. */ void Material::setSpecular (float r, float g, float b) { if (verbose >= 3) printf ("setting Specular Color of Material %s to r=%f g=%f b=%f.\n", name, r, g, b); specular[0] = r; specular[1] = g; specular[2] = b; specular[3] = 1.0; } /** \brief Sets the Material Specular Color. \param rgb The red, green, blue channel in char format (with spaces between them) */ void Material::setSpecular (char* rgb) { char r[20],g[20],b[20]; sscanf (rgb, "%s %s %s", r, g, b); setSpecular (atof(r), atof(g), atof(b)); } /** \brief Sets the Material Shininess. \param shini stes the Shininess from float. */ void Material::setShininess (float shini) { shininess = shini; } /** \brief Sets the Material Shininess. \param shini stes the Shininess from char*. */ void Material::setShininess (char* shini) { setShininess (atof(shini)); } /** \brief Sets the Material Transparency. \param trans stes the Transparency from int. */ void Material::setTransparency (float trans) { if (verbose >= 3) printf ("setting Transparency of Material %s to %f.\n", name, trans); transparency = trans; } /** \brief Sets the Material Transparency. \param trans stes the Transparency from char*. */ void Material::setTransparency (char* trans) { char tr[20]; sscanf (trans, "%s", tr); setTransparency (atof(tr)); } // MAPPING // /** \brief Sets the Materials Diffuse Map \param dMap the Name of the Image to Use */ void Material::setDiffuseMap(char* dMap) { if (verbose>=2) printf ("setting Diffuse Map %s\n", dMap); // diffuseTextureSet = loadBMP(dMap, &diffuseTexture); diffuseTextureSet = loadImage(dMap, &diffuseTexture); } /** \brief Sets the Materials Ambient Map \param aMap the Name of the Image to Use */ void Material::setAmbientMap(char* aMap) { SDL_Surface* ambientMap; } /** \brief Sets the Materials Specular Map \param sMap the Name of the Image to Use */ void Material::setSpecularMap(char* sMap) { SDL_Surface* specularMap; } /** \brief Sets the Materials Bumpiness \param bump the Name of the Image to Use */ void Material::setBump(char* bump) { } bool Material::loadTexToGL (Image* pImage, GLuint* texture) { glGenTextures(1, texture); glBindTexture(GL_TEXTURE_2D, *texture); /* not Working, and not needed. glTexImage2D( GL_TEXTURE_2D, 0, 3, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, map->pixels ); */ gluBuild2DMipmaps(GL_TEXTURE_2D, 3, pImage->width, pImage->height, GL_RGB, GL_UNSIGNED_BYTE, pImage->data); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR_MIPMAP_LINEAR); } /** \brief Makes the Programm ready to Read-in a texture-File 1. Checks what type of Image should be imported 2. ToDO: Checks where to find the Image */ bool Material::loadImage(char* imageName, GLuint* texture) { if (!strncmp(imageName+strlen(imageName)-4, ".bmp", 4)) { if (verbose >=2) printf ("Requested bmp-image. Trying to Import.\n"); return loadBMP(imageName, texture); } else if (!strncmp(imageName+strlen(imageName)-4, ".jpg", 4) || !strncmp(imageName+strlen(imageName)-5, ".jpg", 5)) { if (verbose >=2) printf ("Requested jpeg-image. Trying to Import\n"); return loadJPG(imageName, texture); } else if (!strncmp(imageName+strlen(imageName)-4, ".tga", 4)) { if (verbose >=2) printf ("Requested tga-image. Trying to Import\n"); return loadTGA(imageName, texture); } else if (!strncmp(imageName+strlen(imageName)-4, ".png", 4)) { if (verbose >=2) printf ("Requested png-image. Trying to Import\n"); return loadPNG(imageName, texture); } else { if (verbose >=1) printf ("Requested Image was not recognized in its type. (Maybe a type-Cast-error.)\n FileName: %s", imageName); return false; } } /** \brief reads in a Windows BMP-file, and imports it to openGL. \param bmpName The name of the Image to load. \param texture A pointer to the Texture which should be read to. */ bool Material::loadBMP (char* bmpName, GLuint* texture) { Image* pImage = new Image; FILE *file; unsigned long size; // size of the image in bytes. unsigned long i; // standard counter. unsigned short int planes; // number of planes in image (must be 1) unsigned short int bpp; // number of bits per pixel (must be 24) GLuint temp; // temporary color storage for bgr-rgb conversion. // make sure the file is there. if ((file = fopen(bmpName, "rb"))==NULL) { if (verbose >=1) printf("File Not Found : %s\n",bmpName); return false; } // seek through the bmp header, up to the width/height: fseek(file, 18, SEEK_CUR); // read the width if ((i = fread(&pImage->width, 4, 1, file)) != 1) { if (verbose >=1) printf("Error reading width from %s.\n", bmpName); return false; } // read the height if ((i = fread(&pImage->height, 4, 1, file)) != 1) { if (verbose>=1) printf("Error reading height from %s.\n", bmpName); return false; } // calculate the size (assuming 24 bits or 3 bytes per pixel). size = pImage->width * pImage->height * 3; // read the planes if ((fread(&planes, 2, 1, file)) != 1) { if (verbose>=1) printf("Error reading planes from %s.\n", bmpName); return false; } if (planes != 1) { if (verbose>=1) printf("Planes from %s is not 1: %u\n", bmpName, planes); return false; } // read the bpp if ((i = fread(&bpp, 2, 1, file)) != 1) { if (verbose>=1) printf("Error reading bpp from %s.\n", bmpName); return false; } if (bpp != 24) { if (verbose>=1) printf("Bpp from %s is not 24: %u\n", bmpName, bpp); return false; } // seek past the rest of the bitmap header. fseek(file, 24, SEEK_CUR); // read the data. pImage->data = (GLubyte *) malloc(size); if (pImage->data == NULL) { if (verbose>=1) printf("Error allocating memory for color-corrected image data"); return false; } if ((i = fread(pImage->data, size, 1, file)) != 1) { if (verbose>=1) printf("Error reading image data from %s.\n", bmpName); return false; } fclose(file); // reverse all of the colors. (bgr -> rgb) for (i=0;idata[i]; pImage->data[i] = pImage->data[i+2]; pImage->data[i+2] = temp; } loadTexToGL (pImage, texture); return true; if (pImage) { if (pImage->data) { free(pImage->data); } free(pImage); } } /** \brief reads in a jpg-file \param jpgName the Name of the Image to load \param texture a reference to the Texture to write the image to */ bool Material::loadJPG (char* jpgName, GLuint* texture) { struct jpeg_decompress_struct cinfo; Image *pImage = NULL; FILE *pFile; // Open a file pointer to the jpeg file and check if it was found and opened if((pFile = fopen(jpgName, "rb")) == NULL) { // Display an error message saying the file was not found, then return NULL printf("Unable to load JPG File %s.\n", jpgName); return false; } // Create an error handler jpeg_error_mgr jerr; // Have our compression info object point to the error handler address cinfo.err = jpeg_std_error(&jerr); // Initialize the decompression object jpeg_create_decompress(&cinfo); // Specify the data source (Our file pointer) jpeg_stdio_src(&cinfo, pFile); // Allocate the structure that will hold our eventual jpeg data (must free it!) pImage = (Image*)malloc(sizeof(Image)); // DECOFING // Read in the header of the jpeg file jpeg_read_header(&cinfo, TRUE); // Start to decompress the jpeg file with our compression info jpeg_start_decompress(&cinfo); // Get the image dimensions and row span to read in the pixel data pImage->rowSpan = cinfo.image_width * cinfo.num_components; pImage->width = cinfo.image_width; pImage->height = cinfo.image_height; // Allocate memory for the pixel buffer pImage->data = new unsigned char[pImage->rowSpan * pImage->height]; // Here we use the library's state variable cinfo.output_scanline as the // loop counter, so that we don't have to keep track ourselves. // Create an array of row pointers unsigned char** rowPtr = new unsigned char*[pImage->height]; for (int i = 0; i < pImage->height; i++) rowPtr[i] = &(pImage->data[i*pImage->rowSpan]); // Now comes the juice of our work, here we extract all the pixel data int rowsRead = 0; while (cinfo.output_scanline < cinfo.output_height) { // Read in the current row of pixels and increase the rowsRead count rowsRead += jpeg_read_scanlines(&cinfo, &rowPtr[rowsRead], cinfo.output_height - rowsRead); } // Delete the temporary row pointers delete [] rowPtr; // Finish decompressing the data jpeg_finish_decompress(&cinfo);// decodeJPG(&cinfo, pImage); // This releases all the stored memory for reading and decoding the jpeg jpeg_destroy_decompress(&cinfo); // Close the file pointer that opened the file fclose(pFile); if(pImage == NULL) exit(0); loadTexToGL (pImage, texture); if (pImage) { if (pImage->data) { free(pImage->data); } free(pImage); } return true; } /** \brief reads in a tga-file \param tgaName the Name of the Image to load \param texture a reference to the Texture to write the image to */ bool Material::loadTGA(const char * tgaName, GLuint* texture) { typedef struct { GLubyte Header[12]; } TGAHeader; TGAHeader tgaHeader; GLubyte uTGAcompare[12] = {0,0,2, 0,0,0,0,0,0,0,0,0}; // Uncompressed TGA Header GLubyte cTGAcompare[12] = {0,0,10,0,0,0,0,0,0,0,0,0}; // Compressed TGA Header FILE * fTGA; fTGA = fopen(tgaName, "rb"); if(fTGA == NULL) { printf("Error could not open texture file: %s\n", tgaName); return false; } if(fread(&tgaHeader, sizeof(TGAHeader), 1, fTGA) == 0) { printf("Error could not read file header of %s\n", tgaName); if(fTGA != NULL) { fclose(fTGA); } return false; } if(memcmp(uTGAcompare, &tgaHeader, sizeof(TGAHeader)) == 0) { loadUncompressedTGA(tgaName, fTGA, texture); if (fTGA) fclose (fTGA); } else if(memcmp(cTGAcompare, &tgaHeader, sizeof(TGAHeader)) == 0) { loadCompressedTGA(tgaName, fTGA, texture); if (fTGA) fclose (fTGA); } else { printf("Error TGA file be type 2 or type 10\n"); if (fTGA) fclose(fTGA); return false; } return true; } /** \brief reads in an uncompressed tga-file \param filename the Name of the Image to load \param fTGA a Pointer to a File, that should be read \param texture a reference to the Texture to write the image to */ bool Material::loadUncompressedTGA(const char * filename, FILE * fTGA, GLuint* texture) { GLubyte header[6]; // First 6 Useful Bytes From The Header GLuint bytesPerPixel; // Holds Number Of Bytes Per Pixel Used In The TGA File GLuint imageSize; // Used To Store The Image Size When Setting Aside Ram GLuint temp; // Temporary Variable GLuint type; GLuint Height; // Height of Image GLuint Width; // Width of Image GLuint Bpp; // Bits Per Pixel Image* pImage = new Image; GLuint cswap; if(fread(header, sizeof(header), 1, fTGA) == 0) { printf("Error could not read info header\n"); return false; } Width = pImage->width = header[1] * 256 + header[0]; Height = pImage->height = header[3] * 256 + header[2]; Bpp = pImage->bpp = header[4]; // Make sure all information is valid if((pImage->width <= 0) || (pImage->height <= 0) || ((pImage->bpp != 24) && (pImage->bpp !=32))) { printf("Error invalid texture information\n"); return false; } if(pImage->bpp == 24) { pImage->type = GL_RGB; } else { pImage->type = GL_RGBA; } bytesPerPixel = (Bpp / 8); imageSize = (bytesPerPixel * Width * Height); pImage->data = (GLubyte*) malloc(imageSize); if(pImage->data == NULL) { printf("Error could not allocate memory for image\n"); return false; } if(fread(pImage->data, 1, imageSize, fTGA) != imageSize) { printf("Error could not read image data\n"); if(pImage->data != NULL) { free(pImage->data); } return false; } for(cswap = 0; cswap < (int)imageSize; cswap += bytesPerPixel) { pImage->data[cswap] ^= pImage->data[cswap+2] ^= pImage->data[cswap] ^= pImage->data[cswap+2]; } loadTexToGL (pImage, texture); return true; } /** \brief reads in a compressed tga-file \param filename the Name of the Image to load \param fTGA a Pointer to a File, that should be read \param texture a reference to the Texture to write the image to */ bool Material::loadCompressedTGA(const char * filename, FILE * fTGA, GLuint* texture) { GLubyte header[6]; // First 6 Useful Bytes From The Header GLuint bytesPerPixel; // Holds Number Of Bytes Per Pixel Used In The TGA File GLuint imageSize; // Used To Store The Image Size When Setting Aside Ram GLuint temp; // Temporary Variable GLuint type; GLuint Height; // Height of Image GLuint Width; // Width of Image GLuint Bpp; // Bits Per Pixel Image* pImage = new Image; if(fread(header, sizeof(header), 1, fTGA) == 0) { printf("Error could not read info header\n"); return false; } Width = pImage->width = header[1] * 256 + header[0]; Height = pImage->height = header[3] * 256 + header[2]; Bpp = pImage->bpp = header[4]; GLuint pixelcount = Height * Width; GLuint currentpixel = 0; GLuint currentbyte = 0; GLubyte * colorbuffer = (GLubyte *)malloc(bytesPerPixel); //Make sure all pImage info is ok if((pImage->width <= 0) || (pImage->height <= 0) || ((pImage->bpp != 24) && (pImage->bpp !=32))) { printf("Error Invalid pImage information\n"); return false; } bytesPerPixel = (Bpp / 8); imageSize = (bytesPerPixel * Width * Height); pImage->data = (GLubyte*) malloc(imageSize); if(pImage->data == NULL) { printf("Error could not allocate memory for image\n"); return false; } do { GLubyte chunkheader = 0; if(fread(&chunkheader, sizeof(GLubyte), 1, fTGA) == 0) { printf("Error could not read RLE header\n"); if(pImage->data != NULL) { free(pImage->data); } return false; } // If the ehader is < 128, it means the that is the number of RAW color packets minus 1 if(chunkheader < 128) { short counter; chunkheader++; // Read RAW color values for(counter = 0; counter < chunkheader; counter++) { // Try to read 1 pixel if(fread(colorbuffer, 1, bytesPerPixel, fTGA) != bytesPerPixel) { printf("Error could not read image data\n"); if(colorbuffer != NULL) { free(colorbuffer); } if(pImage->data != NULL) { free(pImage->data); } return false; } // write to memory // Flip R and B vcolor values around in the process pImage->data[currentbyte ] = colorbuffer[2]; pImage->data[currentbyte + 1] = colorbuffer[1]; pImage->data[currentbyte + 2] = colorbuffer[0]; if(bytesPerPixel == 4) // if its a 32 bpp image { pImage->data[currentbyte + 3] = colorbuffer[3];// copy the 4th byte } currentbyte += bytesPerPixel; currentpixel++; // Make sure we haven't read too many pixels if(currentpixel > pixelcount) { printf("Error too many pixels read\n"); if(colorbuffer != NULL) { free(colorbuffer); } if(pImage->data != NULL) { free(pImage->data); } return false; } } } // chunkheader > 128 RLE data, next color reapeated chunkheader - 127 times else { short counter; chunkheader -= 127; // Subteact 127 to get rid of the ID bit if(fread(colorbuffer, 1, bytesPerPixel, fTGA) != bytesPerPixel) // Attempt to read following color values { printf("Error could not read from file"); if(colorbuffer != NULL) { free(colorbuffer); } if(pImage->data != NULL) { free(pImage->data); } return false; } for(counter = 0; counter < chunkheader; counter++) //copy the color into the image data as many times as dictated { // switch R and B bytes areound while copying pImage->data[currentbyte ] = colorbuffer[2]; pImage->data[currentbyte + 1] = colorbuffer[1]; pImage->data[currentbyte + 2] = colorbuffer[0]; if(bytesPerPixel == 4) { pImage->data[currentbyte + 3] = colorbuffer[3]; } currentbyte += bytesPerPixel; currentpixel++; if(currentpixel > pixelcount) { printf("Error too many pixels read\n"); if(colorbuffer != NULL) { free(colorbuffer); } if(pImage->data != NULL) { free(pImage->data); } return false; } } } } while(currentpixel < pixelcount); // Loop while there are still pixels left loadTexToGL (pImage, texture); return true; } /* static int ST_is_power_of_two(unsigned int number) { return (number & (number - 1)) == 0; } */ /** \brief reads in a png-file \param pngName the Name of the Image to load \param texture a reference to the Texture to write the image to */ bool Material::loadPNG(const char* pngName, GLuint* texture) { Image* pImage = new Image; FILE *PNG_file = fopen(pngName, "rb"); if (PNG_file == NULL) { return 0; } GLubyte PNG_header[8]; fread(PNG_header, 1, 8, PNG_file); if (png_sig_cmp(PNG_header, 0, 8) != 0) { if (verbose >=2) printf ("Not Recognized as a pngFile\n"); fclose (PNG_file); return 0; } png_structp PNG_reader = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (PNG_reader == NULL) { fclose(PNG_file); return 0; } png_infop PNG_info = png_create_info_struct(PNG_reader); if (PNG_info == NULL) { png_destroy_read_struct(&PNG_reader, NULL, NULL); fclose(PNG_file); return 0; } png_infop PNG_end_info = png_create_info_struct(PNG_reader); if (PNG_end_info == NULL) { png_destroy_read_struct(&PNG_reader, &PNG_info, NULL); fclose(PNG_file); return 0; } if (setjmp(png_jmpbuf(PNG_reader))) { png_destroy_read_struct(&PNG_reader, &PNG_info, &PNG_end_info); fclose(PNG_file); return (0); } png_init_io(PNG_reader, PNG_file); png_set_sig_bytes(PNG_reader, 8); png_read_info(PNG_reader, PNG_info); pImage->width = png_get_image_width(PNG_reader, PNG_info); pImage->height = png_get_image_height(PNG_reader, PNG_info); png_uint_32 bit_depth, color_type; bit_depth = png_get_bit_depth(PNG_reader, PNG_info); color_type = png_get_color_type(PNG_reader, PNG_info); if (color_type == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(PNG_reader); } if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { png_set_gray_1_2_4_to_8(PNG_reader); } if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { png_set_gray_to_rgb(PNG_reader); } if (png_get_valid(PNG_reader, PNG_info, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(PNG_reader); } else { png_set_filler(PNG_reader, 0xff, PNG_FILLER_AFTER); } if (bit_depth == 16) { png_set_strip_16(PNG_reader); } png_read_update_info(PNG_reader, PNG_info); pImage->data = (png_byte*)malloc(4 * pImage->width * pImage->height); png_byte** PNG_rows = (png_byte**)malloc(pImage->height * sizeof(png_byte*)); unsigned int row; for (row = 0; row < pImage->height; ++row) { PNG_rows[pImage->height - 1 - row] = pImage->data + (row * 4 * pImage->width); } png_read_image(PNG_reader, PNG_rows); free(PNG_rows); png_destroy_read_struct(&PNG_reader, &PNG_info, &PNG_end_info); fclose(PNG_file); /* if (!ST_is_power_of_two(pImage->width) || !ST_is_power_of_two(pImage->height)) { free(pImage->data); return 0; } */ loadTexToGL (pImage, texture); free(pImage->data); return true; }