You are on page 1of 47

project: speed typing game (former “Sponge Bob’s Words”)

author: (c) 2008 Kai Kajus Noack. All rights reserved.

website: http://media-it.blogspot.com/2008/02/speed-typing-game.html

License:
CREATIVE COMMONS Attribution-Noncommercial-Share Alike 3.0 Germany
You may not use this file except in compliance with the License.
You may obtain a copy of the License at:
http://creativecommons.org/licenses/by-nc-sa/3.0/de/deed.de

Es ist Ihnen gestattet:


 das Werk vervielfältigen, verbreiten, öffentlich zugänglich zu machen
 Abwandlungen bzw. Bearbeitungen des Inhaltes anzufertigen
 zu den folgenden Bedingungen:

• Namensnennung.
Sie müssen den Namen des Autors/Rechteinhabers in der von ihm
festgelegten Weise nennen.

• Keine kommerzielle Nutzung.


Dieses Werk darf nicht für kommerzielle Zwecke verwendet werden.

• Weitergabe unter gleichen Bedingungen.


Wenn Sie den lizenzierten Inhalt bearbeiten oder in anderer Weise
umgestalten, verändern oder als Grundlage für einen anderen Inhalt
verwenden, dürfen Sie den neu entstandenen Inhalt nur unter
Verwendung von Lizenzbedingungen weitergeben, die mit denen dieses
Lizenzvertrages identisch oder vergleichbar sind.

- Im Falle einer Verbreitung müssen Sie anderen die Lizenzbedingungen,


unter welche dieses Werk fällt, mitteilen. Am Einfachsten ist es, einen
Link auf diese Seite einzubinden.

- Jede der vorgenannten Bedingungen kann aufgehoben werden, sofern


Sie die Einwilligung des Rechteinhabers dazu erhalten.

- Diese Lizenz lässt die Urheberpersönlichkeitsrechte unberührt.


const.h — Printed on 29.12.2008, 13:26:58 — Page 1
#ifndef CONSTS_H_INCLUDED
#define CONSTS_H_INCLUDED

// unsere Konstantent für das Spiel

static const float PI = 3.14159265L;

static const int WINWIDTH = 800;


static const int WINHEIGHT = 600;

static const int WORTANZAHL = 2000;


static const int MAXWORTLAENGE = 15;

static const int COUNTDOWN = 60;

static const int MAXBLITZE = 10;

#endif
main.h — Printed on 29.12.2008, 13:29:14 — Page 1
#ifndef MAIN_H_
#define MAIN_H_

class game
{
public:
Game();
virtual ~Game();
};

void initRendering(void);

#endif
main.cpp — Printed on 29.12.2008, 13:28:56 — Page 1
/******************************************************
* Kai Kajus Noack *
* Project: "Game: SpongeBob's Words" *
* Date: 2008-01-20 *
******************************************************/

#include <iostream>
#include <stdlib.h>
#include <GL/glut.h>
#include <GL/gl.h>
#include "text3d.h"
// selbstdefinierte Konstanten
#include "const.h"
// zum Dateieinlesen
#include <fstream>
#include <iomanip>
// für Casting int zu String
#include <sstream>
#include <string>
// für Texturen
#include "imageloader.h"
// der Timer
#include <time.h>
// trigonometrische Funktionen
#include <math.h>
// Objekt: Wort (Spielelement)
#include "Word.h"
// vektor-klasse zum zeichnen
#include <vector>
#include "vec3f.h"

using namespace std;

bool blitz = false;


bool psychomode = false;
bool startScreen = true;
bool gameOver = false;

// Alpha-Würfel *** START


const float BOX_SIZE = 7.0f; // Seitenlänge des Würfels
const float ALPHA = 0.6f; // Durchsichtigkeit der Flächen

// Vektoren geben Richtungen der Seitenfläche an


struct Face {
Vec3f up;
Vec3f right;
Vec3f out;
};

// Seitenfläche Alpha-Würfel
struct Cube {
Face top;
Face bottom;
Face left;
Face right;
Face front;
Face back;
};

// Rotation des Vektors entsprechend der Gradangabe um die gegebene Achse


Vec3f rotate(Vec3f v, Vec3f axis, float degrees) {
axis = axis.normalize();
float radians = degrees * PI / 180;
main.cpp — Printed on 29.12.2008, 13:28:56 — Page 2
float s = sin(radians);
float c = cos(radians);
return v * c + axis * axis.dot(v) * (1 - c) + v.cross(axis) * s;
}

// Rotation der Seitenfläche


void rotate(Face &face, Vec3f axis, float degrees) {
face.up = rotate(face.up, axis, degrees);
face.right = rotate(face.right, axis, degrees);
face.out = rotate(face.out, axis, degrees);
}

// Rotation des Würfels


void rotate(Cube &cube, Vec3f axis, float degrees) {
rotate(cube.top, axis, degrees);
rotate(cube.bottom, axis, degrees);
rotate(cube.left, axis, degrees);
rotate(cube.right, axis, degrees);
rotate(cube.front, axis, degrees);
rotate(cube.back, axis, degrees);
}

// Vektoren-Initialisierung für die Seitenflächen


void initCube(Cube &cube) {
cube.top.up = Vec3f(0, 0, -1);
cube.top.right = Vec3f(1, 0, 0);
cube.top.out = Vec3f(0, 1, 0);

cube.bottom.up = Vec3f(0, 0, 1);


cube.bottom.right = Vec3f(1, 0, 0);
cube.bottom.out = Vec3f(0, -1, 0);

cube.left.up = Vec3f(0, 0, -1);


cube.left.right = Vec3f(0, 1, 0);
cube.left.out = Vec3f(-1, 0, 0);

cube.right.up = Vec3f(0, -1, 0);


cube.right.right = Vec3f(0, 0, 1);
cube.right.out = Vec3f(1, 0, 0);

cube.front.up = Vec3f(0, 1, 0);


cube.front.right = Vec3f(1, 0, 0);
cube.front.out = Vec3f(0, 0, 1);

cube.back.up = Vec3f(1, 0, 0);


cube.back.right = Vec3f(0, 1, 0);
cube.back.out = Vec3f(0, 0, -1);
}

// Ist uns face1 oder face2 zugewandt


bool compareFaces(Face* face1, Face* face2) {
return face1->out[2] < face2->out[2];
}

// Eckpunkte der Seitenfläche


void faceVertices(Face &face, Vec3f* vs) {
vs[0] = BOX_SIZE / 2 * (face.out - face.right - face.up);
vs[1] = BOX_SIZE / 2 * (face.out - face.right + face.up);
vs[2] = BOX_SIZE / 2 * (face.out + face.right + face.up);
vs[3] = BOX_SIZE / 2 * (face.out + face.right - face.up);
}

void drawTopFace(Face &face) {


main.cpp — Printed on 29.12.2008, 13:28:56 — Page 3
Vec3f vs[4];
faceVertices(face, vs);
glDisable(GL_TEXTURE_2D);
glBegin(GL_QUADS);
glColor4f(1.0f, 1.0f, 0.0f, ALPHA);
glNormal3f(face.out[0], face.out[1], face.out[2]);
glVertex3f(vs[0][0], vs[0][1], vs[0][2]);
glVertex3f(vs[1][0], vs[1][1], vs[1][2]);
glVertex3f(vs[2][0], vs[2][1], vs[2][2]);
glVertex3f(vs[3][0], vs[3][1], vs[3][2]);
glEnd();
}

void drawBottomFace(Face &face) {


Vec3f vs[4];
faceVertices(face, vs);
glDisable(GL_TEXTURE_2D);
glBegin(GL_QUADS);
glColor4f(1.0f, 0.0f, 1.0f, ALPHA);
glNormal3f(face.out[0], face.out[1], face.out[2]);
glVertex3f(vs[0][0], vs[0][1], vs[0][2]);
glVertex3f(vs[1][0], vs[1][1], vs[1][2]);
glVertex3f(vs[2][0], vs[2][1], vs[2][2]);
glVertex3f(vs[3][0], vs[3][1], vs[3][2]);
glEnd();
}

void drawLeftFace(Face &face) {


Vec3f vs[4];
faceVertices(face, vs);
glDisable(GL_TEXTURE_2D);
glBegin(GL_QUADS);
glNormal3f(face.out[0], face.out[1], face.out[2]);
glColor4f(0.0f, 1.0f, 1.0f, ALPHA);
glVertex3f(vs[0][0], vs[0][1], vs[0][2]);
glVertex3f(vs[1][0], vs[1][1], vs[1][2]);
glColor4f(0.0f, 0.0f, 1.0f, ALPHA);
glVertex3f(vs[2][0], vs[2][1], vs[2][2]);
glVertex3f(vs[3][0], vs[3][1], vs[3][2]);
glEnd();
}

void drawRightFace(Face &face) {


Vec3f vs[4];
faceVertices(face, vs);
glDisable(GL_TEXTURE_2D);
glBegin(GL_QUADS);
glNormal3f(face.out[0], face.out[1], face.out[2]);
glColor4f(1.0f, 0.0f, 0.0f, ALPHA);
glVertex3f(vs[0][0], vs[0][1], vs[0][2]);
glVertex3f(vs[1][0], vs[1][1], vs[1][2]);
glColor4f(0.0f, 1.0f, 0.0f, ALPHA);
glVertex3f(vs[2][0], vs[2][1], vs[2][2]);
glVertex3f(vs[3][0], vs[3][1], vs[3][2]);
glEnd();
}

void drawFrontFace(Face &face, GLuint textureId) {


Vec3f vs[4];
faceVertices(face, vs);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, textureId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
main.cpp — Printed on 29.12.2008, 13:28:56 — Page 4
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glColor4f(1.0f, 1.0f, 1.0f, ALPHA);
glBegin(GL_QUADS);
glNormal3f(face.out[0], face.out[1], face.out[2]);
glTexCoord2f(0, 0);
glVertex3f(vs[0][0], vs[0][1], vs[0][2]);
glTexCoord2f(0, 1);
glVertex3f(vs[1][0], vs[1][1], vs[1][2]);
glTexCoord2f(1, 1);
glVertex3f(vs[2][0], vs[2][1], vs[2][2]);
glTexCoord2f(1, 0);
glVertex3f(vs[3][0], vs[3][1], vs[3][2]);
glEnd();
}

