Sun Mar 20, 2011 10:26 pm
/**************************************************
* TextureTorus.c
*
* Program to demonstrate how to attach a texture
* map to a torus. This is the LightTorus program,
* modified to display a texture map.
*
* The torus can be viewed in wireframe or in "filled
* polygon" mode.
*
* The triangles or quadrilaterals are wrapped around the
* torus in a single long strip. See figure 13 Chapter I
* in the book mentioned below.
*
*
* Author: Samuel R. Buss
*
* Software accompanying the book
* 3D Computer Graphics: A Mathematical Introduction with OpenGL,
* by S. Buss, Cambridge University Press, 2003.
*
* Software is "as-is" and carries no warranty. It may be used without
* restriction, but if you modify it, please change the filenames to
* prevent confusion between different versions.
* Bug reports: Sam Buss, [email protected].
* Web page: http://math.ucsd.edu/~sbuss/MathCG
*
* USAGES: There are a number of keyboard commands that control
* the animation. They must be typed into the graphics window,
* and are listed below:
*
* TURN THE TEXTURE OFF AND ON:
* Press "t" to toggle the texture off and on.
*
* CONTROLLING RESOLUTION OF THE TORUS MESH
* Press "W" to increase the number wraps.
* Press "w" to decrease the number wraps.
* Press "N" to increase the number of segments per wrap.
* Press "n" to decrease the number of segments per wrap.
* Press "q" to toggle between quadrangles and triangles.
*
* CONTROLLING THE ANIMATION:
* Press the "a" key to toggle the animation off and on.
* Press the "s" key to perform a single step of the animation.
* The left and right arrow keys controls the
* rate of rotation around the y-axis.
* The up and down arrow keys increase and decrease the rate of
* rotation around the x-axis. In order to reverse rotational
* direction you must zero or reset the torus ("0" or "r").
* Press the "r" key to reset the torus back to initial
* position, with no rotation.
* Press "0" (zero) to zero the rotation rates.
*
* CONTROLLING LIGHTS
* Press '0' or '1' to toggle the first or second light off and on.
* Press 'f' to toggle between flat and smooth shading.
* Press 'l' to toggle local modes on and off (local viewer and positional light,
* or non-local viewer and directional light).
*
* COMMANDS SHOWING OPENGL FEATURES:
* Pressing "p" toggles between wireframe and polygon mode.
* Pressing "f" key toggles between flat and smooth shading.
*
**/
#include <stdlib.h>
#include <math.h>
#include <limits.h>
#include "RgbImage.h"
#include "TextureTorus.h"
#include <GL/glut.h> // OpenGL Graphics Utility Library
const float PI2 = 2.0f*3.1415926535;
GLenum runMode = GL_TRUE;
GLenum shadeModel = GL_FLAT; // Toggles between GL_FLAT and GL_SMOOTH
GLenum polygonMode = GL_LINE; // Toggles between GL_LINE and GL_FILL
// Variables controlling the animation
float RotX = 0.0f; // Rotational position around x-axis
float RotY = 0.0f; // Rotational position around y-axis
float RotIncrementX = 0.0; // Rotational increment, x-axis
float RotIncrementY = 0.0; // Rotational increment, y-axis
const float RotIncFactor = 1.5; // Factor change in rot rate per key stroke
// Variables controlling the fineness of the polygonal mesh
int NumWraps = 10;
int NumPerWrap = 8;
// Variables controlling the size of the torus
float MajorRadius = 3.0;
float MinorRadius = 1.0;
// Mode flags
int QuadMode = 1; // Quad/Triangle toggling
GLenum LocalMode = GL_TRUE; // Local viewer/non-local viewer mode
int Light0Flag = 1; // Is light #0 on?
int Light1Flag = 1; // Is light #1 on?
// Lighting values
float ambientLight[4] = {0.6, 0.6, 0.6, 1.0};
float Lt0amb[4] = {0.2, 0.2, 0.2, 1.0};
float Lt0diff[4] = {1.0, 1.0, 1.0, 1.0};
float Lt0spec[4] = {1.0, 1.0, 1.0, 1.0};
float Lt0pos[4] = {1.7f*(MajorRadius+MinorRadius), 0.0f, 0.0f, 1.0f};
float Lt1amb[4] = {0.2, 0.2, 0.2, 1.0};
float Lt1diff[4] = {0.7, 0.7, 0.7, 1.0};
float Lt1spec[4] = {1.0, 1.0, 1.0, 1.0};
float Lt1pos[4] = {0.0f, 1.2f*(MajorRadius+MinorRadius), 0.0f, 1.0f};
// Material values
float Noemit[4] = {0.0, 0.0, 0.0, 1.0};
float Matspec[4] = {0.3, 0.3, 0.3, 1.0};
float Matnonspec[4] = {0.4, 0.4, 0.4, 1.0};
float Matshiny = 16.0;
// A texture
int TextureWrapVert=6;
int TextureWrapHoriz=6;
bool textureFlag = true;
// glutKeyboardFunc is called below to set this function to handle
// all "normal" key presses.
void myKeyboardFunc( unsigned char key, int x, int y )
{
switch ( key ) {
case 'a':
runMode = !runMode;
break;
case 's':
runMode = GL_TRUE;
updateScene();
runMode = GL_FALSE;
break;
case 27: // Escape key
exit(1);
case 'r': // Reset the animation (resets everything)
ResetAnimation();
break;
case 'z': // Zero the rotation rates
ZeroRotation();
break;
case 'f': // Shade mode toggles from flat to smooth
ShadeModelToggle();
break;
case 'p': // Polygon mode toggles between fill and line
FillModeToggle();
break;
case 'w': // Decrement number of wraps around torus
WrapLess();
break;
case 'W': // Increment number of wraps around torus
WrapMore();
break;
case 'n': // Decrement number of polys per wrap
NumPerWrapLess();
break;
case 'N': // Increment number of polys per wrap
NumPerWrapMore();
break;
case 'q': // Toggle between triangles and Quadrilaterals
QuadTriangleToggle();
break;
case 'l': // Toggle between local and non-local viewer
LocalToggle();
break;
case '0': // Toggle light #0 on and off
Light0Toggle();
break;
case '1': // Toggle light #1 on and off
Light1Toggle();
break;
case 't':
textureFlag = !textureFlag;
break;
}
}
// glutSpecialFunc is called below to set this function to handle
// all "special" key presses. See glut.h for the names of
// special keys.
void mySpecialKeyFunc( int key, int x, int y )
{
switch ( key ) {
case GLUT_KEY_UP:
// Either increase upward rotation, or slow downward rotation
KeyUp();
break;
case GLUT_KEY_DOWN:
// Either increase downwardward rotation, or slow upward rotation
KeyDown();
break;
case GLUT_KEY_LEFT:
// Either increase left rotation, or slow down rightward rotation.
KeyLeft();
break;
case GLUT_KEY_RIGHT:
// Either increase right rotation, or slow down leftward rotation.
KeyRight();
break;
}
}
// The routines below are coded so that the only way to change from
// one direction of rotation to the opposite direction is to first
// reset the animation,
void KeyUp() {
if ( RotIncrementX == 0.0 ) {
RotIncrementX = -0.1; // Initially, one-tenth degree rotation per update
}
else if ( RotIncrementX < 0.0f) {
RotIncrementX *= RotIncFactor;
}
else {
RotIncrementX /= RotIncFactor;
}
}
void KeyDown() {
if ( RotIncrementX == 0.0 ) {
RotIncrementX = 0.1; // Initially, one-tenth degree rotation per update
}
else if ( RotIncrementX > 0.0f) {
RotIncrementX *= RotIncFactor;
}
else {
RotIncrementX /= RotIncFactor;
}
}
void KeyLeft() {
if ( RotIncrementY == 0.0 ) {
RotIncrementY = -0.1; // Initially, one-tenth degree rotation per update
}
else if ( RotIncrementY < 0.0) {
RotIncrementY *= RotIncFactor;
}
else {
RotIncrementY /= RotIncFactor;
}
}
void KeyRight()
{
if ( RotIncrementY == 0.0 ) {
RotIncrementY = 0.1; // Initially, one-tenth degree rotation per update
}
else if ( RotIncrementY > 0.0) {
RotIncrementY *= RotIncFactor;
}
else {
RotIncrementY /= RotIncFactor;
}
}
// Resets position and sets rotation rate back to zero.
void ResetAnimation() {
RotX = RotY = RotIncrementX = RotIncrementY = 0.0;
}
// Sets rotation rates back to zero.
void ZeroRotation() {
RotIncrementX = RotIncrementY = 0.0;
}
// Toggle between smooth and flat shading
void ShadeModelToggle() {
if ( shadeModel == GL_FLAT ) {
shadeModel = GL_SMOOTH;
}
else {
shadeModel = GL_FLAT;
}
}
// Toggle between line mode and fill mode for polygons.
void FillModeToggle() {
if ( polygonMode == GL_LINE ) {
polygonMode = GL_FILL;
}
else {
polygonMode = GL_LINE;
}
}
// Toggle quadrilaterial and triangle mode
void QuadTriangleToggle() {
QuadMode = 1-QuadMode;
}
// Toggle from local to global mode
void LocalToggle() {
LocalMode = !LocalMode;
if ( LocalMode ) {
Lt0pos[3] = Lt1pos[3] = 1.0; // Put lights back at finite location.
}
else {
Lt0pos[3] = Lt1pos[3] = 0.0; // Put lights at infinity too.
}
}
// The next two routines toggle the lights on and off
void Light0Toggle() {
Light0Flag = 1-Light0Flag;
}
void Light1Toggle() {
Light1Flag = 1-Light1Flag;
}
// Increment number of wraps
void WrapMore() {
NumWraps++;
}
// Decrement number of wraps
void WrapLess() {
if (NumWraps>4) {
NumWraps--;
}
}
// Increment number of segments per wrap
void NumPerWrapMore() {
NumPerWrap++;
}
// Decrement number segments per wrap
void NumPerWrapLess() {
if (NumPerWrap>4) {
NumPerWrap--;
}
}
/*
* issue vertex command for segment number j of wrap number i.
*/
void putVertTexture(int i, int j) {
float wrapFrac = (j%NumPerWrap)/(float)NumPerWrap;
float wrapFracTex = (float)j/(float)NumPerWrap;
float phi = PI2*wrapFrac;
float thetaFrac = ((float)(i%NumWraps)+wrapFracTex)/(float)NumWraps;
float thetaFracTex = ((float)i+wrapFracTex)/(float)NumWraps;
float theta = PI2*thetaFrac;
float sinphi = sin(phi);
float cosphi = cos(phi);
float sintheta = sin(theta);
float costheta = cos(theta);
float y = MinorRadius*sinphi;
float r = MajorRadius + MinorRadius*cosphi;
float x = sintheta*r;
float z = costheta*r;
glTexCoord2f( wrapFracTex*(float)TextureWrapVert, thetaFracTex*(float)TextureWrapHoriz );
glNormal3f(sintheta*cosphi, sinphi, costheta*cosphi);
glVertex3f(x,y,z);
}
void updateScene( void )
{
// Clear the redering window
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glShadeModel( shadeModel ); // Set the shading to flat or smooth.
glPolygonMode(GL_FRONT_AND_BACK, polygonMode); // Set to be "wire" or "solid"
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, LocalMode);
// Set up lights
glDisable( GL_TEXTURE_2D );
if ( Light0Flag==1 || Light1Flag==1 ) {
// Emissive spheres have no other color.
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Noemit);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, Noemit);
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 0.0);
}
if ( Light0Flag==1 ) {
glPushMatrix();
glTranslatef(Lt0pos[0], Lt0pos[1], Lt0pos[2]);
glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, Lt0diff);
glutSolidSphere(0.2,5,5);
glPopMatrix();
glEnable(GL_LIGHT0);
glLightfv(GL_LIGHT0, GL_POSITION, Lt0pos);
}
else {
glDisable(GL_LIGHT0);
}
if ( Light1Flag==1 ) {
glPushMatrix();
glTranslatef(Lt1pos[0], Lt1pos[1], Lt1pos[2]);
glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, Lt1diff);
glutSolidSphere(0.2,5,5);
glPopMatrix();
glEnable(GL_LIGHT1);
glLightfv(GL_LIGHT1, GL_POSITION, Lt1pos);
}
else {
glDisable(GL_LIGHT1);
}
// Torus Materials
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Matnonspec);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, Matspec);
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, Matshiny);
glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, Noemit);
if ( textureFlag ) {
glEnable( GL_TEXTURE_2D );
}
glPushMatrix(); // Save to use again next time.
// Update the orientation of the torus, if the animation is running.
if ( runMode ) {
RotY += RotIncrementY;
if ( fabs(RotY)>360.0 ) {
RotY -= 360.0*((int)(RotY/360.0));
}
RotX += RotIncrementX;
if ( fabs(RotX)>360.0 ) {
RotX -= 360.0*((int)(RotX/360.0));
}
}
// Set the orientation.
glRotatef( RotX, 1.0, 0.0, 0.0);
glRotatef( RotY, 0.0, 1.0, 0.0);
// Draw the torus
for (int i=0; i<NumWraps; i++ ) {
glBegin( QuadMode ? GL_QUAD_STRIP : GL_TRIANGLE_STRIP);
for (int j=0; j<=NumPerWrap; j++) {
putVertTexture(i, j);
putVertTexture(i+1,j);
}
glEnd();
}
// Draw a reference sphere
glDisable( GL_TEXTURE_2D );
glTranslatef( -MajorRadius-MinorRadius-0.3, 0.0, 0.0);
glColor3f( 1.0f, 0.0f, 0.0f );
glutWireSphere( 0.1f, 5, 5 );
glPopMatrix(); // Restore to original matrix as set in resizeWindow()
// Flush the pipeline, swap the buffers
glFlush();
glutSwapBuffers();
}
/*
* Read a texture map from a BMP bitmap file.
*/
void loadTextureFromFile(char *filename)
{
glClearColor (0.0, 0.0, 0.0, 0.0);
glShadeModel(GL_FLAT);
glEnable(GL_DEPTH_TEST);
RgbImage theTexMap( filename );
// Pixel alignment: each row is word aligned (aligned to a 4 byte boundary)
// Therefore, no need to call glPixelStore( GL_UNPACK_ALIGNMENT, ... );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
gluBuild2DMipmaps(GL_TEXTURE_2D, 3,theTexMap.GetNumCols(), theTexMap.GetNumRows(),
GL_RGB, GL_UNSIGNED_BYTE, theTexMap.ImageData() );
}
/*
* Initialize OpenGL: Lights, rendering modes, and textures.
*/
void initRendering()
{
glEnable( GL_DEPTH_TEST );
glEnable(GL_LIGHTING); // Enable lighting calculations
glEnable(GL_LIGHT0); // Turn on lights (unnecessary here, since also in Animate()
glEnable(GL_LIGHT1);
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight); // Ambient light
// Light 0 (Position is set in updateScene)
glLightfv(GL_LIGHT0, GL_AMBIENT, Lt0amb);
glLightfv(GL_LIGHT0, GL_DIFFUSE, Lt0diff);
glLightfv(GL_LIGHT0, GL_SPECULAR, Lt0spec);
// Light 1 (Position is set in updateScene)
glLightfv(GL_LIGHT1, GL_AMBIENT, Lt1amb);
glLightfv(GL_LIGHT1, GL_DIFFUSE, Lt1diff);
glLightfv(GL_LIGHT1, GL_SPECULAR, Lt1spec);
// Load the texture
loadTextureFromFile( "WoodGrain.bmp" );
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
}
// Called when the window is resized
// Sets up the projection view matrix (somewhat poorly, however)
void resizeWindow(int w, int h)
{
float aspectRatio;
glViewport( 0, 0, w, h ); // View port uses whole window
h = (w == 0) ? 1 : h;
aspectRatio = (float)w/(float)h;
// Set up the proection view matrix
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluPerspective( 60.0, aspectRatio, 1.0, 30.0 );
// Move system 10 units away to be able to view from the origin.
// Also tilt the system 15 degrees downward in order to view from above.
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glTranslatef(0.0, 0.0, -10.0);
glRotatef(15.0, 1.0,0.0,0.0);
}
// Main routine
// Set up OpenGL, hook up callbacks, and start the main loop
int main( int argc, char** argv )
{
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH );
// Window position (from top corner), and size (width and hieght)
glutInitWindowPosition( 10, 60 );
glutInitWindowSize( 620, 160 );
glutCreateWindow( "TextureTorus - Press \"t\" to toggle texture" );
// Initialize OpenGL rendering modes
initRendering();
resizeWindow(620,160);
// Set up callback functions for key presses
glutKeyboardFunc( myKeyboardFunc );
glutSpecialFunc( mySpecialKeyFunc );
// Set up the callback function for resizing windows
glutReshapeFunc( resizeWindow );
// Call this for background processing
glutIdleFunc( updateScene );
// Call this whenever window needs redrawing
glutDisplayFunc( updateScene );
// Start the main loop. glutMainLoop never returns.
glutMainLoop( );
return(0); // This line is never reached.
}
/*
* TextureTorus.h
*
* Author: Samuel R. Buss
*
* Software accompanying the book
* 3D Computer Graphics: A Mathematical Introduction with OpenGL,
* by S. Buss, Cambridge University Press, 2003.
*
* Software is "as-is" and carries no warranty. It may be used without
* restriction, but if you modify it, please change the filenames to
* prevent confusion between different versions.
* Bug reports: Sam Buss, [email protected].
* Web page: http://math.ucsd.edu/~sbuss/MathCG
*/
// Function prototypes
void myKeyboardFunc( unsigned char key, int x, int y );
void mySpecialKeyFunc( int key, int x, int y );
void updateScene( void );
void initRendering();
void resizeWindow(int w, int h);
void KeyUp();
void KeyDown();
void KeyLeft();
void KeyRight();
void ResetAnimation();
void ZeroRotation();
void ShadeModelToggle();
void FillModeToggle();
void QuadTriangleToggle();
void LocalToggle();
void Light0Toggle();
void Light1Toggle();
void WrapMore();
void WrapLess();
void NumPerWrapMore();
void NumPerWrapLess();
// Torus specific routines.
void putVertTexture(int i, int j);
/*
*
* RayTrace Software Package, release 1.0.4, February 2004.
*
* Author: Samuel R. Buss
*
* Software accompanying the book
* 3D Computer Graphics: A Mathematical Introduction with OpenGL,
* by S. Buss, Cambridge University Press, 2003.
*
* Software is "as-is" and carries no warranty. It may be used without
* restriction, but if you modify it, please change the filenames to
* prevent confusion between different versions. Please acknowledge
* all use of the software in any publications or products based on it.
*
* Bug reports: Sam Buss, [email protected].
* Web page: http://math.ucsd.edu/~sbuss/MathCG
*
*/
#ifndef RGBIMAGE_H
#define RGBIMAGE_H
#include <stdio.h>
#include <assert.h>
// Include the next line to turn off the routines that use OpenGL
// #define RGBIMAGE_DONT_USE_OPENGL
class RgbImage
{
public:
RgbImage();
RgbImage( const char* filename );
RgbImage( int numRows, int numCols ); // Initialize a blank bitmap of this size.
~RgbImage();
bool LoadBmpFile( const char *filename ); // Loads the bitmap from the specified file
bool WriteBmpFile( const char* filename ); // Write the bitmap to the specified file
#ifndef RGBIMAGE_DONT_USE_OPENGL
bool LoadFromOpenglBuffer(); // Load the bitmap from the current OpenGL buffer
#endif
long GetNumRows() const { return NumRows; }
long GetNumCols() const { return NumCols; }
// Rows are word aligned
long GetNumBytesPerRow() const { return ((3*NumCols+3)>>2)<<2; }
const void* ImageData() const { return (void*)ImagePtr; }
const unsigned char* GetRgbPixel( long row, long col ) const;
unsigned char* GetRgbPixel( long row, long col );
void GetRgbPixel( long row, long col, float* red, float* green, float* blue ) const;
void GetRgbPixel( long row, long col, double* red, double* green, double* blue ) const;
void SetRgbPixelf( long row, long col, double red, double green, double blue );
void SetRgbPixelc( long row, long col,
unsigned char red, unsigned char green, unsigned char blue );
// Error reporting. (errors also print message to stderr)
int GetErrorCode() const { return ErrorCode; }
enum {
NoError = 0,
OpenError = 1, // Unable to open file for reading
FileFormatError = 2, // Not recognized as a 24 bit BMP file
MemoryError = 3, // Unable to allocate memory for image data
ReadError = 4, // End of file reached prematurely
WriteError = 5 // Unable to write out data (or no date to write out)
};
bool ImageLoaded() const { return (ImagePtr!=0); } // Is an image loaded?
void Reset(); // Frees image data memory
private:
unsigned char* ImagePtr; // array of pixel values (integers range 0 to 255)
long NumRows; // number of rows in image
long NumCols; // number of columns in image
int ErrorCode; // error code
static short readShort( FILE* infile );
static long readLong( FILE* infile );
static void skipChars( FILE* infile, int numChars );
static void RgbImage::writeLong( long data, FILE* outfile );
static void RgbImage::writeShort( short data, FILE* outfile );
static unsigned char doubleToUnsignedChar( double x );
};
inline RgbImage::RgbImage()
{
NumRows = 0;
NumCols = 0;
ImagePtr = 0;
ErrorCode = 0;
}
inline RgbImage::RgbImage( const char* filename )
{
NumRows = 0;
NumCols = 0;
ImagePtr = 0;
ErrorCode = 0;
LoadBmpFile( filename );
}
inline RgbImage::~RgbImage()
{
delete[] ImagePtr;
}
// Returned value points to three "unsigned char" values for R,G,B
inline const unsigned char* RgbImage::GetRgbPixel( long row, long col ) const
{
assert ( row<NumRows && col<NumCols );
const unsigned char* ret = ImagePtr;
long i = row*GetNumBytesPerRow() + 3*col;
ret += i;
return ret;
}
inline unsigned char* RgbImage::GetRgbPixel( long row, long col )
{
assert ( row<NumRows && col<NumCols );
unsigned char* ret = ImagePtr;
long i = row*GetNumBytesPerRow() + 3*col;
ret += i;
return ret;
}
inline void RgbImage::GetRgbPixel( long row, long col, float* red, float* green, float* blue ) const
{
assert ( row<NumRows && col<NumCols );
const unsigned char* thePixel = GetRgbPixel( row, col );
const float f = 1.0f/255.0f;
*red = f*(float)(*(thePixel++));
*green = f*(float)(*(thePixel++));
*blue = f*(float)(*thePixel);
}
inline void RgbImage::GetRgbPixel( long row, long col, double* red, double* green, double* blue ) const
{
assert ( row<NumRows && col<NumCols );
const unsigned char* thePixel = GetRgbPixel( row, col );
const double f = 1.0/255.0;
*red = f*(double)(*(thePixel++));
*green = f*(double)(*(thePixel++));
*blue = f*(double)(*thePixel);
}
inline void RgbImage::Reset()
{
NumRows = 0;
NumCols = 0;
delete[] ImagePtr;
ImagePtr = 0;
ErrorCode = 0;
}
#endif // RGBIMAGE_H
/*
*
* RayTrace Software Package, release 1.0.4, February 2004.
*
* Author: Samuel R. Buss
*
* Software accompanying the book
* 3D Computer Graphics: A Mathematical Introduction with OpenGL,
* by S. Buss, Cambridge University Press, 2003.
*
* Software is "as-is" and carries no warranty. It may be used without
* restriction, but if you modify it, please change the filenames to
* prevent confusion between different versions. Please acknowledge
* all use of the software in any publications or products based on it.
*
* Bug reports: Sam Buss, [email protected].
* Web page: http://math.ucsd.edu/~sbuss/MathCG
*
*/
#include "RgbImage.h"
#ifndef RGBIMAGE_DONT_USE_OPENGL
#include <windows.h>
#include "GL/gl.h"
#endif
RgbImage::RgbImage( int numRows, int numCols )
{
NumRows = numRows;
NumCols = numCols;
ImagePtr = new unsigned char[NumRows*GetNumBytesPerRow()];
if ( !ImagePtr ) {
fprintf(stderr, "Unable to allocate memory for %ld x %ld bitmap.\n",
NumRows, NumCols);
Reset();
ErrorCode = MemoryError;
}
// Zero out the image
unsigned char* c = ImagePtr;
int rowLen = GetNumBytesPerRow();
for ( int i=0; i<NumRows; i++ ) {
for ( int j=0; j<rowLen; j++ ) {
*(c++) = 0;
}
}
}
/* ********************************************************************
* LoadBmpFile
* Read into memory an RGB image from an uncompressed BMP file.
* Return true for success, false for failure. Error code is available
* with a separate call.
* Author: Sam Buss December 2001.
**********************************************************************/
bool RgbImage::LoadBmpFile( const char* filename )
{
Reset();
FILE* infile = fopen( filename, "rb" ); // Open for reading binary data
if ( !infile ) {
fprintf(stderr, "Unable to open file: %s\n", filename);
ErrorCode = OpenError;
return false;
}
bool fileFormatOK = false;
int bChar = fgetc( infile );
int mChar = fgetc( infile );
if ( bChar=='B' && mChar=='M' ) { // If starts with "BM" for "BitMap"
skipChars( infile, 4+2+2+4+4 ); // Skip 4 fields we don't care about
NumCols = readLong( infile );
NumRows = readLong( infile );
skipChars( infile, 2 ); // Skip one field
int bitsPerPixel = readShort( infile );
skipChars( infile, 4+4+4+4+4+4 ); // Skip 6 more fields
if ( NumCols>0 && NumCols<=100000 && NumRows>0 && NumRows<=100000
&& bitsPerPixel==24 && !feof(infile) ) {
fileFormatOK = true;
}
}
if ( !fileFormatOK ) {
Reset();
ErrorCode = FileFormatError;
fprintf(stderr, "Not a valid 24-bit bitmap file: %s.\n", filename);
fclose ( infile );
return false;
}
// Allocate memory
ImagePtr = new unsigned char[NumRows*GetNumBytesPerRow()];
if ( !ImagePtr ) {
fprintf(stderr, "Unable to allocate memory for %ld x %ld bitmap: %s.\n",
NumRows, NumCols, filename);
Reset();
ErrorCode = MemoryError;
fclose ( infile );
return false;
}
unsigned char* cPtr = ImagePtr;
for ( int i=0; i<NumRows; i++ ) {
int j;
for ( j=0; j<NumCols; j++ ) {
*(cPtr+2) = fgetc( infile ); // Blue color value
*(cPtr+1) = fgetc( infile ); // Green color value
*cPtr = fgetc( infile ); // Red color value
cPtr += 3;
}
int k=3*NumCols; // Num bytes already read
for ( ; k<GetNumBytesPerRow(); k++ ) {
fgetc( infile ); // Read and ignore padding;
*(cPtr++) = 0;
}
}
if ( feof( infile ) ) {
fprintf( stderr, "Premature end of file: %s.\n", filename );
Reset();
ErrorCode = ReadError;
fclose ( infile );
return false;
}
fclose( infile ); // Close the file
return true;
}
short RgbImage::readShort( FILE* infile )
{
// read a 16 bit integer
unsigned char lowByte, hiByte;
lowByte = fgetc(infile); // Read the low order byte (little endian form)
hiByte = fgetc(infile); // Read the high order byte
// Pack together
short ret = hiByte;
ret <<= 8;
ret |= lowByte;
return ret;
}
long RgbImage::readLong( FILE* infile )
{
// Read in 32 bit integer
unsigned char byte0, byte1, byte2, byte3;
byte0 = fgetc(infile); // Read bytes, low order to high order
byte1 = fgetc(infile);
byte2 = fgetc(infile);
byte3 = fgetc(infile);
// Pack together
long ret = byte3;
ret <<= 8;
ret |= byte2;
ret <<= 8;
ret |= byte1;
ret <<= 8;
ret |= byte0;
return ret;
}
void RgbImage::skipChars( FILE* infile, int numChars )
{
for ( int i=0; i<numChars; i++ ) {
fgetc( infile );
}
}
/* ********************************************************************
* WriteBmpFile
* Write an RGB image to an uncompressed BMP file.
* Return true for success, false for failure. Error code is available
* with a separate call.
* Author: Sam Buss, January 2003.
**********************************************************************/
bool RgbImage::WriteBmpFile( const char* filename )
{
FILE* outfile = fopen( filename, "wb" ); // Open for reading binary data
if ( !outfile ) {
fprintf(stderr, "Unable to open file: %s\n", filename);
ErrorCode = OpenError;
return false;
}
fputc('B',outfile);
fputc('M',outfile);
int rowLen = GetNumBytesPerRow();
writeLong( 40+14+NumRows*rowLen, outfile ); // Length of file
writeShort( 0, outfile ); // Reserved for future use
writeShort( 0, outfile );
writeLong( 40+14, outfile ); // Offset to pixel data
writeLong( 40, outfile ); // header length
writeLong( NumCols, outfile ); // width in pixels
writeLong( NumRows, outfile ); // height in pixels (pos for bottom up)
writeShort( 1, outfile ); // number of planes
writeShort( 24, outfile ); // bits per pixel
writeLong( 0, outfile ); // no compression
writeLong( 0, outfile ); // not used if no compression
writeLong( 0, outfile ); // Pixels per meter
writeLong( 0, outfile ); // Pixels per meter
writeLong( 0, outfile ); // unused for 24 bits/pixel
writeLong( 0, outfile ); // unused for 24 bits/pixel
// Now write out the pixel data:
unsigned char* cPtr = ImagePtr;
for ( int i=0; i<NumRows; i++ ) {
// Write out i-th row's data
int j;
for ( j=0; j<NumCols; j++ ) {
fputc( *(cPtr+2), outfile); // Blue color value
fputc( *(cPtr+1), outfile); // Blue color value
fputc( *(cPtr+0), outfile); // Blue color value
cPtr+=3;
}
// Pad row to word boundary
int k=3*NumCols; // Num bytes already read
for ( ; k<GetNumBytesPerRow(); k++ ) {
fputc( 0, outfile ); // Read and ignore padding;
cPtr++;
}
}
fclose( outfile ); // Close the file
return true;
}
void RgbImage::writeLong( long data, FILE* outfile )
{
// Read in 32 bit integer
unsigned char byte0, byte1, byte2, byte3;
byte0 = (unsigned char)(data&0x000000ff); // Write bytes, low order to high order
byte1 = (unsigned char)((data>>8)&0x000000ff);
byte2 = (unsigned char)((data>>16)&0x000000ff);
byte3 = (unsigned char)((data>>24)&0x000000ff);
fputc( byte0, outfile );
fputc( byte1, outfile );
fputc( byte2, outfile );
fputc( byte3, outfile );
}
void RgbImage::writeShort( short data, FILE* outfile )
{
// Read in 32 bit integer
unsigned char byte0, byte1;
byte0 = data&0x000000ff; // Write bytes, low order to high order
byte1 = (data>>8)&0x000000ff;
fputc( byte0, outfile );
fputc( byte1, outfile );
}
/*********************************************************************
* SetRgbPixel routines allow changing the contents of the RgbImage. *
*********************************************************************/
void RgbImage::SetRgbPixelf( long row, long col, double red, double green, double blue )
{
SetRgbPixelc( row, col, doubleToUnsignedChar(red),
doubleToUnsignedChar(green),
doubleToUnsignedChar(blue) );
}
void RgbImage::SetRgbPixelc( long row, long col,
unsigned char red, unsigned char green, unsigned char blue )
{
assert ( row<NumRows && col<NumCols );
unsigned char* thePixel = GetRgbPixel( row, col );
*(thePixel++) = red;
*(thePixel++) = green;
*(thePixel) = blue;
}
unsigned char RgbImage::doubleToUnsignedChar( double x )
{
if ( x>=1.0 ) {
return (unsigned char)255;
}
else if ( x<=0.0 ) {
return (unsigned char)0;
}
else {
return (unsigned char)(x*255.0); // Rounds down
}
}
// Bitmap file format (24 bit/pixel form) BITMAPFILEHEADER
// Header (14 bytes)
// 2 bytes: "BM"
// 4 bytes: long int, file size
// 4 bytes: reserved (actually 2 bytes twice)
// 4 bytes: long int, offset to raster data
// Info header (40 bytes) BITMAPINFOHEADER
// 4 bytes: long int, size of info header (=40)
// 4 bytes: long int, bitmap width in pixels
// 4 bytes: long int, bitmap height in pixels
// 2 bytes: short int, number of planes (=1)
// 2 bytes: short int, bits per pixel
// 4 bytes: long int, type of compression (not applicable to 24 bits/pixel)
// 4 bytes: long int, image size (not used unless compression is used)
// 4 bytes: long int, x pixels per meter
// 4 bytes: long int, y pixels per meter
// 4 bytes: colors used (not applicable to 24 bit color)
// 4 bytes: colors important (not applicable to 24 bit color)
// "long int" really means "unsigned long int"
// Pixel data: 3 bytes per pixel: RGB values (in reverse order).
// Rows padded to multiples of four.
#ifndef RGBIMAGE_DONT_USE_OPENGL
bool RgbImage::LoadFromOpenglBuffer() // Load the bitmap from the current OpenGL buffer
{
int viewportData[4];
glGetIntegerv( GL_VIEWPORT, viewportData );
int& vWidth = viewportData[2];
int& vHeight = viewportData[3];
if ( ImagePtr==0 ) { // If no memory allocated
NumRows = vHeight;
NumCols = vWidth;
ImagePtr = new unsigned char[NumRows*GetNumBytesPerRow()];
if ( !ImagePtr ) {
fprintf(stderr, "Unable to allocate memory for %ld x %ld buffer.\n",
NumRows, NumCols);
Reset();
ErrorCode = MemoryError;
return false;
}
}
assert ( vWidth>=NumCols && vHeight>=NumRows );
int oldGlRowLen;
if ( vWidth>=NumCols ) {
glGetIntegerv( GL_UNPACK_ROW_LENGTH, &oldGlRowLen );
glPixelStorei( GL_UNPACK_ROW_LENGTH, NumCols );
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
// Get the frame buffer data.
glReadPixels( 0, 0, NumCols, NumRows, GL_RGB, GL_UNSIGNED_BYTE, ImagePtr);
// Restore the row length in glPixelStorei (really ought to restore alignment too).
if ( vWidth>=NumCols ) {
glPixelStorei( GL_UNPACK_ROW_LENGTH, oldGlRowLen );
}
return true;
}
#endif // RGBIMAGE_DONT_USE_OPENGL
Codemiles.com is a participant in the Amazon Services LLC Associates Program, an affiliate advertising program designed to provide a means for sites to earn advertising fees by advertising and linking to Amazon.com
Powered by phpBB © phpBB Group.