cpp-cheat/opengl/main.cpp
2016-03-26 16:46:19 +01:00

1045 lines
30 KiB
C++

/*
TODO split this up into many smaller files.
*/
#include <cstdio>
#include <iostream>
#include <stdlib.h>
// TODO this is just a cheap workaround for an Ubuntu 13.10 bug:
// https://bugs.launchpad.net/ubuntu/+source/nvidia-graphics-drivers-319/+bug/1248642?comments=all
// Get rid of this in the future.
#include <pthread.h>
// Required to open extension prototypes on the glext.h.
#define GL_GLEXT_PROTOTYPES 1
// Also includes gl.h and glu.h.
#include <GL/glut.h>
#include <GL/glext.h>
#include "vec3.h"
using namespace std;
/*
#fps
Depending on your driver, onscreen rendering may be limited to screen refresh rate (often 60FPS) when
rendering is done to the screen, and unlimited if done to buffers.
It is also interesting to see how FPS may be affected by giving another window
focus and hiding the OpenGL window. Certain drivers are smart enough to
let FPS increase since the image no longer needs to be rendered.
If false: outputs to screen and an image representation to stdout.
If true : outputs to stdout only.
*/
bool offscreen = false;
// Number of frames to calculate on offscreen rendering.
// After that number, stop.
// If negative, never stop (until program receives a signal).
int maxNFrames = -1;
// Some color constants:
const GLfloat white[] = {1.0, 1.0, 1.0};
const GLfloat gray [] = {0.1, 0.1, 0.1};
const GLfloat black[] = {0.0, 0.0, 0.0};
const GLfloat red [] = {1.0, 0.0, 0.0};
const GLfloat green[] = {0.0, 1.0, 0.0};
const GLfloat blue [] = {0.0, 0.0, 1.0};
GLubyte stipplePattern[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x80, 0x01, 0xC0, 0x06, 0xC0, 0x03, 0x60,
0x04, 0x60, 0x06, 0x20, 0x04, 0x30, 0x0C, 0x20,
0x04, 0x18, 0x18, 0x20, 0x04, 0x0C, 0x30, 0x20,
0x04, 0x06, 0x60, 0x20, 0x44, 0x03, 0xC0, 0x22,
0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22,
0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22,
0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22,
0x66, 0x01, 0x80, 0x66, 0x33, 0x01, 0x80, 0xCC,
0x19, 0x81, 0x81, 0x98, 0x0C, 0xC1, 0x83, 0x30,
0x07, 0xe1, 0x87, 0xe0, 0x03, 0x3f, 0xfc, 0xc0,
0x03, 0x31, 0x8c, 0xc0, 0x03, 0x33, 0xcc, 0xc0,
0x06, 0x64, 0x26, 0x60, 0x0c, 0xcc, 0x33, 0x30,
0x18, 0xcc, 0x33, 0x18, 0x10, 0xc4, 0x23, 0x08,
0x10, 0x63, 0xC6, 0x08, 0x10, 0x30, 0x0c, 0x08,
0x10, 0x18, 0x18, 0x08, 0x10, 0x00, 0x00, 0x08
};
// Param values not to see cross wall.
//GLfloat frustrumNear = 0.95*personR; //so that won't see across wall
//GLfloat frustrumFar = 100.0;
//GLfloat frustrumL = 2*personR*0.9;
class Drawable {
public:
virtual void draw() = 0;
};
template <class T=float>
class Sphere : public Drawable {
public:
Vec3<T> pos;
Vec3<T> speed;
GLfloat color[3];
T rad;
int slices;
int stacks;
Sphere()
:
pos(Vec3<>()),
rad(1.0),
slices(50),
stacks(50),
speed(Vec3<>())
{
color[0] = 1.0;
color[1] = 1.0;
color[2] = 1.0;
}
Sphere(
Vec3<> pos,
T rad = 1.0,
int slices = 50,
int stacks = 50,
const GLfloat* color = white,
Vec3<> speed = Vec3<>()
) :
pos(pos),
rad(rad),
slices(slices),
stacks(stacks),
speed(speed)
{
this->color[0] = color[0];
this->color[1] = color[1];
this->color[2] = color[2];
}
void draw(){
glPushMatrix();
glTranslatef( pos.x, pos.y, pos.z );
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, color);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, color);
//glLineWidth(1.0);
//glutWireSphere(rad, slices, stacks);
glutSolidSphere(rad, slices, stacks);
glPopMatrix();
}
};
// View parameters.
template <class T=float>
class Camera {
public:
Camera()
:
pos(Vec3<T>(0.0, 0.0, 0.0)),
dir(Vec3<T>(1.0, 0.0, 0.0)),
up(Vec3<T>(0.0, 1.0, 0.0f)),
frustrumNear(1.0),
frustrumFar(100.0),
frustrumL(1.0),
resX(700),
resY(700),
format(GL_RGB)
{
}
Camera(
Vec3<T> pos, // Position of the camera.
Vec3<T> dir, // Direction of viewing.
Vec3<T> up = Vec3<T>(0.0, 1.0, 0.0f),
T frustrumNear = 1.0,
T frustrumFar = 100.0,
T frustrumL = 1.0,
GLint resX = 700,
GLint resY = 700,
GLint format = GL_RGB
) :
pos(pos),
dir(dir),
up(up),
frustrumNear(frustrumNear),
frustrumFar(frustrumFar),
frustrumL(frustrumL),
resX(resX),
resY(resY),
format(format)
{
}
Vec3<T> getLookat(){ return pos+dir; }
// Gets number of components for the already given format.
int getNComponents() {
switch (format) {
case GL_BGR:
case GL_RGB:
return 3;
break;
case GL_BGRA:
case GL_RGBA:
return 4;
break;
case GL_ALPHA:
case GL_LUMINANCE:
return 1;
break;
}
}
Vec3<T> pos;
Vec3<T> dir;
Vec3<T> up;
GLfloat frustrumNear;
GLfloat frustrumFar;
GLfloat frustrumL;
GLint format; //format of output image
int resX;
int resY;
};
// Profiling
// Window
GLint windowW = 50;
GLint windowH = 50;
GLint windowPosX = 10;
GLint windowPosY = 10;
int oldT; //used to keep real time consistent
int nFrames = 0; //total number of frame
// Event
bool mouseLeftDown;
bool mouseRightDown;
float mouseX, mouseY;
// Camera.
Camera<> camera;
// Stores rendered pixels.
GLubyte* pixels;
GLuint fboId;
GLuint texId;
GLuint rboColor;
GLuint rboDepth;
// Light
GLfloat lightPos[] = {2.0, 0.0, 0.0, 0.0};
// Scenario
GLfloat mat_shininess[] = {100.0};
enum nDrawables {
nDrawables = 2,
nSpheres = 2
};
Drawable* drawables[nDrawables];
Sphere<> spheres[nSpheres];
// Physical viewer parameters.
float fast_forward = 1.f;
// Radius for collision detection.
GLfloat personR = 1.f;
// Wait for input mode.
GLfloat dirStep = 2.f; // speed forward
GLfloat dirRotStep = 2.f; // speed forward
// Don't wait for input.
GLfloat speedMax = 2.f; // trsnalation speed
GLfloat rotSpeedMax = 0.0f; // rotation speed in rad/
Vec3<> speed; // translation speed in rad/
Vec3<> rotSpeed; // rotation speed in rad/s. right hand rule
// Stuff set only once at beginning.
void init(int argc, char** argv) {
int doubleBuffer;
glutInit(&argc, argv);
if (offscreen) {
glutInitWindowSize(1, 1);
doubleBuffer = GLUT_SINGLE;
} else {
glutInitWindowSize( windowW, windowH );
glutInitWindowPosition( windowPosX, windowPosY );
doubleBuffer = GLUT_DOUBLE;
}
glutInitDisplayMode( doubleBuffer | GLUT_RGB | GLUT_DEPTH );
// #GLUT_SINGLE
// Use single framebuffer GL_FRONT.
// Drawing makes change immediatelly visible even before complete
// Not appropriate for animation
// #GLUT_DOUBLE
// Use 2 framebuffers, draw to GL_BACK.
// Call glutSwapBuffers to put back on GL_FRONT once drawing is complete.
// User only sees completed drawing.
// appropriate for animation
// #GLUT_RGB
// http://en.wikipedia.org/wiki/Indexed_color
// GLUT_RGB vs indexed color: instead of using 8bit per channel,
// choose 256 colors that represent well an image, and use 1byte for each pixel.
// #GLUT_DEPTH
// Keep only closest one, cheapest way to hide parts of objects that go behind others.
// Depth buffering: calculate pixels for each plane and their distances,
// Window
// Must always create a window.
// For offscreen rendering, create 1x1 window and hide it.
glutInitWindowSize(windowW, windowH);
glutInitWindowPosition(windowPosX, windowPosY);
glutCreateWindow(argv[0]);
//glutShowWindow();
//glutHideWindow();
//glutIconifyWindow();
if (offscreen) {
// Setup framebuffer.
// Generate namespace for the frame buffer, colorbuffer and depthbuffer.
// FBO is made to render and read pixels back to CPU.
// Render to texture is made to render and reutilize in GUP.
// Backbuffer is to print to screen, therefore slower than FBO.
// Pixel buffer objects to make read pixels asynchronously.
// Color renderbuffer.
// Create a new renderbuffer unique name.
glGenRenderbuffers(1, &rboColor);
// Set it as the current.
glBindRenderbuffer(GL_RENDERBUFFER, rboColor);
// Sets storage type for currently bound renderbuffer.
glRenderbufferStorage(
GL_RENDERBUFFER,
GL_RGBA8,
windowW,
windowH
);
// Depth renderbuffer.
glGenRenderbuffers(1, &rboDepth);
glBindRenderbuffer(GL_RENDERBUFFER, rboDepth);
glRenderbufferStorage(
GL_RENDERBUFFER,
GL_DEPTH_COMPONENT24,
windowW,
windowH
);
// Framebuffer.
// Create a framebuffer and a renderbuffer object.
// You need to delete them when program exits.
glGenFramebuffers(1, &fboId);
glBindFramebuffer(GL_FRAMEBUFFER, fboId);
//from now on, operate on the given framebuffer
//GL_FRAMEBUFFER read write
//GL_READ_FRAMEBUFFER read
//GL_FRAMEBUFFER write
// Adds color renderbuffer to currently bound framebuffer.
glFramebufferRenderbuffer(
GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER,
rboColor
);
glFramebufferRenderbuffer(
GL_FRAMEBUFFER,
GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER,
rboDepth
);
// Unbind current framebuffer.
// Go back to default (GL_BACK or GL_FRONT).
//glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Set read target for:
// - glReadPixel
// - glCopyTexImage1D
// - glCopyTexImage2D
// - glCopyTexSubImage1D
// - glCopyTexSubImage2D
// - glCopyTexSubImage3D
glReadBuffer(GL_COLOR_ATTACHMENT0);
//glReadBuffer(GL_BACK);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
cout << "framebuffer error:" << endl;
switch (status) {
case GL_FRAMEBUFFER_UNDEFINED: {
cout << "GL_FRAMEBUFFER_UNDEFINED" << endl;
break;
}
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: {
cout << "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT" << endl;
break;
}
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: {
cout << "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT" << endl;
break;
}
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: {
cout << "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER" << endl;
break;
}
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: {
cout << "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER" << endl;
break;
}
case GL_FRAMEBUFFER_UNSUPPORTED: {
cout << "GL_FRAMEBUFFER_UNSUPPORTED" << endl;
break;
}
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: {
cout << "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE" << endl;
break;
}
case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: {
cout << "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS" << endl;
break;
}
case 0: {
cout << "0" << endl;
break;
}
}
exit(EXIT_FAILURE);
}
glutHideWindow();
}
// Color to clear screen after each image.
glClearColor(0.0, 0.0, 0.0, 0.0);
glEnable(GL_DEPTH_TEST);
//glEnable(GL_BLEND); //use those alphas TODO ?
glEnable(GL_POLYGON_OFFSET_FILL); //TODO ?
//glEnable(GL_POLYGON_OFFSET_LINE);
glPolygonOffset(1.0, 1.0);
// Which side of objects to take into account.
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
//glPolygonMode(GL_FRONT, GL_FILL);
//glPolygonMode(GL_BACK, GL_FILL);
// Fill solids or not.
//glPolygonMode(GL_FRONT, GL_LINE); //only lines will be drawn
//glPolygonMode(GL_FRONT, GL_FILL); //will fill the solid
// Light
// Learn the color algorithm *now*.
// http://www.glprogramming.com/red/chapter05.html
glEnable(GL_LIGHTING);
//glLightModelfv
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, white);
//global ambient light. is not distance attenuated.
//if white, are no shadows, ever!
//also, if this is the only light, no shadow
//glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE); //TODO ?
//glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); //light both side
//glShadeModel(GL_FLAT);
glShadeModel(GL_SMOOTH);
// light0
//glEnable(GL_LIGHT0);
glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
// All of the following are distance attenuated.
glLightfv(GL_LIGHT0, GL_AMBIENT, gray ); //if this is white and close, you see no shadow
glLightfv(GL_LIGHT0, GL_DIFFUSE, white);
glLightfv(GL_LIGHT0, GL_SPECULAR, white);
glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess);
// Camera
camera = Camera<>(
Vec3<>(0.0, 0.0, -2.0),
Vec3<>(0.0, 0.0, 1.0),
Vec3<>(0.0, 1.0, 0.0),
1.0,
100.0,
1.0,
windowW,
windowH,
GL_RGB
);
pixels = new GLubyte[camera.getNComponents() * windowW];
//to store output pixel
// Create drawable objects to model initial scene.
spheres[0] = Sphere<>(Vec3<>(0.0, 0.0, 0.0), 1.0, 50, 50, red);
spheres[1] = Sphere<>(Vec3<>(0.0, 0.0, 0.0), 1.0, 50, 50, blue);
int total = 0;
for (int i = total; i < nSpheres; i++) {
drawables[i] = &spheres[i];
}
oldT = glutGet(GLUT_ELAPSED_TIME); //init old time in ms.
}
// Calculate the parameters of the new scene and calls display again.
void idle(void) {
//cout << "idle" << endl;
Vec3<> dx, newpos;
float dt;
int t;
// Keep animation real time consistent.
t = glutGet(GLUT_ELAPSED_TIME);
dt = fast_forward*(t - oldT)/1000.0;
oldT = t;
//cout << "===============================================" << endl;
//cout << "pos" << endl << camera.pos.str();
//cout << "dir" << endl << camera.dir.str();
//cout << "dir" << endl << camera.getLookat().str();
//cout << "speed\n" << speed;
//cout << "rotSpeed\n" << rotSpeed;
cout << "FPS average: " << 1000 * (((float)nFrames) / t);
cout << endl;
// Speed nonstop movement method.
//camera.dir.rotY(rotSpeed*dt);
//newpos = camera.pos + speed*dt;
//camera.pos -= Vec3<>(0.005, 0.0, 0.0);
spheres[0].pos += Vec3<>(0.005, 0.0, 0.0);
nFrames++;
if (offscreen && nFrames == maxNFrames) {
exit(EXIT_SUCCESS);
}
glutPostRedisplay();
}
void display() {
//cout << "display" << endl;
// Clear given buffers to clear value. Possible values:
// - GL_COLOR_BUFFER_BIT
// - GL_DEPTH_BUFFER_BIT
// - GL_ACCUM_BUFFER_BIT
// - GL_STENCIL_BUFFER_BIT
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Reset everything before starting.
glLoadIdentity();
Vec3<> lookat = camera.getLookat();
gluLookAt(
camera.pos.x,
camera.pos.y,
camera.pos.z,
lookat.x,
lookat.y,
lookat.z,
camera.up.x,
camera.up.y,
camera.up.z
);
// Get / set attributes.
//void glEnable(GLenum cap);
//void glDisable(GLenum cap);
//GLboolean glIsEnabled(GLenum capability)
//void glGet*v(GLenum pname, GL* *params);
//void glGetPointerv(GLenum pname, GLvoid **params);
// Can be done more efficiently in a stacked manner for groups of attribute
// GLbitfield represents a group of several attribute:
//void glPushAttrib(GLbitfield mask);
//void glPopAttrib(void);
// Before transforming save the old matrix.
// Potentially faster than loading identity at the end.
glPushMatrix();
// Geometric transformation.
//glMultMatrixf(N);
//glScalef(1.0, 1.0, 1.0);
//glRotatef(45., 1.0, 1.0, 0.0);
//glTranslatef(1., 0.0, 0.0);
// Can format primitives with:
//void glPointSize(GLfloat size);
//void glLineWidth(GLfloat width);
//void glLineStipple(GLint factor, GLushort pattern);
////pattern=0x3F07 (translates to 0011111100000111 in binary),
////a line would be drawn with 3 pixels on, then 5 off, 6 on, and 2 off
//glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); //default mode
//GL_FRONT, GL_BACK
//GL_FILL, GL_LINE, GL_POINT
//void glFrontFace(GLenum mode) TODO
// #PolygonStipple: Put a 32x32 pattern on polygon.
// glEnable (GL_POLYGON_STIPPLE);
// glPolygonStipple (stipplePattern);
// glDisable (GL_POLYGON_STIPPLE)
// OpenGL deals with planar polygons because they are primitives.
//glBegin(GL_POLYGON);
//glVertex3f (-0.5, -0.5, 0.0);
//glVertex3f ( 0.5, -0.5, 0.0);
//glVertex3f ( 0.5, 0.5, 0.0);
//glVertex3f (-0.5, 0.5, 0.0);
//glEnd();
// Other types of basic shapes:
// - GL_POINT
// - GL_LINE
// - GL_LINE_STRIP
// - GL_LINE_LOOP
// - GL_TRIANGLE
// - GL_TRIANGLE_STRIP
// - GL_TRIANGLE_FAN
// - GL_QUAD
// - GL_QUAD_STRIP
// - GL_POLYGON
//the following are also allowed betwen glBegin and glEnd
//glColor3f(1.0, 0.0, 0.0);
//glIndex*
//glNormal*
//normal affects lightning
//
//normal is set per vertex, not surface
//since all calculations are per vertex,
//and then later interpolated
//glBegin (GL_POLYGON);
//glNormal3fv(n0);
//glVertex3fv(v0);
//glNormal3fv(n1);
//glVertex3fv(v1);
//glNormal3fv(n2);
//glVertex3fv(v2);
//glNormal3fv(n3);
//glVertex3fv(v3);
//glEnd();
//glTexCoord*
//glEdgeFlag*
//glMaterial*
//glArrayElement
//glEvalCoord*, glEvalPoint*
//glCallList, glCallList
//vertex array
//reduce amounts of GL_VERTEX call
//to a single call on array
//static GLint vertices[] = {
//25, 25,
//100, 325,
//175, 25,
//175, 325,
//250, 25,
//325, 325
//};
//static GLfloat colors[] = {
//1.0, 0.2, 0.2,
//0.2, 0.2, 1.0,
//0.8, 1.0, 0.2,
//0.75, 0.75, 0.75,
//0.35, 0.35, 0.35,
//0.5, 0.5, 0.5
//};
///color and position on the same array
//static GLfloat intertwined[] = {
//1.0, 0.2, 1.0, 100.0, 100.0, 0.0,
//1.0, 0.2, 0.2, 0.0, 200.0, 0.0,
//1.0, 1.0, 0.2, 100.0, 300.0, 0.0,
//0.2, 1.0, 0.2, 200.0, 300.0, 0.0,
//0.2, 1.0, 1.0, 300.0, 200.0, 0.0,
//0.2, 0.2, 1.0, 200.0, 100.0, 0.0
//};
//glEnableClientState( GL_COLOR_ARRAY );
//glEnableClientState( GL_VERTEX_ARRAY );
//glEnableClientState( GL_NORMAL_ARRAY );
//glColorPointer (3, GL_FLOAT, 0, colors);
//glVertexPointer(2, GL_INT, 0, vertices);
//glInterleavedArrays (GL_C3F_V3F, 0, intertwined);
//glDisableClientState( GL_COLOR_ARRAY );
//glDisableClientState( GL_VERTEX_ARRAY );
//glDisableClientState( GL_NORMAL_ARRAY );
//glRectf(0.0, 0.0, 1.0, 1.0;)
//rectangle, x1, y1, x2, y2, z=0
//may be optimized with respect to GL_POLYGON
//can be transformed to leave z=0
//non convex line polygon
//build up from smaller triangle
//+ EdgeFlag
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//glBegin(GL_POLYGON);
//glEdgeFlag ( GL_TRUE );
//glVertex3fv( V0 );
//glEdgeFlag ( GL_FALSE );
//glVertex3fv( V1 );
//glEdgeFlag ( GL_TRUE );
//glVertex3fv(V2);
//glEnd();
// 3D shapes are not primitives, so you must call them from GLUT (or GLU).
//glutWireCube(1.0);
//glutSolidCube(1.0);
//glutWireSphere(1.0, 20, 20);
//glutSolidSphere(1.0, 20, 20);
//glutSolidTeapot(1.0);
//glutWireTeapot(1.0);
// GLUT can also draw (wire/solid):
// - cone
// - icosahedron
// - octahedron
// - tetrahedron
// - dodecahedron
// - torus
// - teapot
// Reload the old transform after drawing,
glPopMatrix();
for (int i = 0; i < nDrawables; i++) {
drawables[i]->draw();
}
// Ensure draw is up to date.
// Send all commands even if output is not read. (parallel processing, network).
// Pause application until drawing is complete and back framebuffer updated.
// Does not put back framebuffer on front, so you see nothing.
//glFlush();
// Put backbuffer into frontbuffer making drawn scene visible.
// *Wait* for the screen to refresh, limiting application speed to screen refresh rate.
// Also flushes.
// Backbuffer becomes undefined.
// If not GL_DOUBLE, simply flushes.
glutSwapBuffers();
//read pixels from render
//find where glReadPixels is reading from
//int buff;
//glGetIntegerv(GL_READ_BUFFER,&buff);
//cout << "glReadPixels reads from:" << endl;
//switch (buff) {
//case GL_BACK: {
//cout << "GL_BACK" << endl;
//break;
//}
//case GL_FRONT: {
//cout << "GL_FRONT" << endl;
//break;
//}
//case GL_COLOR_ATTACHMENT0: {
//cout << "GL_COLOR_ATTACHMENT0" << endl;
//}
//}
//#glReadPixels
// Read a rectangle of pixels from last render.
glReadPixels(
0, // top X rectangle
windowH/2, // top Y rectangle
camera.resX, // rectangle width
1, // rectangle height
camera.format, // GL_RGB, output format
GL_UNSIGNED_BYTE, // output data type
pixels // where output will be saved
);
for (int i = 0; i < camera.resX * camera.getNComponents(); i = i + 3) {
//cout << i/3 << " ";
printf(
"%3d %3d %3d | ",
(int)pixels[i],
(int)pixels[i+1],
(int)pixels[i+2]
);
}
}
void reshape(int w, int h) {
// w/2 would only draw on left half of the screen.
glViewport(0, 0, (GLsizei) w, (GLsizei) h);
// From now on, modify projection transform matrix stack.
glMatrixMode(GL_PROJECTION);
// Clear all transforms.
glLoadIdentity();
// Use 2D like projection transform.
//glOrtho(-1.0, 1.0, -1.0, 1.0, -0.0, 2.0);
// Use 3D like real life projection transform.
glFrustum(
-camera.frustrumL,
camera.frustrumL,
-camera.frustrumL,
camera.frustrumL,
camera.frustrumNear,
camera.frustrumFar
);
// From now on, modify model (scene objects) transform matrix stack.
glMatrixMode(GL_MODELVIEW);
}
// We will cover the following movement techniques:
// - fixed displacement (fixed steps)
// - fixed speed (displacement is proportional to elapsed time)
// - fixed acceleration (displacement acceleration is proportional to elapsed time).
// Can have max speed or not. Most physically accurate.
// Fixed displacement.
void keyDown(unsigned char key, int x, int y) {
switch (key) {
case 's': {
camera.dir.z -= dirStep;
break;
}
case 'w': {
camera.dir.z += dirStep;
break;
}
case 27: {
// Esc
//quit
exit(EXIT_SUCCESS);
}
break;
}
}
// Fixed speed.
// Position will be calculated as function of elapsed time in idle().
void keyDownSpeed(unsigned char key, int x, int y) {
switch (key) {
case 'a': {
// Rotate left.
//rotSpeed = -rotSpeedMax;
break;
}
case 'd': {
// Rotate right.
//rotSpeed = rotSpeedMax;
break;
}
case 'e': {
// Run faster.
//speed *= 2.f;
break;
}
case 's': {
// Backwards.
//speed = -dirSpeedMax;
break;
}
case 'w': {
// Forward.
//speed = dirSpeedMax;
break;
}
case 27: {
// Esc.
//quit
exit(EXIT_SUCCESS);
}
break;
}
}
void keyUpSpeed(unsigned char key, int x, int y) {
switch (key) {
case 'a': {
//rotSpeed = 0.f;
break;
}
case 'd': {
//rotSpeed = 0.f;
break;
}
case 'e': {
//speed /= 2.f;
break;
}
case 's': {
//speed = 0.f;
break;
}
case 'w': {
//speed = 0.f;
break;
}
}
}
void mouse(int button, int state, int x, int y) {
mouseX = x;
mouseY = y;
if (button == GLUT_LEFT_BUTTON) {
if (state == GLUT_DOWN) {
mouseLeftDown = true;
} else if (state == GLUT_UP) {
mouseLeftDown = false;
}
}
else if (button == GLUT_RIGHT_BUTTON) {
if (state == GLUT_DOWN) {
mouseRightDown = true;
} else if (state == GLUT_UP) {
mouseRightDown = false;
}
}
}
void mouseMotion(int x, int y) {
if (mouseLeftDown) {
mouseX = x;
mouseY = y;
}
if (mouseRightDown) {
mouseY = y;
}
}
void exitCB() {
//cout << "exiting" << endl;
}
int main(int argc, char** argv) {
// TODO this is just a cheap workaround for an Ubuntu 13.10 bug: https://bugs.launchpad.net/ubuntu/+source/nvidia-graphics-drivers-319/+bug/1248642?comments=all
// Get rid of this in the future.
int i=pthread_getconcurrency();
if (argc > 1) {
cout << argv[1] << endl;
if ( string(argv[1]) == "0" )
offscreen = false;
}
init(argc,argv);
//#DisplayFunc
// Sets callback function used to draw the scene,
// therefore the most important call back.
// This callback should be set on any OpenGL program.
// The display function is set to be called on next loop when `glutPostRedisplay` is called.
// Scene calculation should be left for the `glutIdleFunc` set callback,
// this callback should only draw to screen.
glutDisplayFunc(display);
//#DisplayFunc
// What to do when the window changes size.
// This is required as it is called when the window is first shown TODO confirm.
glutReshapeFunc(reshape);
//#IdleFunc
// Called when nothing else is happening: rendering, event handling.
// Often used to recalculate positions for the next frame, with a `glutPostRedisplay`
// added to the end of the idle function, so that refresh will happen
// Not required, but used by almost all programs.
// TODO why not put calculations directly inside the display func?
glutIdleFunc(idle);
//#event handlers
glutKeyboardFunc(keyDown);
//glutKeyboardUpFunc(keyUp);
//glutMouseFunc(mouse);
//glutMotionFunc(mouseMotion); //mouse motion
atexit(exitCB);
//#MainLoop
// Start the main loop which calls all the callbacks at the right time.
// TODO how to leave the main loop from a program?
// http://www.opengl.org/discussion_boards/showthread.php/142428-How-to-exit-MainLoop-WITHOUT-killing-the-app
// The most common way is a simple exit.
glutMainLoop();
// This point is only reached when the program window is closed by the user.
return EXIT_SUCCESS;
}