void drawBackFace(Face &face, GLuint textureId) {


Vec3f vs[4];
faceVertices(face, vs);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, textureId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glColor4f(1.0f, 1.0f, 1.0f, ALPHA);
glBegin(GL_QUADS);
glNormal3f(face.out[0], face.out[1], face.out[2]);
glTexCoord2f(0, 0);
glVertex3f(vs[0][0], vs[0][1], vs[0][2]);
glTexCoord2f(0, 1);
glVertex3f(vs[1][0], vs[1][1], vs[1][2]);
glTexCoord2f(1, 1);
glVertex3f(vs[2][0], vs[2][1], vs[2][2]);
glTexCoord2f(1, 0);
glVertex3f(vs[3][0], vs[3][1], vs[3][2]);
glEnd();
}

// Seitenfläche auf Würfel zeichnen


void drawFace(Face* face, Cube &cube, GLuint textureId) {
if (face == &(cube.top)) { drawTopFace(cube.top); }
else if (face == &(cube.bottom)) { drawBottomFace(cube.bottom); }
else if (face == &(cube.left)) { drawLeftFace(cube.left); }
else if (face == &(cube.right)) { drawRightFace(cube.right); }
else if (face == &(cube.front)) { drawFrontFace(cube.front, textureId); }
else { drawBackFace(cube.back, textureId); }
}
// Alpha-Würfel *** ENDE

Cube _cube; // der Würfel

// Timer-Variablen
int minutes, sek, recentTime;

// Spielzeit in Sekunden
clock_t startTime, endTime;

// Spielmodus: Training oder Countdown


string gamemode;

// Spielzeit
int countDownTime = COUNTDOWN;
string timeSpent;

// Arrays + String + Zähler für Keyboard-Eingabe des Spielers


main.cpp — Printed on 29.12.2008, 13:28:56 — Page 5
char keyboardInput[MAXWORTLAENGE];
string eingabewort;
unsigned char* keys = new unsigned char[MAXWORTLAENGE];
int inputChar = 0;

// Statistisch mitzählen
int charCounter = 0;
int wordCounter = 0;
int wordFalse = 0;
int wordRight = 0;

// Arrays für eingelesene Strings


char allWords[WORTANZAHL][MAXWORTLAENGE];
string alleWoerter[WORTANZAHL];

// Zufallszahl für Wortauswahl


int randomNr = 0;

// Punkte speichern
int scoreInt = 0;
string score;

// das Wort auf dem Bildschirm


Word *wort;

string createNewWord(void) {
// initialisieren eines Random (notwendig für nä. Fkt.aufruf)
srand ( time(NULL) );
// generate random number
randomNr = rand() % WORTANZAHL;
// zu lange Wörter ggf. kürzen
if((alleWoerter[randomNr]).length()>=(unsigned)MAXWORTLAENGE) {
return (alleWoerter[randomNr]).substr(0,MAXWORTLAENGE-1);
}
return allWords[randomNr];
}

// Stream zwecks Umwandlung von int zu string


stringstream out;
string setScore(int scoredValue){
// Punktzahl + 1
scoreInt+=scoredValue;
// stream bereinigen
out.str("");
out.clear();
// neue Punkte in den Stream schreiben
out << "Score: " << scoreInt;
return out.str();
}

void resetInputState(void) {
// bisherige Eingaben löschen
eingabewort.clear();
for (int j = 0; j < MAXWORTLAENGE; j++) {
keyboardInput[j] = '\0';
}
// Position des Wortes auf Bildschirm zurücksetzen
wort->resetWordPos();
}

// Textur-Loader
GLuint loadTexture(Image *image) {
GLuint texture;
main.cpp — Printed on 29.12.2008, 13:28:56 — Page 6
glGenTextures(1, &texture);
// Textur den Bilddaten zuordnen
glBindTexture(GL_TEXTURE_2D, texture);
// Mappen des Bildes auf die Textur (Bild nach OpenGL laden)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image->width, image->height, 0, GL_RGB,
GL_UNSIGNED_BYTE, image->pixels); //The actual pixel data
// Textur-ID zurückgeben
return texture;
}

// 3D-Text bereinigen
void cleanup() {
t3dCleanup();
}

/**** TEXTUREN ****/


GLuint _textureIdFloor;
GLuint _textureIdSponge;

// weitere Spielvariablen (Winkel)


float _angle = 0.0f;
float _angle2 = 0.0f;
// Var. für: Verschiebung Würfel in X-Richtung je nach Punktzahl des Spielers
float offsetX = 1.0;

// Rendern vorbereiten
void initRendering() {
glEnable(GL_DEPTH_TEST);
glEnable(GL_COLOR_MATERIAL);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHT1);
glEnable(GL_NORMALIZE);
glShadeModel(GL_SMOOTH);

glEnable(GL_FOG);
glClearColor(0.1f, 0.1f, 0.15f, 1);
// Initialisierung der 3D-Schriften
t3dInit();

// Bild in Textur laden


Image* image = loadBMP("img/floor.bmp");
_textureIdFloor = loadTexture(image);
// image-objekt löschen, dann neu laden
delete image;

image = loadBMP("img/spongebob.bmp");
_textureIdSponge = loadTexture(image);
delete image;
}

/**** PARTIKEL START ****/


const int ParticleCount = 500;

typedef struct {
double Xpos;
double Ypos;
double Zpos;
double Xmov;
double Zmov;
double Red;
main.cpp — Printed on 29.12.2008, 13:28:56 — Page 7
double Green;
double Blue;
double Direction;
double Acceleration;
double Deceleration;
double Scalez;
bool Visible;
}PARTICLES;

PARTICLES Particle[ParticleCount];

void glCreateParticles (void) {


int i;
for (i = 1; i < ParticleCount; i++) {
Particle[i].Xpos = 0;
Particle[i].Ypos = -5;
Particle[i].Zpos = -5;
Particle[i].Xmov = (((((((2 - 1 + 1) * rand()%11) + 1) - 1 + 1) * rand()%11) + 1) * 0.005) -
(((((((2 - 1 + 1) * rand()%11) + 1) - 1 + 1) * rand()%11) + 1) * 0.005);
Particle[i].Zmov = (((((((2 - 1 + 1) * rand()%11) + 1) - 1 + 1) * rand()%11) + 1) * 0.005) -
(((((((2 - 1 + 1) * rand()%11) + 1) - 1 + 1) * rand()%11) + 1) * 0.005);
Particle[i].Red = 1;
Particle[i].Green = 1;
Particle[i].Blue = 1;
Particle[i].Scalez = 0.25;
Particle[i].Direction = 0;
Particle[i].Acceleration = ((((((8 - 5 + 2) * rand()%11) + 5) - 1 + 1) * rand()%11) + 1) * 0.02;
Particle[i].Deceleration = 0.0025;
}
}

void glUpdateParticles (void) {


int i;
for (i = 1; i < ParticleCount; i++) {
glColor3f (Particle[i].Red, Particle[i].Green, Particle[i].Blue);
Particle[i].Ypos = (Particle[i].Ypos + Particle[i].Acceleration - Particle[i].Deceleration)*0.8; // 0.8 ->
cool particle effect
Particle[i].Deceleration = Particle[i].Deceleration + 0.0025;
Particle[i].Xpos = Particle[i].Xpos + Particle[i].Xmov;
Particle[i].Zpos = Particle[i].Zpos + Particle[i].Zmov;
Particle[i].Direction = Particle[i].Direction + ((((((int)(0.5 - 0.1 + 0.1) * rand()%11) + 1) - 1 + 1)
* rand()%11) + 1);
if (Particle[i].Ypos < -5) {
Particle[i].Xpos = 0;
Particle[i].Ypos = -5;
Particle[i].Zpos = -5;
Particle[i].Red = 1;
Particle[i].Green = 1;
Particle[i].Blue = 1;
Particle[i].Direction = 0;
Particle[i].Acceleration = ((((((8 - 5 + 2) * rand()%11) + 5) - 1 + 1) * rand()%11) + 1) *
0.02;
Particle[i].Deceleration = 0.0025;
}
}
}

void glDrawParticles (void) {


glPushMatrix();
glTranslatef(0.0f, 10.0f, -9.0f);
glRotatef(180.0f, 0.0f, 0.0f, 1.0f);
glColor3f(1.0f, 0.5f, 0.0f);
main.cpp — Printed on 29.12.2008, 13:28:56 — Page 8
int i;
glDisable(GL_LIGHTING); // zusätzlich hinzugefügt
glDisable(GL_TEXTURE_2D);
for (i = 1; i < ParticleCount; i++) {
glPushMatrix();
glTranslatef (Particle[i].Xpos, Particle[i].Ypos, Particle[i].Zpos);
glRotatef (Particle[i].Direction - 90, 0, 0, 1);
glScalef (Particle[i].Scalez, Particle[i].Scalez, Particle[i].Scalez);
glDisable (GL_DEPTH_TEST);
glEnable (GL_BLEND);
// Erklärung http://wiki.delphigl.com/index.php/GlBlendFunc
glBlendFunc (GL_DST_COLOR, GL_ZERO);
glBegin (GL_QUADS);
glTexCoord2d (0, 0);
glVertex3f (-1, -1, 0);
glTexCoord2d (1, 0);
glVertex3f (1, -1, 0);
glTexCoord2d (1, 1);
glVertex3f (1, 1, 0);
glTexCoord2d (0, 1);
glVertex3f (-1, 1, 0);
glEnd();
glBlendFunc (GL_ONE, GL_ONE);
glBegin (GL_QUADS);
glTexCoord2d (0, 0);
glVertex3f (-1, -1, 0);
glTexCoord2d (1, 0);
glVertex3f (1, -1, 0);
glTexCoord2d (1, 1);
glVertex3f (1, 1, 0);
glTexCoord2d (0, 1);
glVertex3f (-1, 1, 0);
glEnd();
glEnable(GL_DEPTH_TEST);
glPopMatrix();
}
glEnable(GL_LIGHTING);
glPopMatrix();
}

void initParticles() {
glEnable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
glCreateParticles();
}

/**** PARTIKEL ENDE ****/

