// lab6.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <windows.h>
#include <gl/GL.h>
#include <gl/GLU.h>
#include <gl/glut.h>
#include <stdio.h>
#include <math.h>
#include <string>
//#include <FreeImage.h>
//#include <vld.h>
#include "imageLoader.cpp"
#define PI 3.14159265
#define BYTE unsigned char
#define LEFT_X 0
#define RIGHT_X 1
#define LEFT_Y 2
#define RIGHT_Y 3
#define LEFT_Z 4
#define RIGHT_Z 5
// Структура точки в трехмерном пространстве
struct point {
double x, y, z;
};
// Класс правильной прямой призмы
class Prism
{
private:
double h; // Высота призмы
double a; // Длина стороны основания призмы
double R; // Радиус окружности, описанной около основания
double r; // Радиус окружности, вписанной в основание
double t; // Параметр преобразования в пирамиду (параметр анимации), от 0 до 1
BYTE n; // Количество вершин основания
point *C; // Массив точек, описывающий относительные координаты вершин
point *N; // Массив координат нормалей
void calculate_vertices(); // Заполняет массив C, вычисляя относительные координаты вершин по h, a и n.
void calculate_normals(); // Заполняет массив N, вычисляя координаты нормалей
public:
Prism(double h, double a, BYTE n); // Прототип конструктора
virtual ~Prism(); // Прототип деструктора
double get_h() { return h; } // Возвращает высоту
double get_a() { return a; } // Возвращает длину стороны основания
BYTE get_n() { return n; } // Возвращающий количество вершин основания
void set_h(double h) { // Меняет высоту и пересчитывает координаты вершин
this->h = h;
calculate_vertices();
}
void set_a(double a) { // Меняет длину стороны основания и пересчитывает координаты вершин
this->a = a;
calculate_vertices();
}
void set_n(BYTE n) { // Меняет количество вершин и пересчитывает их координаты
this->n = n;
delete [] C;
delete [] N;
C = new point[2 * n];
N = new point[2 * n];
calculate_vertices();
}
void animate(double t);
void draw(); // Прототип функции отрисовки, принимающей координату отрисовки
};
Prism::Prism(double h, double a, BYTE n) {
this->h = h;
this->a = a;
this->n = n;
t = 0;
C = new point[2 * n];
N = new point[2 * n];
calculate_vertices();
}
Prism::~Prism() {
delete [] C;
delete [] N;
}
void Prism::calculate_vertices() {
if (n < 1) return;
R = a / (2 * sin(PI / n));
r = a / (2 * tan(PI / n));
for (int i = 0; i < n; i++) {
C[i].x = R * cos(2 * PI * i / n);
C[i].y = R * sin(2 * PI * i / n);
C[i].z = -h/2;
C[n+i].x = C[i].x;
C[n+i].y = C[i].y;
C[n+i].z = h/2;
}
animate(t);
}
void Prism::calculate_normals() {
if (n < 2) return;
double r = sqrt((C[0].x - C[n].x)*(C[0].x - C[n].x) + (C[0].y - C[n].y)*(C[0].y - C[n].y));
double h = C[n].z - C[0].z;
double th = h*r*r/(r*r + h*h);
double tr = h*h*r/(r*r + h*h);
double k = h*h/(r*r + h*h);
//double k = tr/R;
for (int i = 0; i < n; i++) {
int j = i + 1;
if (j == n) j = 0;
//N[i].x = (C[i].x - C[n+i].x + C[j].x - C[n+j].x)*k/2;
//N[i].y = (C[i].y - C[n+i].y + C[j].y - C[n+j].y)*k/2;
N[i].x = (C[i].x + C[j].x)*k/2;
N[i].y = (C[i].y + C[j].y)*k/2;
N[i].z = th;
}
//double cx = (C[n].x + C[n+1].x)/2;
//double cy = (C[n].y + C[n+1].y)/2;
//r = sqrt(cx*cx + cy*cy);
r = this->r * (1 - t);
h = this->h/2 - C[n].z;
//printf("this->h = %f, C[n].z = %f, this->h - C[n].z = %f\n", this->h, C[n].z, this->h - C[n].z);
th = h*r*r/(r*r + h*h);
k = h*h/(r*r + h*h);
if (r == 0) {
for (int i = 0; i < n; i++) {
N[i+n].x = 0.0;
N[i+n].y = 0.0;
N[i+n].z = 1.0;
}
} else {
for (int i = 0; i < n; i++) {
int j = i + 1;
if (j == n) j = 0;
N[i+n].x = (C[i+n].x + C[j+n].x)*k/2 + N[i].x*t;
N[i+n].y = (C[i+n].y + C[j+n].y)*k/2 + N[i].y*t;
N[i+n].z = th + N[i].z*t + (1 - t)*(1 - t)*(1 - t)*(1 - t)*(1 - t)*(1 - t);
}
}
}
void Prism::animate(double t) {
double R = a / (2 * sin(PI / n));
for (int i = 0; i < n; i++) {
C[n+i].x = (1 - t)*C[i].x;// + 3*(1-t)*(1-t)*t + 3*(1-t)*t*t;
C[n+i].y = (1 - t)*C[i].y;// + 3*(1-t)*(1-t)*t + 3*(1-t)*t*t;
C[n+i].z = (1 - t)*(1 - t)*(1 - t)*h/2 + t*t*t*h/2;
}
this->t = t;
calculate_normals();
}
void Prism::draw() {
if (n < 1) return;
for (int i = 0; i < n; i++) {
int j = i + 1;
if (j == n) j = 0;
// GL_QUADS
glBegin( GL_QUADS );
//glNormal3f(C[i].x + C[j].x, C[i].y + C[j].y, 0);
glNormal3f(N[i].x, N[i].y, N[i].z);
glTexCoord2d(0, (double)i*a/h);
glVertex3d(C[i].x, C[i].y, C[i].z);
glTexCoord2d(0, (double)(i+1)*a/h);
glVertex3d(C[j].x, C[j].y, C[j].z);
glTexCoord2d(1, (double)(i+1)*a/h);
glVertex3d(C[j+n].x, C[j+n].y, C[j+n].z);
glTexCoord2d(1, (double)i*a/h);
glVertex3d(C[i+n].x, C[i+n].y, C[i+n].z);
glEnd();
// GL_TRIANGLE
glBegin( GL_TRIANGLES );
glNormal3f(C[i].x, C[i].y, -0.5);
glTexCoord2d(0, (double)i*a/h);
glVertex3d(C[i].x, C[i].y, C[i].z);
glNormal3f(C[j].x, C[j].y, -0.5);
glTexCoord2d(0, (double)(i+1)*a/h);
glVertex3d(C[j].x, C[j].y, C[j].z);
glNormal3f(0, 0, -1.0);
glTexCoord2d(1, (double)i*a/h);
glVertex3d(0, 0, -h/2);
glEnd();
// GL_TRIANGLE
//printf("N[%d].x = %f, N[%d].y = %f, N[%d].z = %f\n", i+n, N[i+n].x, i+n, N[i+n].y, i+n, N[i+n].z);
glBegin( GL_TRIANGLES );
glNormal3d(N[i+n].x, N[i+n].y, N[i+n].z);
//glNormal3f(C[i+n].x, C[i+n].y, 0.5);
//glNormal3f(C[i].x/4, C[i].y/4, h);
glTexCoord2d(0, (double)i*a/h);
glVertex3d(C[i+n].x, C[i+n].y, C[i+n].z);
//glNormal3f(C[j+n].x, C[j+n].y, 0.5);
//glNormal3d(C[j].x/4, C[j].y/4, h);
glTexCoord2d(0, (double)(i+1)*a/h);
glVertex3d(C[j+n].x, C[j+n].y, C[j+n].z);
glNormal3d(0, 0, 1.0);
glTexCoord2d(1, (double)i*a/h);
glVertex3d(0, 0, h/2);
glEnd();
}
}
struct SCENE_FORMAT {
double h; // Высота призмы
double a; // Длина стороны основания призмы
BYTE n; // Количество углов основания призмы
bool solid; // true - твердотельное отображение объектов, false - каркасное
bool light0; // Источник света №1
bool light1; // Источник света №2
bool light2; // Источник света №3
bool texture; // Отображение текстуры
bool animation; // Прокрутка анимации
bool direction; // Направление анимации
double t; // Параметр анимации
double rotate_x; // Угол поворота призмы по оси x
double rotate_y; // Угол поворота по оси y призмы, повернутой по оси x на угол rotate_x
double rotate_z; // Угол поворота по сои z призмы, повернутой по осям x и y на углы rotate_x и rotate_y соответственно
double scale; // Коэффициент сжатия призмы
};
// Класс сцены содержит условно-глобальные переменные и функции
class Scene {
public:
~Scene();
int screen_height, screen_width; // Высота и ширина окна просмотра
double screen_ratio; // Отношение высоты к ширине
Prism *prism; // Объект призма
BYTE verteces_count; // Количество уголов основания призмы
bool solid; // true - твердотельное отображение объектов, false - каркасное
bool light0; // Источник света №1
bool light1; // Источник света №2
bool light2; // Источник света №3
bool texture; // Отображение текстуры
bool animation; // Прокрутка анимации
bool direction; // Направление анимации
double t; // Параметр анимации
double rotate_x; // Угол поворота призмы по оси x
double rotate_y; // Угол поворота по оси y призмы, повернутой по оси x на угол rotate_x
double rotate_z; // Угол поворота по сои z призмы, повернутой по осям x и y на углы rotate_x и rotate_y соответственно
double scale; // Коэффициент сжатия призмы
BMPimage *img; // Текстура объекта
char *file_name; // Имя файла с описанием сцены
void initialization();
void save();
void load();
void toggle_polygon_mode();
void set_texture(bool on);
void toogle_texture();
void set_light(int n, bool on);
void toogle_light(int n);
void set_animation(bool anim);
void toggle_animation();
void translate(double x, double y, double z);
void rotate(double angle, char axis);
//void scale(double s);
void draw_cube(double a);
};
Scene::~Scene() {
delete prism;
delete img;
}
Scene scene;
void Scene::save() {
//glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat*)&modelview_matrix);
SCENE_FORMAT dump = { prism->get_h(), prism->get_a(), verteces_count, solid, light0, light1, light2, texture, animation, direction, t, rotate_x, rotate_y, rotate_z, scale};
//for (int i = 0; i < 16; i++) dump.modelview_matrix[i] = modelview_matrix[i];
FILE *pFile = fopen (file_name,"w");
if (pFile != NULL) {
fwrite((void*)&dump, sizeof(SCENE_FORMAT), 1, pFile);
fclose (pFile);
printf("scene has been saved\n");
} else {
printf("cannot open file for writing :(\n");
return;
}
}
void Scene::load() {
SCENE_FORMAT dump;
FILE *pFile = fopen (file_name,"r");
if (pFile != NULL) {
fread((void*)&dump, sizeof(SCENE_FORMAT), 1, pFile);
fclose (pFile);
printf("scene has been loaded\n");
} else {
printf("cannot open file for reading :(\n");
return;
}
prism->set_h(dump.h);
prism->set_a(dump.a);
prism->set_n(dump.n);
verteces_count = dump.n;
solid = dump.solid;
set_light(0, dump.light0);
set_light(1, dump.light1);
set_light(2, dump.light2);
set_texture(dump.texture);
set_animation(dump.animation);
direction = dump.direction;
t = dump.t;
rotate_x = dump.rotate_x;
rotate_y = dump.rotate_y;
rotate_z = dump.rotate_z;
scale = dump.scale;
//glMatrixMode( GL_MODELVIEW );
//glLoadMatrixf(dump.modelview_matrix);
}
void Scene::toggle_polygon_mode() {
solid = !solid;
}
void Scene::set_texture(bool on) {
if (on) glEnable(GL_TEXTURE_2D);
else glDisable(GL_TEXTURE_2D);
texture = on;
}
void Scene::toogle_texture() {
if (texture) {
glDisable(GL_TEXTURE_2D);
texture = false;
} else {
glEnable(GL_TEXTURE_2D);
texture = true;
}
}
void animation_function() {
double step = 0.001;
double t = scene.t;
bool d = scene.direction;
if (d) t -= step;
else t += step;
if (t > 1.0) {
t = 1.0 - step;
d = 1;
}
if (t < 0) {
t = step;
d = 0;
}
//printf("%f\n", t);
scene.t = t;
scene.direction = d;
scene.prism->animate(t);
glutPostRedisplay();
//printf("animation! ");
}
void Scene::set_animation(bool anim) {
animation = anim;
if (animation) {
glutIdleFunc(animation_function);
} else {
glutIdleFunc(NULL);
}
}
void Scene::toggle_animation() {
set_animation(!animation);
}
void Scene::set_light(int n, bool on) {
switch (n) {
case 0:
if (on) glEnable(GL_LIGHT0); else glDisable(GL_LIGHT0);
light0 = on;
break;
case 1:
if (on) glEnable(GL_LIGHT1); else glDisable(GL_LIGHT1);
light1 = on;
break;
case 2:
if (on) glEnable(GL_LIGHT2); else glDisable(GL_LIGHT2);
light2 = on;
break;
}
}
void Scene::toogle_light(int n) {
switch (n) {
case 0:
if (!light0) glEnable(GL_LIGHT0); else glDisable(GL_LIGHT0);
light0 = !light0;
break;
case 1:
if (!light1) glEnable(GL_LIGHT1); else glDisable(GL_LIGHT1);
light1 = !light1;
break;
case 2:
if (!light2) glEnable(GL_LIGHT2); else glDisable(GL_LIGHT2);
light2 = !light2;
break;
}
}
void Scene::translate(double x, double y, double z) {
glMatrixMode( GL_MODELVIEW );
glTranslated(x, y, z);
}
void Scene::rotate(double angle, char axis) {
glMatrixMode( GL_MODELVIEW );
if (axis == 'x') glRotated(angle, 1, 0, 0);
else if (axis == 'y') glRotated(angle, 0, 1, 0);
else glRotated(angle, 0, 0, 1);
}
/*void Scene::scale(double s) {
glMatrixMode( GL_MODELVIEW );
glScaled(s, s, s);
}*/
unsigned int picture;
void Scene::draw_cube(double a) {
double h = a/2;
glBegin( GL_QUADS );
glNormal3f(0, 0, -1.0);
glVertex3d( h, h, -h);
glVertex3d( h, -h, -h);
glVertex3d(-h, -h, -h);
glVertex3d(-h, h, -h);
glNormal3f(0, 0, 1.0);
glVertex3d( h, h, h);
glVertex3d( h, -h, h);
glVertex3d(-h, -h, h);
glVertex3d(-h, h, h);
glNormal3f(0, -1.0, 0);
glVertex3d( h, -h, h);
glVertex3d( h, -h, -h);
glVertex3d(-h, -h, -h);
glVertex3d(-h, -h, h);
glNormal3f(0, 1.0, 0);
glVertex3d( h, h, h);
glVertex3d( h, h, -h);
glVertex3d(-h, h, -h);
glVertex3d(-h, h, h);
glNormal3f(-1.0, 0, 0);
glVertex3d(-h, h, h);
glVertex3d(-h, h, -h);
glVertex3d(-h, -h, -h);
glVertex3d(-h, -h, h);
glNormal3f(1.0, 0, 0);
glVertex3d( h, h, h);
glVertex3d( h, h, -h);
glVertex3d( h, -h, -h);
glVertex3d( h, -h, h);
glEnd();
}
void Scene::initialization() {
glLineWidth(0.5); // Ширина линии
glPointSize(1.0); // Ширина точки
glClearColor(0.9f, 0.9f, 0.9f, 0.9f); // Цвет фона - RGB, alpha
glColor3f(0.1f, 0.4f, 0.4f); // Текущий цвет - RGB
//float pos[4] = {3,3,3,1};
//float dir[3] = {-1,-1,-1};
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
// Глобальные параметры модели освещения
glEnable(GL_NORMALIZE);
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
// Локальные источники света
float pos[4] = {0,0,-20,0.0};
float dir[4] = {0,0,0, 1.0};
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glLightfv(GL_LIGHT0, GL_POSITION, pos);
glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 180.0);
glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, dir);
// Спецификация материалов
GLfloat mat_specular[] = {1,1,1,1 };
//glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glShadeModel(GL_FLAT || GL_SMOOTH);
prism = new Prism(0.80, 0.20, verteces_count);
img = new BMPimage("texture.bmp");
img->image->fill_data();
glEnable(GL_TEXTURE_2D);
//glGenTextures(1, &picture);
//glBindTexture(GL_TEXTURE_2D, picture);
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, img->image->w, img->image->h, GL_RGB, GL_UNSIGNED_BYTE, img->image->data);
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, ( float )GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, ( float )GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, ( float )GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, ( float )GL_REPEAT);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, ( float )GL_MODULATE);
// Работа с матрицей проекции
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
double fz = 0.5; // Коэффициент искажения по z
double phi = asin(fz/(sqrt(2.0)))*57.3; // Переводим в градусы
double tetta = asin(fz/sqrt(2-fz*fz))*57.3;
//glOrtho(-screen_height/2, screen_height/2, -screen_height/2, screen_height/2, -200, 200);
glRotated(tetta, 1, 0, 0);
glRotated(phi, 0, 1, 0);
// Работа с модельно-видовой матрицей
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
// Функция отображения
void display_handler()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); // Очистка буфера цвета
GLfloat front_ambient[] = {0.8, 0.8, 0.8, 1.0};
GLfloat front_diffuse[] = {0.8, 0.8, 0.8, 1.0};
GLfloat front_specular[] = {0.6, 0.6, 0.6, 1.0};
glMaterialfv(GL_FRONT, GL_AMBIENT, front_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, front_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, front_specular);
glMaterialf(GL_FRONT, GL_SHININESS, 90.0);
//glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, front_color);
//glMatrixMode(GL_PROJECTION);
//glLoadIdentity();
//glOrtho(0.0, scene.screen_width, 0.0, scene.screen_height, -200, 200);
if (scene.solid) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
else glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// Преобразование поворота
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
scene.rotate(scene.rotate_x, 'x');
scene.rotate(scene.rotate_y, 'y');
scene.rotate(scene.rotate_z, 'z');
glScaled(scene.scale, scene.scale, scene.scale);
scene.prism->draw();
//scene.draw_cube(0.50);
glPopMatrix();
//scene.img->image->draw();
//glFlush(); // Сиюминутная отрисовка
glutSwapBuffers();
}
// Обработчик изменения рамеров окна
void reshape_handler(int w, int h) {
/*double R = scene.screen_ratio; // Изначальное отношение сторон окна
double r = (double)h/w; // Текущее отношение сторон окна
if (R < r) // Если изначальное отношение меньше текущего,
h = w*R; // то подгоняем высоту под ширину,
else w = h/R; // в противном случае - ширину под высоту
*/
/*glViewport(0, 0, (GLsizei)w, (GLsizei)h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(40.0, (GLfloat)w/h, 1, 100.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0, 0.0, 8.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);*/
if (w > h) glViewport((w-h)/2, 0, h, h);
else glViewport(0, (h-w)/2, w, w);
scene.screen_height = h;
scene.screen_width = w;
}
// Обработчик нажатия клавиш
void keyboard_handler(unsigned char key, int x, int y) {
double rotate_step = 5.0;
double scale_factor = 0.99;
switch(key) {
case '2': // Увеличить количество вершин основания призмы
scene.prism->set_n(++scene.verteces_count);
break;
case '1': // Уменьшить количество вершин основания призмы
scene.prism->set_n(--scene.verteces_count);
break;
case 'r': // Уменьшить количество вершин основания призмы
scene.scale *= scale_factor;
//scene.scale( scale_factor);
break;
case 't': // Уменьшить количество вершин основания призмы
scene.scale /= scale_factor;
//scene.scale((double)1/scale_factor);
break;
case ' ': // Переключение между каркасным и твердотельным отображением
scene.toggle_polygon_mode();
break;
case 'w': // Повороты по всем осям:
scene.rotate_x += rotate_step;
break;
case 's':
scene.rotate_x -= rotate_step;
break;
case 'q':
scene.rotate_y += rotate_step;
break;
case 'e':
scene.rotate_y -= rotate_step;
break;
case 'a':
scene.rotate_z += rotate_step;
break;
case 'd':
scene.rotate_z -= rotate_step;
break;
case 'z': // Управление светом
scene.toogle_light(0);
break;
case 'x': // Управление светом
scene.toogle_light(1);
break;
case 'c': // Управление светом
scene.toogle_light(2);
break;
case 'v': // Включение/отключение текстуры
scene.toogle_texture();
break;
case ' ': // Остановка/возобновление анимации
scene.toggle_animation();
break;
case '[':
scene.save();
break;
case ']':
scene.load();
break;
}
glutPostRedisplay();
}
void main(int argc, char** argv)
{
// Инициализация глобальных переменных
scene.screen_width = 600;
scene.screen_height = 600;
scene.screen_ratio = (double)scene.screen_height / scene.screen_width;
scene.verteces_count = 12;
scene.solid = true;
scene.animation = false;
scene.texture = true;
scene.t = 0;
scene.direction = 0;
scene.file_name = "prism.scene";
scene.light0 = true;
scene.light1 = true;
scene.rotate_x = 0;
scene.rotate_y = 0;
scene.rotate_z = 0;
scene.scale = 1.0;
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGB);
glutInitWindowSize(scene.screen_width, scene.screen_height);
glutInitWindowPosition(100, 100);
glutCreateWindow("Model transformations & projections, accurate images");
glutKeyboardFunc(keyboard_handler);
glutDisplayFunc(display_handler);
glutReshapeFunc(reshape_handler);
scene.initialization();
glutMainLoop(); // Цикл обработки событий
}