void paintFlyingCube() {

// Optional:
// Rotes ambientes Licht neben Würfel hinzufügen
// GLfloat ambientColor[] = {0.2f, 0.2f, 0.2f, 1.0f};
// glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientColor);
// GLfloat lightColor1[] = {1.5f, 0.5f, 0.5f, 1.0f};
// GLfloat lightPos1[] = {2.0f, 0.0f, 2.0f, 1.0f};
// glLightfv(GL_LIGHT1, GL_DIFFUSE, lightColor1);
// glLightfv(GL_LIGHT1, GL_POSITION, lightPos1);

glPushMatrix();
glTranslatef(-8.0f, 0.0f, 0.0f);
glRotatef(_angle, 0.0f, 1.0f, 0.0f);
glColor3f(1.0f, 1.0f, 0.0f);
main.cpp — Printed on 29.12.2008, 13:28:56 — Page 9
glBegin(GL_QUADS);
// Smooth Shading (für jeden Punkt eine eigene Normale!)
// Front
glNormal3f(-1.0f, 0.0f, 1.0f);
glVertex3f(-1.5f+offsetX, -1.0f, 1.5f);
glNormal3f(1.0f, 0.0f, 1.0f);
glVertex3f(1.5f+offsetX, -1.0f, 1.5f);
glNormal3f(1.0f, 0.0f, 1.0f);
glVertex3f(1.5f+offsetX, 1.0f, 1.5f);
glNormal3f(-1.0f, 0.0f, 1.0f);
glVertex3f(-1.5f+offsetX, 1.0f, 1.5f);
// Rechts
glNormal3f(1.0f, 0.0f, -1.0f);
glVertex3f(1.5f+offsetX, -1.0f, -1.5f);
glNormal3f(1.0f, 0.0f, -1.0f);
glVertex3f(1.5f+offsetX, 1.0f, -1.5f);
glNormal3f(1.0f, 0.0f, 1.0f);
glVertex3f(1.5f+offsetX, 1.0f, 1.5f);
glNormal3f(1.0f, 0.0f, 1.0f);
glVertex3f(1.5f+offsetX, -1.0f, 1.5f);
// Hinten
glNormal3f(-1.0f, 0.0f, -1.0f);
glVertex3f(-1.5f+offsetX, -1.0f, -1.5f);
glNormal3f(-1.0f, 0.0f, -1.0f);
glVertex3f(-1.5f+offsetX, 1.0f, -1.5f);
glNormal3f(1.0f, 0.0f, -1.0f);
glVertex3f(1.5f+offsetX, 1.0f, -1.5f);
glNormal3f(1.0f, 0.0f, -1.0f);
glVertex3f(1.5f+offsetX, -1.0f, -1.5f);
// Links
glNormal3f(-1.0f, 0.0f, -1.0f);
glVertex3f(-1.5f+offsetX, -1.0f, -1.5f);
glNormal3f(-1.0f, 0.0f, 1.0f);
glVertex3f(-1.5f+offsetX, -1.0f, 1.5f);
glNormal3f(-1.0f, 0.0f, 1.0f);
glVertex3f(-1.5f+offsetX, 1.0f, 1.5f);
glNormal3f(-1.0f, 0.0f, -1.0f);
glVertex3f(-1.5f+offsetX, 1.0f, -1.5f);
glEnd();
glPopMatrix();

int blitzZaehler = 0;

void blitzen() {
// Blitz-Effekt im Hintergrund
if (0==blitzZaehler%2) glClearColor(2.0f, 2.0f, 2.0f, 1);
else glClearColor(0.1f, 0.1f, 0.15f, 1);
blitzZaehler++;
// auf 5 Blitze beschränken
if (blitzZaehler>MAXBLITZE) {
blitz = false;
blitzZaehler=0;
glClearColor(0.1f, 0.1f, 0.15f, 1);
}
}

// Tastatur-Eingaben des Spielers prüfen


void checkUserInput() {
wordCounter++;

// bei korrekter Eingabe


main.cpp — Printed on 29.12.2008, 13:28:56 — Page 10
if ( !(wort->name.compare(eingabewort)) ) {
wordRight++;
score = setScore(1);
blitz = true;
resetInputState();
wort->name = createNewWord();
// Würfel verschiebt sich
offsetX+= 0.5;
}
// Spezial: Eingabe zur Aktivierung der Psychomode-Grafik ;)
else if ( ! (eingabewort.compare("000")) ) {
psychomode = !psychomode;
resetInputState();
}
// bei Falschschreibung des Wortes
else if ( wort->name.compare(eingabewort) ) {
wordFalse++;
score = setScore(-1);
resetInputState();
wort->name = createNewWord();
// Würfel verschiebt sich
offsetX-= 0.5;
}

// wichtig für die Position xxx eingabewort


inputChar = 0;
}

void handleKeypress(unsigned char key, int x, int y) {

// Startbildschirm:
if (startScreen) {
switch (key) {
case 27: // Escape
cleanup();
exit(0);
break;
case '1':
gamemode = "countdown";
startScreen = false;
break;
case '2':
gamemode = "training";
startScreen = false;
break;
default:
cout << "falsche Eingabe" << endl;
}
resetInputState();
}
// Spiel läuft:
else if (!gameOver) {
charCounter++;
switch (key) {
case 27: // Escape
cleanup();
exit(0);
break;
case '\r':
checkUserInput();
break;
default:
keyboardInput[inputChar] = key;
main.cpp — Printed on 29.12.2008, 13:28:56 — Page 11
eingabewort = keyboardInput;
inputChar++;
}
// Eingabe ist zu lang
if ((unsigned)inputChar>wort->name.length()) {
score = setScore(-1);
resetInputState();
inputChar = 0;
}
}
// wenn GameOver aktiv ist:
else {
switch (key) {
case 27: // Escape
cleanup();
exit(0);
break;
// Neustart des Spiels via Enter-Taste
case '\r':
// Neu-Initialisierungen
charCounter = 0;
wordCounter = 0;
wordFalse = 0;
wordRight = 0;
scoreInt = 0;
score = setScore(0);
countDownTime = COUNTDOWN;
startTime = clock();
offsetX = 1.0f;
startScreen = true;
gameOver = false;
resetInputState();
break;
}
}
}

// Bodenplatte mit Wassertextur zeichnen


void paintFloor() {
// Texturen aktivieren
glEnable(GL_TEXTURE_2D);
// Flur-Textur verwenden
glBindTexture(GL_TEXTURE_2D, _textureIdFloor);
// Bild nah oder fern, nutze GL_NEAREST Mapping
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// zeichne Boden mit Texturkoordinaten
glBegin(GL_QUADS);
// Normalen-Vektor zeigt nach oben
glNormal3f(0.0, 1.0f, 0.0f);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(-7.0f, -5.0f, 5.0f);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(7.0f, -5.0f, 5.0f);
glTexCoord2f(1.0f, 1.0f);
glVertex3f(25.0f, -5.0f, -25.0f);
glTexCoord2f(0.0f, 1.0f);
glVertex3f(-25.0f, -5.0f, -25.0f);
glEnd();
// Texturen deaktivieren
glDisable(GL_TEXTURE_2D);
}
main.cpp — Printed on 29.12.2008, 13:28:56 — Page 12

// eine Veränderung der Fenstergröße beachten


void handleResize(int w, int h) {
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0, (double)w / (double)h, 1.0, 200.0);
}

void moveAndPaintWord(string suchwort) {


glColor3f(1.0,1.0,1.0);
// zeichne das frontal zu sehende Wort
glPushMatrix();
// Wort bewegen
glTranslatef(wort->posX, wort->posY, wort->posZ);
// optional: Wort rotieren
// glRotatef(wort->angle, 0.0f, 1.0f, 0.0f);
// Wort zeichnen
t3dDraw3D(suchwort, 0, 0, 0.2f);
glPopMatrix();

// Wortkopie als Reflexion auf dem Boden zeichnen


glPushMatrix();
glTranslatef(wort->posX, wort->posY-9.0f, wort->posZ-4.0f);
glRotatef(-180.0f, 1.0f,0.0f,0.0f);
glColor4f(0.2f,0.2f,1.0f,0.5f);
t3dDraw3D(suchwort, 0, 0, 0.2f);
glColor3f(0.5f, 0.5f, 0.65f);
glPopMatrix();
glEnable(GL_NORMALIZE);
// wenn das Suchwort hinter uns ist, bringe es wieder nach vorne
if (wort->posZ > 12.0) {
resetInputState();
}
}

// rechts unten im Bildschirm erscheint die Eingabe des Spielers


void showKeyboardInput() {
glPushMatrix();
glTranslated(5.2f,-4.5f,3.0f);
glScalef(0.5f, 0.5f, 0.5f);
t3dDraw3D(eingabewort, 0, 0, 0.2f);
glPopMatrix();
}

// rechts oben erscheinen die Punkte und der Timer


void showScore() {
glColor3f(2.0f,0.0f,0.0f);
glPushMatrix();
glTranslated(5.45f,4.0f,4.0f);
glScalef(0.35f, 0.35f, 0.35f);
t3dDraw3D(score, 0, 0, 0.2f);
glPopMatrix();
}
void showTimer() {
glPushMatrix();
glTranslated(5.5f,4.4f,4.0f);
glScalef(0.3f, 0.3f, 0.3f);
glColor3f(2.0f, 0.0f, 0.0f);
t3dDraw3D(timeSpent, 0, 0, 0.2f);
glPopMatrix();
// glColor3f(0.3f, 1.0f, 0.3f);
}
main.cpp — Printed on 29.12.2008, 13:28:56 — Page 13

// Konvertierung
double convertRADtoANGLE(double rad) {
double angle0 = rad * 2 * PI / 360;
return angle0;
}

// Zahl runden
double Round(double Zahl, unsigned int Stellen) {
Zahl *= pow(10, Stellen);
if (Zahl >= 0)
Zahl = floor(Zahl + 0.5);
else
Zahl = ceil(Zahl - 0.5);
Zahl /= pow(10, Stellen);
return Zahl;
}

// bei Spielstart anzeigen


void showStartScreen() {
glDisable(GL_FOG);
glPushMatrix();
glTranslated(0.0f,0.0f,-3.0f);
glColor3f(1.5f, 1.5f, 0.5f);
t3dDraw3D("Wahl des Modus:\n1 - Countdown\n2 - Training", 0, 0, 0.2f);
glPopMatrix();
glEnable(GL_FOG);
}

// beim Gameover anzeigen


void showGameOver(double result) {
glPushMatrix();
glTranslated(0.0f,-2.0f,3.0f);
glRotatef(_angle, 0.5f, 1.0f, 0.5f);
_angle -= 0.5f;
glColor3f(0.2f, 0.2f, 1.0f);
t3dDraw3D("Game Over!", 0, 0, 0.2f);
glPopMatrix();

// Ausgabe des erzielten Ergebnisses


glPushMatrix();
glTranslated(0.0f,1.0f+cos(convertRADtoANGLE(_angle)),0.0f);
glScalef(0.5f, 0.5f, 0.5f);
glColor3f(0.2f, 1.0f, 0.2f);

// Lösche Daten aus Stream


out.str(""); out.clear();
// Schreibe neue Daten in Stream (beachte negatives oder Null-Ergebnis)
if (result<=0) out << "Oh, keine Woerter richtig :(";
else out << "Super! " << scoreInt << " Punkte!\n\n " << Round(result,3) << " Sekunden pro Wort!\n\n"
<< wordRight << " von " << wordCounter << " Woertern richtig!\n" << "Fehlerquote: " << Round( ((float)(
wordFalse)/(float)(wordCounter))*100 , 2) << "%" << "\n\n" << charCounter*(60.0f/(float)COUNTDOWN)
<< " Anschlaege pro Minute";

// String aus dem Stream generieren und an Text-Zeichner übergeben


t3dDraw3D(out.str(), 0, 0, 0.2f);
glPopMatrix();
}

// Nebeleffekt (+ versteckte psychomode-Grafik)


void paintFog(){
GLfloat fogColor[] = {0.5f, 0.5f, 0.5f, 1};
glFogfv(GL_FOG_COLOR, fogColor);
main.cpp — Printed on 29.12.2008, 13:28:56 — Page 14
glFogi(GL_FOG_MODE, GL_LINEAR);
glFogf(GL_FOG_START, 4.0f);
glFogf(GL_FOG_END, 25.0f);

if (psychomode) {
double r = 20.0f;
double tiefeC = 8.0f;
int stepF = 90;

glColor3f(1.0f,0.0f,0.0f);
glPushMatrix();
glTranslatef(0.0f,0.0f,-20.0f);
// eine interessante Rotation der Box
glRotatef(_angle2, cos(convertRADtoANGLE(_angle)), 0.5f, 1.0f);

for (int angl = 45; angl <= 315; angl+=stepF) {


glBegin(GL_POLYGON);
// Box wird sehr verschiedenfarbig
glColor3f(cos(convertRADtoANGLE(angl)), sin(convertRADtoANGLE(angl)), 0.0f);
glVertex3f(r*cos(convertRADtoANGLE(angl)), r*sin(convertRADtoANGLE(angl)), 0.0f);
glVertex3f(r*cos(convertRADtoANGLE(angl)), r*sin(convertRADtoANGLE(angl)), tiefeC);
glVertex3f(r*cos(convertRADtoANGLE(angl+stepF)), r*sin(convertRADtoANGLE(angl+stepF)),
tiefeC);
glVertex3f(r*cos(convertRADtoANGLE(angl+stepF)), r*sin(convertRADtoANGLE(angl+stepF)),
0.0f);
glEnd ();
}
glPopMatrix();

// zeichne die von mir erdachte "cos-sin-Krone"


glColor3f(1.0f,0.0f,0.0f);
glPushMatrix();
glTranslatef(0.0f,0.0f,-15.0f);
double tiefe = 25.0f;
double nextStep = 15.0f;
double radius = 5.5f;
double paintAngle, nextStage;
glRotatef(_angle2*5, 0.1f, 0.1f, 1.0f);
for (double recentAngle = 0; recentAngle <= 360; recentAngle+=nextStep) {
glColor3f(cos(convertRADtoANGLE(recentAngle)), sin(convertRADtoANGLE(recentAngle)), 0.0f);
paintAngle = convertRADtoANGLE(recentAngle);
nextStage = convertRADtoANGLE(recentAngle+nextStep);
glBegin(GL_POLYGON);
glVertex3f(radius*cos(paintAngle),radius*sin(paintAngle),0.0f);
glVertex3f(radius*cos(paintAngle),radius*sin(paintAngle),tiefe-21.0f);
glVertex3f(radius*cos(nextStage),radius*sin(nextStage),tiefe);
glVertex3f(radius*cos(nextStage),radius*sin(nextStage),0.0f);
glEnd();
}
glPopMatrix();
}
}

// Zeit ermitteln und auf Bildschirm ausgeben je nach Spielmodus


stringstream outTime;
void writeTime() {
// Stream löschen
outTime.str("");
outTime.clear();

if(gamemode=="training") {
if (sek >= 60) {
sek = 0;
main.cpp — Printed on 29.12.2008, 13:28:56 — Page 15
minutes++;
}
if (sek < 10) outTime << "Time " << minutes << ":0" << sek << endl;
else {
outTime << "Time " << minutes << ":" << sek << endl;
}
}
else if (gamemode=="countdown") {
outTime << "Time: " << countDownTime << endl;
}
timeSpent = outTime.str();
}

// Viereck-Bild von Spongebob auf Boden zeichnen


void paintSpongebob(){
glDisable(GL_FOG);
glPushMatrix();
glEnable(GL_TEXTURE_2D);
glTranslatef(-7.0+(float)(scoreInt/3.0),-3.0,1.0);
glRotatef(scoreInt*5,0.0,1.0,0.0);
// Spongebob-Textur anwenden
glBindTexture(GL_TEXTURE_2D, _textureIdSponge);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

glBegin(GL_QUADS);
glNormal3f(0.0, 1.0f, 0.0f);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 3.0f);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
glTexCoord2f(1.0f, 1.0f);
glVertex3f(3.0f, 0.0f, 0.0f);
glTexCoord2f(0.0f, 1.0f);
glVertex3f(3.0f, 0.0f, 3.0f);
glEnd();

glDisable(GL_TEXTURE_2D);
glPopMatrix();
glEnable(GL_FOG);
}

// Alpha-Würfel wird größer, je mehr Punkte der Spieler erreicht


float cubeScale = 0.0f;
void paintAlphaCube() {
glDisable(GL_FOG);
glEnable(GL_BLEND); // alpha blending aktivieren
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Blendfunktion bestimmen
glPushMatrix();
cubeScale = 1.0+(float)scoreInt/20.0;
glScalef(cubeScale,cubeScale,cubeScale);
glTranslatef(0.0f,2.0f,-35.0f+float(scoreInt));
vector<Face*> faces;
faces.push_back(&(_cube.top));
faces.push_back(&(_cube.bottom));
faces.push_back(&(_cube.left));
faces.push_back(&(_cube.right));
faces.push_back(&(_cube.front));
faces.push_back(&(_cube.back));
// Seitenflächen sortieren von hinten nach vorne
sort(faces.begin(), faces.end(), compareFaces);
for(unsigned int i = 0; i < faces.size(); i++) {
drawFace(faces[i], _cube, _textureIdSponge);
main.cpp — Printed on 29.12.2008, 13:28:56 — Page 16
}
glPopMatrix();
glEnable(GL_FOG);
}

// gesamte Szene zeichnen (im Loop)


void drawScene() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0f, 0.0f, -8.0f);

// Licht einstellen
GLfloat ambientColor[] = {0.4f, 0.4f, 0.4f, 1.0f};
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientColor);
GLfloat lightColor0[] = {0.6f, 0.6f, 0.6f, 1.0f};
GLfloat lightPos0[] = {-0.5f, 0.5f, 1.0f, 0.0f};
glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor0);
glLightfv(GL_LIGHT0, GL_POSITION, lightPos0);

glScalef(0.5, 0.5, 0.5);


glColor3f(0.3f, 1.0f, 0.3f);
if(!startScreen) {
// Zeit für den Trainingsmodus
if (gamemode=="training") {
endTime = clock();
recentTime = (endTime - startTime)/1000;
if ( recentTime > 0) {
startTime = clock();
endTime = clock();
sek++;
}
}
// Timer für Modus Countdown
if (gamemode=="countdown") {
endTime = clock();
recentTime = (endTime - startTime)/1000;
if ( recentTime > 0) {
startTime = clock();
endTime = clock();
countDownTime--;
if (countDownTime <= 0) gameOver = true;
}
}
writeTime();
glUpdateParticles();
glDrawParticles();
}

// SPIELENDE
if (gameOver) {
// kalkuliere Punkte + Statistik
double result;
if (scoreInt <= 0) result = 0;
else result = double(COUNTDOWN)/double(scoreInt);
showGameOver(result);
}
// SPIELSTART
else if (startScreen) {
showStartScreen();
}
// SPIEL LÄUFT!
main.cpp — Printed on 29.12.2008, 13:28:56 — Page 17
else {
// bewege das Wort
moveAndPaintWord(wort->name);
// zeige bereits eingebenes Wort
showKeyboardInput();
// Grafikelemente zeichnen
paintFog();
paintFloor();
paintSpongebob();
paintFlyingCube();
paintAlphaCube();

glDisable(GL_DEPTH_TEST); // Depth Testing ausschalten (zeichnet somit nur auf Vordergrund)


showTimer();
showScore();
glEnable(GL_DEPTH_TEST); // Depth Testing wieder anschalten
glDisable(GL_BLEND); // Turn Blending Off
}

// blitzen, falls Wort richtig eingegeben


if (blitz==true) blitzen();

glutSwapBuffers();
}

// sich verändernde Paramter zur Belebung der Szene


void update(int value) {
wort->posZ += 0.1f;
_angle += 1.5f;
wort->angle += 0.1f;
_angle2 += 0.2f;
// alpha cube drehen
rotate(_cube, Vec3f(1, 1, 0), 1);
glutPostRedisplay();
glutTimerFunc(25, update, 0);
}

// Wortliste einlesen und verfügbar machen


void initWordlist(void) {
// Dateiname wählen
char datei[] = "wordlist.txt";
// Datei öffnen und einlesen
ifstream filestream(datei, ios::in);
// wenn Einlesen erfolgreich
if(filestream.good()) {
// Dateizeiger ans Ende der Datei positionieren
filestream.seekg(0L,ios::end);
// Ausgabe der Datei-Eigenschaften (Konsole)
cout << "Datei: " << datei << "\t Size: " << filestream.tellg() << " Bytes" << endl;
// Dateizeiger an den Datei-Anfang positionieren
filestream.seekg(0L,ios::beg);
// lese Wort für Wort ein:
for (int j = 0; j < WORTANZAHL; j++) {
filestream.getline(allWords[j],WORTANZAHL*MAXWORTLAENGE); //
http://www.cplusplus.com/reference/iostream/istream/getline.html
alleWoerter[j] = allWords[j];
}
}
else
cout << "Dateifehler oder Datei nicht gefunden!" << endl;
}
main.cpp — Printed on 29.12.2008, 13:28:56 — Page 18
int main(int argc, char** argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(WINWIDTH, WINHEIGHT);
glutCreateWindow("SpongeBob's Words 1.0");
initRendering();
initWordlist();
wort = new Word(createNewWord());
score = setScore(0);

glutDisplayFunc(drawScene);
glutKeyboardFunc(handleKeypress);
glutReshapeFunc(handleResize);
glutTimerFunc(25, update, 0);

// schreibe Timer-Startzeit
startTime = clock();

// weitere Initialisierungen
initParticles();
initCube(_cube);

glutMainLoop();
return 0;
}
text3d.h — Printed on 29.12.2008, 13:30:14 — Page 1
/* File for "Drawing Text" lesson of the OpenGL tutorial on
* www.videotutorialsrock.com
*/

#ifndef TEXT_3D_H_INCLUDED
#define TEXT_3D_H_INCLUDED

#include <string>

//Initializes 3D text. Must be called before other functions in this header.


void t3dInit();
//Frees memory allocated for 3D text. No other functions in this header may be
//called after this one.
void t3dCleanup();
/* Draws the specified string, using OpenGL, as a set of polygons in the x-y
* plane, with the top of the letters having the greatest y coordinate. The
* normals point in the positive z direction. (If you need the normals to point
* in the positive z direction on one side of the characters and the negative z
* direction on the other, call t3dDraw3D with a very small depth.)
*
* The string is drawn left-aligned if hAlign is negative, right-aligned if it
* is positive, and centered horizontally if it is 0. The string is drawn top-
* aligned if vAlign is negative, bottom-aligned if it is positive, and centered
* vertically if it is 0.
*
* The string may have newline characters, in which case the string will be
* drawn on multiple lines as one would expect. The lines are drawn lineHeight
* times the height of the font apart. The height of the font is the "normal"
* height of capital letters, rather than the distance from the top of "normal"
* capital letters to the bottom of lowercase letters like "p".
*
* All unprintable ASCII characters (other than '\n') are drawn as spaces.
*/
void t3dDraw2D(std::string str,
int hAlign, int vAlign,
float lineHeight = 1.5f);
/* Draws the specified string, using OpenGL, using polygons as a right prism,
* where the parallel faces are letters parallel to the x-y plane, with the top
* of the letters having the greatest y coordinate.
*
* The string is drawn left-aligned if hAlign is negative, right-aligned if it
* is positive, and centered horizontally if it is 0. The string is drawn top-
* aligned if vAlign is negative, bottom-aligned if it is positive, and centered
* vertically if it is 0.
*
* The string may have newline characters, in which case the string will be
* drawn on multiple lines as one would expect. The lines are drawn lineHeight
* times the height of the font apart. The height of the font is the "normal"
* height of capital letters, rather than the distance from the top of "normal"
* capital letters to the bottom of lowercase letters like "p".
*
* The depth of the characters is depth times the height of the font. The
* characters are centered at z = 0.
*
* All unprintable ASCII characters (other than '\n') are drawn as spaces.
*/
void t3dDraw3D(std::string str,
int hAlign, int vAlign,
float depth,
float lineHeight = 1.5f);
/* Returns the draw width of the specified string, as a multiple of the height
text3d.h — Printed on 29.12.2008, 13:30:14 — Page 2
* of the font. The height of the font is the "normal" height of capital
* letters, rather than the distance from the top of "normal" capital letters to
* the bottom of lowercase letters like "p". The width is the same as the width
* of the longest line.
*/
float t3dDrawWidth(std::string str);
/* Returns the draw height of the specified string, as a multiple of the height
* of the font. The height of the font is the "normal" height of capital
* letters, rather than the distance from the top of "normal" capital letters to
* the bottom of lowercase letters like "p". The draw is lineHeight times one
* fewer than the number of lines in the string, plus 1.
*/
float t3dDrawHeight(std::string str, float lineHeight = 1.5f);

//Indicates that an exception occurred when setting up 3D text


class T3DLoadException {
private:
std::string message0;
public:
T3DLoadException(std::string message1);
std::string message() const;
};

#endif
text3d.cpp — Printed on 29.12.2008, 13:29:58 — Page 1
/* File for "Drawing Text" lesson of the OpenGL tutorial on
* www.videotutorialsrock.com
*/

/* The 3D font is stored in an external file called "charset", which is


* basically a compact way of representing a series of OpenGL commands for
* drawing each of the printable ASCII characters, other than the space
* character (33 to 126). The file has the following format:
*
*
*
* the characters "VTR\0FNT\0"
* float space_width (the width of ' ', relative to the height the font)
*
* float char_33_scale
* unsigned short char_33_width (width = value * scale / 65536)
* unsigned short char_33_height (height = value * scale / 65536)
* unsigned short char_33_num_verts
* unsigned short char_33_vert_1_x (x = value * scale / 65536 + scale / 2)
* unsigned short char_33_vert_1_y (y = value * scale / 65536 + scale / 2)
* unsigned short char_33_vert_2_x (x = value * scale / 65536 + scale / 2)
* unsigned short char_33_vert_2_y (y = value * scale / 65536 + scale / 2)
* ...
* unsigned short char_33_vert_n_x (x = value * scale / 65536 + scale / 2)
* unsigned short char_33_vert_n_y (y = value * scale / 65536 + scale / 2)
* unsigned short opcode_1_for_char_33_front_face
* unsigned short opcode_2_for_char_33_front_face
* ...
* unsigned short opcode_n_for_char_33_front_face
* unsigned short end_part_opcode
* unsigned short opcode_1_for_char_33_3D_part
* unsigned short opcode_2_for_char_33_3D_part
* ...
* unsigned short opcode_n_for_char_33_3D_part
* unsigned short end_part_opcode
*
* float char_34_scale
* ...
*
*
* The character models are centered at (0, 0[, 0]).
*
* unsigned shorts are represented in little-endian format. floats are
* represented using one signed character exp followed by one signed integer
* mant, presented in little-endian format. This represents the number
* mant * 2^exp if mant is positive and -(~mant * 2^exp) if mant is negative.
*
* The opcodes are as follows:
*
* 0 to num_verts - 1:
* vertex with the same index as the opcode, using the vertex on the front
* face (the one with normal (0, 0, 1))
* num_verts to 2 * num_verts - 1:
* Vertex with index opcode - num_verts, using the vertex on the back face
* (the one with normal (0, 0, -1)). This opcode is only available for the
* 3D part of each model.
* 65532:
* Normal vector. Followed by an unsigned short indicating the angle of the
* vector divided by (2 pi) times 65536. The normal vector indicated is
* (cos theta, sin theta, 0). This opcode is only available for the 3D part
* of each model.
* 65533: GL_TRIANGLE_STRIP
* 65534: GL_TRIANGLES
text3d.cpp — Printed on 29.12.2008, 13:29:58 — Page 2
* 65535: end_part
*
* Vertices must be specified in counterclockwise order, or, in the case of
* triangle strips, they must be specified such that the first three vertices
* indicate a triangle in counterclockwise order. When specifying the 2D part
* of the model, counterclockwise order is relative to the front face.
*/

#include <fstream>
#include <math.h>

#ifdef __APPLE__
#include <OpenGL/OpenGL.h>
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif

#include "text3d.h"

using namespace std;

T3DLoadException::T3DLoadException(string message1) : message0(message1) {

string T3DLoadException::message() const {


return message0;
}

namespace {
//Converts a four-character array to an integer, using little-endian form
int toInt(const char* bytes) {
return int(((unsigned char)bytes[3] << 24) |
((unsigned char)bytes[2] << 16) |
((unsigned char)bytes[1] << 8) |
(unsigned char)bytes[0]);
}

//Converts a five-character array to a float, as indicated in the comment at


//the top of this file
float toFloat(const char* buffer) {
char exp = buffer[0];
int mant = toInt(buffer + 1);
bool isNegative;
if (mant < 0) {
isNegative = true;
mant = ~mant;
}
else
isNegative = false;
float a = (2147483648u + (unsigned int)mant) *
pow(2.0f, exp) / 2147483648.0;
return isNegative ? -a : a;
}

//Converts a two-character array to an unsigned short, using little-endian


//form
unsigned short toUShort(const char* buffer) {
return (((unsigned short)((unsigned char)buffer[1])) << 8) +
(unsigned short)((unsigned char)buffer[0]);
}
text3d.cpp — Printed on 29.12.2008, 13:29:58 — Page 3
//Just like auto_ptr, but for arrays
template<class T>
class auto_array {
private:
T* array;
mutable bool isReleased;
public:
explicit auto_array(T* array_ = NULL) :
array(array_), isReleased(false) {
}

auto_array(const auto_array<T> &aarray) {


array = aarray.array;
isReleased = aarray.isReleased;
aarray.isReleased = true;
}

~auto_array() {
if (!isReleased && array != NULL) {
delete[] array;
}
}

T* get() const {
return array;
}

T &operator*() const {
return *array;
}

void operator=(const auto_array<T> &aarray) {


if (!isReleased && array != NULL) {
delete[] array;
}
array = aarray.array;
isReleased = aarray.isReleased;
aarray.isReleased = true;
}

T* operator->() const {
return array;
}

T* release() {
isReleased = true;
return array;
}

void reset(T* array_ = NULL) {


if (!isReleased && array != NULL) {
delete[] array;
}
array = array_;
}

T* operator+(int i) {
return array + i;
}

T &operator[](int i) {
return array[i];
}
text3d.cpp — Printed on 29.12.2008, 13:29:58 — Page 4
};

enum Opcodes {OP_NORMAL = 65532,


OP_TRIANGLE_STRIP,
OP_TRIANGLES,
OP_END_PART};

const float PI_TIMES_2_OVER_65536 = 2 * 3.1415926535f / 65536.0f;

class T3DFont {
private:
float spaceWidth;
float widths[94];
GLuint displayListId2D;
GLuint displayListId3D;
public:
//Loads the specified font file into a new T3DFont object
T3DFont(ifstream &input) {
char buffer[8];
input.read(buffer, 8);
if (input.fail()) {
throw T3DLoadException("Invalid font file");
}

const char header[9] = "VTR\0FNT\0";


for(int i = 0; i < 8; i++) {
if (buffer[i] != header[i]) {
throw T3DLoadException("Invalid font file");
}
}

input.read(buffer, 5);
spaceWidth = toFloat(buffer);

displayListId2D = glGenLists(94);
displayListId3D = glGenLists(94);
for(int i = 0; i < 94; i++) {
input.read(buffer, 5);
float scale = toFloat(buffer) / 65536;
input.read(buffer, 2);
float width = scale * toUShort(buffer);
input.read(buffer, 2);
float height = scale * toUShort(buffer);
scale /= height;
widths[i] = width / height;
input.read(buffer, 2);
unsigned short numVerts = toUShort(buffer);
auto_array<float> verts(new float[2 * numVerts]);
float* verts2 = verts.get();
for(int j = 0; j < numVerts; j++) {
input.read(buffer, 2);
verts2[2 * j] = scale * ((int)toUShort(buffer) - 32768);
input.read(buffer, 2);
verts2[2 * j + 1] =
scale * ((int)toUShort(buffer) - 32768);
}

//Face part of the model


glNewList(displayListId2D + i, GL_COMPILE);

glNormal3f(0, 0, 1);

input.read(buffer, 2);
text3d.cpp — Printed on 29.12.2008, 13:29:58 — Page 5
unsigned short opcode = toUShort(buffer);
switch(opcode) {
case OP_TRIANGLES:
glBegin(GL_TRIANGLES);
break;
case OP_TRIANGLE_STRIP:
glBegin(GL_TRIANGLE_STRIP);
break;
default:
throw T3DLoadException("Invalid font file");
}

//Prevents excessive iteration or infinite loops on invalid


//font files
int limit = 10000;

while(true) {
input.read(buffer, 2);
opcode = toUShort(buffer);
switch(opcode) {
case OP_TRIANGLES:
glEnd();
glBegin(GL_TRIANGLES);
break;
case OP_TRIANGLE_STRIP:
glEnd();
glBegin(GL_TRIANGLE_STRIP);
break;
case OP_END_PART:
goto BreakOuter;
default:
glVertex3f(verts2[2 * opcode],
verts2[2 * opcode + 1],
0);
break;
}

if (--limit == 0) {
glEndList();
throw T3DLoadException("Invalid font file");
}
}
BreakOuter:
glEnd();
glEndList();

//3D part of the model


glNewList(displayListId3D + i, GL_COMPILE);
glPushMatrix();
glTranslatef(0, 0, 0.5f);
glFrontFace(GL_CW);
glCallList(displayListId2D + i);
glTranslatef(0, 0, -1);
glScalef(1, 1, -1);
glFrontFace(GL_CCW);
glCallList(displayListId2D + i);
glFrontFace(GL_CW);

input.read(buffer, 2);
opcode = toUShort(buffer);
switch(opcode) {
case OP_TRIANGLES:
glBegin(GL_TRIANGLES);
text3d.cpp — Printed on 29.12.2008, 13:29:58 — Page 6
break;
case OP_TRIANGLE_STRIP:
glBegin(GL_TRIANGLE_STRIP);
break;
default:
throw T3DLoadException("Invalid font file");
}

limit = 10000;
while(true) {
input.read(buffer, 2);
opcode = toUShort(buffer);
switch(opcode) {
case OP_TRIANGLES:
glEnd();
glBegin(GL_TRIANGLES);
break;
case OP_TRIANGLE_STRIP:
glEnd();
glBegin(GL_TRIANGLE_STRIP);
break;
case OP_NORMAL:
input.read(buffer, 2);
float angle;
angle = toUShort(buffer) *
PI_TIMES_2_OVER_65536;
float x, y;
x = cos(angle);
y = sin(angle);
glNormal3f(x, y, 0);
break;
case OP_END_PART:
goto BreakOuter2;
default:
if (opcode < numVerts) {
glVertex3f(verts2[2 * opcode],
verts2[2 * opcode + 1],
0);
}
else {
glVertex3f(verts2[2 * (opcode - numVerts)],
verts2[2 * (opcode - numVerts) +
1],
-1);
}
break;
}

if (--limit == 0) {
glEndList();
throw T3DLoadException("Invalid font file");
}
}
BreakOuter2:
glEnd();
glPopMatrix();
glEndList();
}

if (input.fail()) {
throw T3DLoadException("Invalid font file");
}
input.read(buffer, 1);
text3d.cpp — Printed on 29.12.2008, 13:29:58 — Page 7
if (!input.eof()) {
throw T3DLoadException("Invalid font file");
}
}

void draw2D(char c) {
if (c >= 33 && c <= 126) {
glCallList(displayListId2D + c - '!');
}
}

void draw3D(char c) {
if (c >= 33 && c <= 126) {
glCallList(displayListId3D + c - '!');
}
}

float width(char c) {
if (c >= 33 && c <= 126) {
return widths[c - 33];
}
else {
return spaceWidth;
}
}
};

T3DFont* font = NULL; //The font used to draw 2D and 3D characters

void draw2D(char c) {
font->draw2D(c);
}

void draw3D(char c) {
font->draw3D(c);
}

void drawLine(const char* str, int hAlign, void (*drawFunc)(char)) {


glPushMatrix();
if (hAlign >= 0) {
float width = 0;
for(int i = 0; str[i] != '\n' && str[i] != '\0'; i++) {
width += font->width(str[i]);
}
glTranslatef(hAlign > 0 ? -width : -width / 2, 0, 0);
}

for(int i = 0; str[i] != '\n' && str[i] != '\0'; i++) {


float width = font->width(str[i]);
glTranslatef(width / 2, 0, 0);
drawFunc(str[i]);
glTranslatef(width / 2, 0, 0);
}

glPopMatrix();
}

void draw(const char* str,


int hAlign, int vAlign,
float lineHeight,
void (*drawFunc)(char)) {
GLint shadeModel;
glGetIntegerv(GL_SHADE_MODEL, &shadeModel);
text3d.cpp — Printed on 29.12.2008, 13:29:58 — Page 8
glShadeModel(GL_SMOOTH);
GLboolean lightsEnabled;
glGetBooleanv(GL_LIGHTING, &lightsEnabled);
GLboolean normalsWereNormalized;
glGetBooleanv(GL_NORMALIZE, &normalsWereNormalized);
if (lightsEnabled) {
glEnable(GL_NORMALIZE);
}
else {
glDisable(GL_NORMALIZE);
}

glPushMatrix();
if (vAlign >= 0) {
int numLines = 1;
for(int i = 0; str[i] != '\0'; i++) {
if (str[i] == '\n') {
numLines++;
}
}

float height = lineHeight * (numLines - 1) + 1;


glTranslatef(0, vAlign > 0 ? height : height / 2, 0);
}

glTranslatef(0, -0.5f, 0);


drawLine(str, hAlign, drawFunc);
for(int i = 0; str[i] != '\0'; i++) {
if (str[i] == '\n') {
glTranslatef(0, -lineHeight, 0);
drawLine(str + i + 1, hAlign, drawFunc);
}
}

glPopMatrix();

glShadeModel(shadeModel);
if (normalsWereNormalized) {
glEnable(GL_NORMALIZE);
}
else {
glDisable(GL_NORMALIZE);
}
}
}

void t3dInit() {
if (font == NULL) {
ifstream input;
input.open("charset", istream::binary);
font = new T3DFont(input);
input.close();
}
}

void t3dCleanup() {
delete font;
}

void t3dDraw2D(string str, int hAlign, int vAlign, float lineHeight) {


GLboolean wasCulling;
glGetBooleanv(GL_CULL_FACE, &wasCulling);
glDisable(GL_CULL_FACE);
text3d.cpp — Printed on 29.12.2008, 13:29:58 — Page 9

draw(str.c_str(), hAlign, vAlign, lineHeight, draw2D);

if (wasCulling) {
glEnable(GL_CULL_FACE);
}
}

void t3dDraw3D(string str,


int hAlign, int vAlign,
float depth,
float lineHeight) {
GLboolean wasCulling;
glGetBooleanv(GL_CULL_FACE, &wasCulling);
glEnable(GL_CULL_FACE);
GLint frontFace;
glGetIntegerv(GL_FRONT_FACE, &frontFace);

glPushMatrix();
glScalef(1, 1, depth);
draw(str.c_str(), hAlign, vAlign, lineHeight, draw3D);
glPopMatrix();

if (!wasCulling) {
glDisable(GL_CULL_FACE);
}
glFrontFace(frontFace);
}

float t3dDrawWidth(string str) {


float bestWidth = 0;
int i = 0;
while (str[i] != '\0') {
float width = 0;
while (str[i] != '\n' && str[i] != '\0') {
width += font->width(str[i]);
i++;
}
if (width > bestWidth) {
bestWidth = width;
}
if (str[i] != '\0') {
i++;
}
}
return bestWidth;
}

float t3dDrawHeight(string str, float lineHeight) {


int numLines = 1;
for(int i = 0; str[i] != '\0'; i++) {
if (str[i] == '\n') {
numLines++;
}
}

return (numLines - 1) * lineHeight + 1;


}
imageloader.h — Printed on 29.12.2008, 13:27:48 — Page 1
/* File for "Fog" lesson of the OpenGL tutorial on
* www.videotutorialsrock.com
*/

#ifndef IMAGE_LOADER_H_INCLUDED
#define IMAGE_LOADER_H_INCLUDED

//Represents an image
class Image {
public:
Image(char* ps, int w, int h);
~Image();

/* An array of the form (R1, G1, B1, R2, G2, B2, ...) indicating the
* color of each pixel in image. Color components range from 0 to 255.
* The array starts the bottom-left pixel, then moves right to the end
* of the row, then moves up to the next column, and so on. This is the
* format in which OpenGL likes images.
*/
char* pixels;
int width;
int height;
};

//Reads a bitmap image from file.


Image* loadBMP(const char* filename);

#endif
imageloader.cpp — Printed on 29.12.2008, 13:27:33 — Page 1
/* File for "Fog" lesson of the OpenGL tutorial on
* www.videotutorialsrock.com
*/

#include <assert.h>
#include <fstream>

#include "imageloader.h"

using namespace std;

Image::Image(char* ps, int w, int h) : pixels(ps), width(w), height(h) {

Image::~Image() {
delete[] pixels;
}

namespace {
//Converts a four-character array to an integer, using little-endian form
int toInt(const char* bytes) {
return (int)(((unsigned char)bytes[3] << 24) |
((unsigned char)bytes[2] << 16) |
((unsigned char)bytes[1] << 8) |
(unsigned char)bytes[0]);
}

//Converts a two-character array to a short, using little-endian form


short toShort(const char* bytes) {
return (short)(((unsigned char)bytes[1] << 8) |
(unsigned char)bytes[0]);
}

//Reads the next four bytes as an integer, using little-endian form


int readInt(ifstream &input) {
char buffer[4];
input.read(buffer, 4);
return toInt(buffer);
}

//Reads the next two bytes as a short, using little-endian form


short readShort(ifstream &input) {
char buffer[2];
input.read(buffer, 2);
return toShort(buffer);
}

//Just like auto_ptr, but for arrays


template<class T>
class auto_array {
private:
T* array;
mutable bool isReleased;
public:
explicit auto_array(T* array_ = NULL) :
array(array_), isReleased(false) {
}

auto_array(const auto_array<T> &aarray) {


array = aarray.array;
imageloader.cpp — Printed on 29.12.2008, 13:27:33 — Page 2
isReleased = aarray.isReleased;
aarray.isReleased = true;
}

~auto_array() {
if (!isReleased && array != NULL) {
delete[] array;
}
}

T* get() const {
return array;
}

T &operator*() const {
return *array;
}

void operator=(const auto_array<T> &aarray) {


if (!isReleased && array != NULL) {
delete[] array;
}
array = aarray.array;
isReleased = aarray.isReleased;
aarray.isReleased = true;
}

T* operator->() const {
return array;
}

T* release() {
isReleased = true;
return array;
}

void reset(T* array_ = NULL) {


if (!isReleased && array != NULL) {
delete[] array;
}
array = array_;
}

T* operator+(int i) {
return array + i;
}

T &operator[](int i) {
return array[i];
}
};
}

Image* loadBMP(const char* filename) {


ifstream input;
input.open(filename, ifstream::binary);
assert(!input.fail() || !"Could not find file");
char buffer[2];
input.read(buffer, 2);
assert(buffer[0] == 'B' && buffer[1] == 'M' || !"Not a bitmap file");
input.ignore(8);
int dataOffset = readInt(input);
imageloader.cpp — Printed on 29.12.2008, 13:27:33 — Page 3
//Read the header
int headerSize = readInt(input);
int width;
int height;
switch(headerSize) {
case 40:
//V3
width = readInt(input);
height = readInt(input);
input.ignore(2);
assert(readShort(input) == 24 || !"Image is not 24 bits per pixel");
assert(readShort(input) == 0 || !"Image is compressed");
break;
case 12:
//OS/2 V1
width = readInt(input);
height = readInt(input);
input.ignore(2);
assert(readShort(input) == 24 || !"Image is not 24 bits per pixel");
break;
case 64:
//OS/2 V2
assert(!"Can't load OS/2 V2 bitmaps");
break;
case 108:
//Windows V4
assert(!"Can't load Windows V4 bitmaps");
break;
case 124:
//Windows V5
assert(!"Can't load Windows V5 bitmaps");
break;
default:
assert(!"Unknown bitmap format");
}

//Read the data


int bytesPerRow = ((width * 3 + 3) / 4) * 4 - (width * 3 % 4);
int size = bytesPerRow * height;
auto_array<char> pixels(new char[size]);
input.seekg(dataOffset, ios_base::beg);
input.read(pixels.get(), size);

//Get the data into the right format


auto_array<char> pixels2(new char[width * height * 3]);
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
for(int c = 0; c < 3; c++) {
pixels2[3 * (width * y + x) + c] =
pixels[bytesPerRow * y + 3 * x + (2 - c)];
}
}
}

input.close();
return new Image(pixels2.release(), width, height);
}
vec3f.h — Printed on 29.12.2008, 13:30:52 — Page 1
/* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above notice and this permission notice shall be included in all copies
* or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/* File for "Alpha Blending" lesson of the OpenGL tutorial on
* www.videotutorialsrock.com
*/

#ifndef VEC3F_H_INCLUDED
#define VEC3F_H_INCLUDED

#include <iostream>

class Vec3f {
private:
float v[3];
public:
Vec3f();
Vec3f(float x, float y, float z);

float &operator[](int index);


float operator[](int index) const;

Vec3f operator*(float scale) const;


Vec3f operator/(float scale) const;
Vec3f operator+(const Vec3f &other) const;
Vec3f operator-(const Vec3f &other) const;
Vec3f operator-() const;

const Vec3f &operator*=(float scale);


const Vec3f &operator/=(float scale);
const Vec3f &operator+=(const Vec3f &other);
const Vec3f &operator-=(const Vec3f &other);

float magnitude() const;


float magnitudeSquared() const;
Vec3f normalize() const;
float dot(const Vec3f &other) const;
Vec3f cross(const Vec3f &other) const;
};

Vec3f operator*(float scale, const Vec3f &v);


std::ostream &operator<<(std::ostream &output, const Vec3f &v);
vec3f.h — Printed on 29.12.2008, 13:30:52 — Page 2

#endif
vec3f.cpp — Printed on 29.12.2008, 13:30:28 — Page 1
/* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above notice and this permission notice shall be included in all copies
* or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/* File for "Alpha Blending" lesson of the OpenGL tutorial on
* www.videotutorialsrock.com
*/

#include <math.h>

#include "vec3f.h"

using namespace std;

Vec3f::Vec3f() {

Vec3f::Vec3f(float x, float y, float z) {


v[0] = x;
v[1] = y;
v[2] = z;
}

float &Vec3f::operator[](int index) {


return v[index];
}

float Vec3f::operator[](int index) const {


return v[index];
}

Vec3f Vec3f::operator*(float scale) const {


return Vec3f(v[0] * scale, v[1] * scale, v[2] * scale);
}

Vec3f Vec3f::operator/(float scale) const {


return Vec3f(v[0] / scale, v[1] / scale, v[2] / scale);
}

Vec3f Vec3f::operator+(const Vec3f &other) const {


return Vec3f(v[0] + other.v[0], v[1] + other.v[1], v[2] + other.v[2]);
}

Vec3f Vec3f::operator-(const Vec3f &other) const {


return Vec3f(v[0] - other.v[0], v[1] - other.v[1], v[2] - other.v[2]);
}
vec3f.cpp — Printed on 29.12.2008, 13:30:28 — Page 2

Vec3f Vec3f::operator-() const {


return Vec3f(-v[0], -v[1], -v[2]);
}

const Vec3f &Vec3f::operator*=(float scale) {


v[0] *= scale;
v[1] *= scale;
v[2] *= scale;
return *this;
}

const Vec3f &Vec3f::operator/=(float scale) {


v[0] /= scale;
v[1] /= scale;
v[2] /= scale;
return *this;
}

const Vec3f &Vec3f::operator+=(const Vec3f &other) {


v[0] += other.v[0];
v[1] += other.v[1];
v[2] += other.v[2];
return *this;
}

const Vec3f &Vec3f::operator-=(const Vec3f &other) {


v[0] -= other.v[0];
v[1] -= other.v[1];
v[2] -= other.v[2];
return *this;
}

float Vec3f::magnitude() const {


return sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
}

float Vec3f::magnitudeSquared() const {


return v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
}

Vec3f Vec3f::normalize() const {


float m = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
return Vec3f(v[0] / m, v[1] / m, v[2] / m);
}

float Vec3f::dot(const Vec3f &other) const {


return v[0] * other.v[0] + v[1] * other.v[1] + v[2] * other.v[2];
}

Vec3f Vec3f::cross(const Vec3f &other) const {


return Vec3f(v[1] * other.v[2] - v[2] * other.v[1],
v[2] * other.v[0] - v[0] * other.v[2],
v[0] * other.v[1] - v[1] * other.v[0]);
}

Vec3f operator*(float scale, const Vec3f &v) {


return v * scale;
}

ostream &operator<<(ostream &output, const Vec3f &v) {


cout << '(' << v[0] << ", " << v[1] << ", " << v[2] << ')';
return output;
vec3f.cpp — Printed on 29.12.2008, 13:30:28 — Page 3
}
Word.h — Printed on 29.12.2008, 13:31:22 — Page 1
#ifndef WORD_H_
#define WORD_H_

#include "const.h"
using namespace std;

class Word {
public:
int id;
string name; //[MAXWORTLAENGE];
float posX;
float posY;
float posZ;
float angle;
Word(string strWord);
virtual ~Word();
string getWord();
string createNewWord(void);
void initWordlist(void);
void moveWord(void);
float getRandomNr(int min, int max);
void resetWordPos(void);
};

#endif /*WORD_H_*/
Word.cpp — Printed on 29.12.2008, 13:31:09 — Page 1
#include <cstring>
#include <iostream>
#include <stdlib.h>

// für datei einlesen


#include <fstream>

#include "Word.h"

using namespace std;

Word::Word(string strWord) {
name = strWord;
posX = getRandomNr(-15, 15);
// posY = getRandomNr(-5, 10);
posY = 0.0f;
posZ = -20.0f;
angle = 5.0f;
}

Word::~Word()
{
}

string Word::getWord() {
cout << "transfered word: " << name << endl;
return name;
};

float Word::getRandomNr(int min, int max) {


// Initialisierung, nötig für Zufallszahl
srand ( time(NULL) );
// generiere die Zufallszahl
return (float) ( min + ( rand() % ( max - min + 1 ) ) )+0.5f;
}

void Word::resetWordPos(void) {
posX = getRandomNr(-15, 15);
// posY = getRandomNr(-5, 10);
posY = 0.0f;
posZ = -20.0f;
angle = getRandomNr(0, 5);
}
Projekt: Spielentwicklung in OpenGL - „SpongeBob’s Words“
Student: Kai Kajus Noack
Datum: 2008-01-20

1. Einleitung
Das von mir entwickelte Spiel „SpongeBob’s Words” stellt dem Spieler die Aufgabe, aus dem
Hintergrund anfliegende Wörter korrekt mit der Tastatur einzugeben. Dabei stören ihn diverse
grafische Objekte, indem sie ihm die Sicht auf die Wörter versperren. Am Ende des Spiels wird der
Spieler mit einer statistischen Auswertung belohnt, die ihm Fehlerquotient und die Anschläge pro
Minute angibt.

2. Bestandteile des Programms

Datei Beschreibung
const.h enthält alle Konstanten des Spiels
imageloader.cpp + .h Funktionalität zum Laden einer Bitmap-Datei
main.cpp + .h Spielfunktionalität und OpenGL-Zeichnen
text3d.cpp + .h 3D-Text-Zeichner
vec3f.cpp + .h Vektorklassen
Word.cpp + .h Word-Spielobjekt
wordlist.txt stellt abzufragende Wörter bereit
charset (Byte-Datei) enthält Code zur Darstellung des 3D-Textes
img/floor.bmp Textur für den Untergrund des Spiels
img/spongebob.bmp Textur für Spongebob-Elemente

Beim Spielstart stehen dem Spieler die Spielmodi


Countdown und Training zur Verfügung. Training
ist dabei endlos, der Countdown zählt 60
Sekunden abwärts.

Countdown ist der spannende Teil, da dieser den


Spieler unter Druck setzt. Am Ende des Spiels ist
eine Statistik wie links zu sehen eingebaut, die die
erbrachte Leistung analysiert.

3. Benutzereingaben im Spiel
• a-z - Eingabe der Buchstaben
• Enter - Eingegebenes Wort bestätigen
- Neustart des Spiels (bei Game Over)
• Esc - Verlassen des Spiels

Seite 1 von 4
4. Effekte, Elemente und Techniken
Sämtlicher Code wurde von mir in den Cpp- und Header-Dateien ausführlich kommentiert und ist
leicht nachvollziehbar. Trotzdem möchte ich kurz auf die im Spiel eingebauten Effekte, Elemente und
Techniken eingehen. Die nachfolgende Grafik zeigt einen Großteil davon:

Die main()-Methode in der main.cpp nimmt zu Beginn die Initialisierungen für alle Elemente des Spiels
vor und definiert die Handler-Methoden. In der initRendering() werden die Texturen geladen und
sämtliche Schalter für Licht, Tiefentest, Shade-Modell, Normalen und Nebel aktiviert. In der
drawScene() befinden sich die Anweisungen, ob Start- oder GameOver-Screen oder das laufende
Spiel gezeichnet werden soll.

Zeit + Punkte
Diese Elemente sind dynamisch, werden mithilfe der text3d-Klasse gezeichnet und befinden sich stets
im Vordergrund, da ich vor dem Zeichnen den GL_DEPTH_TEST ausschalte. Farblich habe ich sie rot
hervorgehoben, damit sie während des Spiels leicht erkennbar sind.
Für das Funktionieren des Timers und der Punkt-Anzeige sind verschiedene Zähl-, Timer- und String-
Variablen sowie Stringstreams (für das Casten von Integer zu String) nötig geworden.

Partikeleffekt
Für den Partikeleffekt habe ich Elemente aus einem Tutorial von videotutorialsrock.com genutzt und
diese implementiert. Jeder Partikel ist ein Objekt mit Positions-, Farb-, Geschwindigkeits- und Größen-
Eigenschaften, die über die Methode glCreateParticles() zu Beginn des Spiels festgelegt und mit der
glUpdateParticles() während des Spiels modifiziert werden. Die glDrawParticles() zeichnet die
Partikelobjekte nach dem Update der Partikeldaten mit den neuen Werten. Die y-Position wurde von
mir im Übrigen so verändert, dass die Partikel sich weniger stark in der Höhe, sondern sich in der
Breite des „Himmels“ des Spiels verteilen.

Würfel mit Alphakanal


Für diesen Würfel konnte ich ebenfalls Informationen aus videotutorialsrock.com nutzen. Die Seiten-
flächen (Faces) des Würfels werden als Objekte definiert und über Zuhilfenahme der Vektorklasse
vec3f.cpp rotiert. Es wird geprüft, welche Seite dem Spieler zugewandt ist und daraufhin die sechs
Seiten unter Berücksichtigung der Alpha-Werte gezeichnet.
Die Seitenflächen des Würfels sind mit Texturen bekleidet, die ich in der main-Klasse lade (vgl.
Methode loadTexture) und verfügbar mache.
Zu beachten war hier, dass der Nebel (GL_FOG) vor dem Zeichnen des Alphawürfels ausgeschaltet
werden musste, da dieser sonst fahl-grau aussah.

Seite 2 von 4
Übrigens wird der Würfel im Spielverlauf mit den richtigen Eingaben des Spielers größer skaliert und
bewegt sich auf ihn zu.

Anfliegendes Wort
Für jedes Wort wird ein Objekt „Word“ erstellt, mit zufälligen Positions-Parametern für die x-Ebene.
Der Text wird über die text3d-Klasse gezeichnet. Die Zeilenhöhe und -breite sowie die Texttiefe sind
über die Methode t3dDraw3D() frei wählbar. Die 3D-Buchstaben werden aus der Byte-Datei charset
ausgelesen und in der text3d-Klasse als Schriftart (Font) verwendet.
Jedes Wort kommt aus dem Hintergrund. Wenn es jedoch hinter dem Spieler verschwindet, es also
den Sichtbereich verlässt, erscheint es wieder von Neuem im Hintergrund. Alle zweitausend mög-
lichen Wörter werden aus der wordlist.txt eingelesen (vgl. Methode initWordlist), in der main-Klasse in
ein Array gelegt und dort weiter genutzt. Über eine Zufallszahl wird eins der Wörter ausgewählt und an
die zeichnende Methode und an die Methoden der Spiellogik übergeben.

Rotierender Würfel mit ambientem Licht


Dieser Würfel wird über die Methode paintFlyingCube() erstellt. Jeder Vertex erhält seine eigene
Normale, sodass durch das gewählte Smooth-Shading (vgl. Methode initRendering) ein leichter Farb-
verlauf auftritt. Weiterhin wird der Würfel über eine dynamische Variable rotiert. Gegebenenfalls kann
ein zusätzliches rötliches Licht gesetzt werden (vgl. Auskommentierung im Quellcode).

Textur auf Textur


Für die Spongebob-Platte wird eine Textur aus der „spongebob.bmp“ mittels der loadTexture()-
Methode erstellt. Der Nebeleffekt (weiter unten erklärt) ist vor dem Zeichnen der Platte auszu-
schalten, da ansonsten die Textur farblich sehr matt dargestellt wird.

Reflexion des Wortes


Die Implementierung der Reflexion (vgl. Methode moveAndPaintWord) erfolgte durch das nochmalige
Zeichnen des Wortes, einer Hellblaufärbung, der Rotation um 180° um die x-Achse und der
Translationen abwärts (y-Achse) und nach hinten (z-Achse).

Bodenplatte mit Textur


Die Bodenplatte erhält eine Wasser-Textur unter Verwendung der Bilddatei „floor.bmp“. Hier wurde im
Gegensatz zur Zeichnung der ersten Textur der Nebel nicht ausgeschaltet, sodass ein in den Horizont
ins Weiße verschwimmender Effekt auftritt.

Eingaben des Spielers


Die Tastatureingaben des Spielers werden rechts unten
gezeichnet. Hierzu wird das eingegebene Zeichen direkt an die
Methode showKeyboardInput() übergeben.
Die Eingaben werden über Methode checkUserInput() geprüft
und entsprechend vom Programm reagiert.

Implementierung des Blitz-Effekts


Sofern der Spieler das Wort richtig eingegeben und mit Enter bestätigt hat, blitzt es 5 mal
hintereinander hell auf. Dieser Effekt wird über einen Boolean-Wert, eine Zählvariable und eine if-
Bedingung (modular) gesteuert und generiert. Der gesamte Bildschirm wird abwechselnd kurz weiß
und dann wieder normal gezeichnet, wodurch der Eindruck eines Blitzes entsteht (vgl. Methode
blitzen(), glClearColor).

Nebeleffekt
Der Nebel wird in der Methode paintFog() angeschaltet, dessen Farbe bestimmt sowie Start- und
Endwert in Richtung z-Achse gesetzt. In dieser Methode verbirgt sich übrigens auch der geheime
psychomode, der mit der Tastatureingabe „000“ de-/aktiviert werden kann. Für diesen Modus habe ich
mir die Sinus- und Cosinusfunktionen zu Hilfe genommen und ein kronenartiges, rotierendes Objekt
geschaffen. Dieser Modus ist sehr farbenfroh ☺

Seite 3 von 4
Start-Screen
Wie auch bei den anderen Text-Zeichnungen wird hier die
Methode t3dDraw3D() verwendet und zuvor muss, wie bereits ein
paar Mal aufgeführt, der Nebel ausgeschaltet werden.

Game-Over-Screen und Statistik


Das “Game Over” links im Bild rotiert über eine Cosinus-Funktion,
der Partikeleffekt läuft weiter und die kalkulierten Statistiken
werden dem Spieler dargestellt.

Weitere Methoden:
- update() dynamisiert die Werte der Objekte
- convertRADtoANGLE() konvertiert Radianz-Werte in Winkel
- Round() rundet auf die angegebene Anzahl an Nachkommastellen
- handleResize() beachtet die Veränderung der Fenstergröße.

5. Fazit
Es hat wirklich Spaß gemacht, ein funktionierendes Spiel mit Logik und Grafik zu entwerfen, auch
wenn ich dafür 15 Tage Arbeit, also etwa 70-80 Stunden investieren musste. Der Mehraufwand
entstand, da C++ bisher nicht Bestandteil unseres Studiums war.

Ich freue mich sehr, dass „Spongebob’s Words!“ gut bei den Kommilitonen ankommt und dazu anregt,
das Spiel mehr als nur einmal zu spielen.

Kai Kajus Noack

6. Quellen & Referenzen

- „Computer Graphics“ Vorlesungsskripte (IMI-Master) von Herrn Dr. Eisert


- „OpenGL SuperBible 4th Edition” (Wright, Lipchak, Haemel)
- „OpenGL Programming Guide“ Addison Wesley
- „Eine Einführung in OpenGL” der Uni Augsburg (pdf-Link)

Online:
http://www.videotutorialsrock.com/
http://www.cplusplus.com/reference/
http://www.swiftless.com/tutorials/opengl/opengltuts.html
http://www-h.eng.cam.ac.uk/help/tpl/languages/C++/FAQ.html

Ressourcen:
Aus www.videotutorialsrock.com wurden entnommen:
- charset
- imageloader.cpp
- text3d.cpp
- vec3f.cpp

Die Spongebob-Grafik wurde modifiziert und entstammt aus:


http://www.poster.de/Spongebob/Spongebob-SpongeBob-SquarePants-9962695.html

Seite 4 von 4

You might also like