Задача: Построить 3D модель кубика-рубика используя графическую библиотеку OpenGL. Кубик рубик размера 3*3*3 состоит из 27 мелких кубиков. Следующий класс - класс самого кубика-рубика (Cube). Клавиши в промежутке ['0', '5'] - команды поворота грани. Файл "Small_Cube.h" #ifndef _SMALL_CUBE_H #define _SMALL_CUBE_H #include "D:\\Common\\OpenGLSB.h" // тут подключение библиотек glut, glu, gl // класс частей кубика-рубика, большой кубик будет состоять из 27 маленьких. struct Small_Cube { // шесть граней куба - шесть цветов unsigned int color[6]; // (верх, низ, впереди, сзади, лево, право) // размер ребра double size; Small_Cube() { // по умолчанию черный цвет memset(color, 0, sizeof(color)); size = 0.0; } // поворот на плоскости X0Y void rotateZ() { unsigned int tmp = color[5]; color[5] = color[3]; color[3] = color[4]; color[4] = color[2]; color[2] = tmp; } // поворот на плоскости X0Z void rotateY() { unsigned int tmp = color[2]; color[2] = color[1]; color[1] = color[3]; color[3] = color[0]; color[0] = tmp; } // поворот на плоскости Y0Z void rotateX() { unsigned int tmp = color[0]; color[0] = color[4]; color[4] = color[1]; color[1] = color[5]; color[5] = tmp; } void setColor(int i, int color) { this->color[i] = color; } unsigned char *at(int i) { // разбиваем color[i] на 3 составляющих // например для 0xFF0000 RGB(FF, 0, 00) - красный цвет; _color[0] = color[i] >> 16; _color[1] = color[i] >> 8; _color[2] = color[i] ; return _color; } // отрисовка куба: // устанавливаем цвет и нормали void draw() { glPushMatrix(); glBegin(GL_QUADS); // верх glColor3ubv(at(0)); glNormal3f(0, 0, 1); glVertex3f(size, size, size); glVertex3f(0, size, size); glVertex3f(0, 0, size); glVertex3f(size, 0, size); // низ glColor3ubv(at(1)); glNormal3f(0, 0, -1); glVertex3f(size, 0, 0); glVertex3f(0, 0, 0); glVertex3f(0, size, 0); glVertex3f(size, size, 0); // спереди glColor3ubv(at(2)); glNormal3f(0, -1, 0); glVertex3f(size, 0, size); glVertex3f(0, 0, size); glVertex3f(0, 0, 0); glVertex3f(size, 0, 0); // сзади glColor3ubv(at(3)); glNormal3f(0, 1, 0); glVertex3f(size, size, 0); glVertex3f(0, size, 0); glVertex3f(0, size, size); glVertex3f(size, size, size); // слева glColor3ubv(at(4)); glNormal3f(-1, 0, 0); glVertex3f(0, size, size); glVertex3f(0, size, 0); glVertex3f(0, 0, 0); glVertex3f(0, 0, size); // справа glColor3ubv(at(5)); glNormal3f(1, 0, 0); glVertex3f(size, size, 0); glVertex3f(size, size, size); glVertex3f(size, 0, size); glVertex3f(size, 0, 0); glEnd(); glPopMatrix(); } // отрисовка куба со смещением (x, y, z) void draw(double x, double y, double z) { glPushMatrix(); glTranslated(x, y, z); draw(); glPopMatrix(); } private: unsigned char _color[4]; }; #endif; Файл "Cube.h" #ifndef _CUBE_H #define _CUBE_H #include "Small_Cube.h" class Cube { // 27 частей Small_Cube a[3][3][3]; // храним угол поворота каждой грани int rotate[6]; // размер кубика-рубика double size; // цвета граней unsigned int color[6]; public: // храним номер грани, которая в данный момент поварачивается, или -1 если ничего не поварачивается int current; Cube() { } Cube(double size, unsigned int *color) { clear(size, color); } void clear(double size, unsigned int *color) { memset(rotate, 0, sizeof(rotate)); this->size = size; current = -1; int i, j, k; for(i = 0; i < 6; i++) this->color[i] = color[i]; // верх for(i = 0; i < 3; i++) for(j = 0; j < 3; j++) a[i][j][2].setColor(0, color[0]); // низ for(i = 0; i < 3; i++) for(j = 0; j < 3; j++) a[i][j][0].setColor(1, color[1]); // спереди for(k = 0; k < 3; k++) for(j = 0; j < 3; j++) a[j][0][k].setColor(2, color[2]); // сзади for(k = 0; k < 3; k++) for(j = 0; j < 3; j++) a[j][2][k].setColor(3, color[3]); // слева for(i = 0; i < 3; i++) for(k = 0; k < 3; k++) a[0][k][i].setColor(4, color[4]); // справа for(i = 0; i < 3; i++) for(k = 0; k < 3; k++) a[2][k][i].setColor(5, color[5]); // устанавливаем размеры мелких деталей // это будет треть всего размера, умноженная на коэффициент немного меньший еденицы // (чтобы детали не были слишком плотно) for(i = 0; i < 3; i++) for(j = 0; j < 3; j++) for(k = 0; k < 3; k++) a[i][j][k].size = (size / 3.0) * 0.95; } void draw() { const double K = 0.65; // рисуем корпус - это просто куб черного цвета, размер которого равен K*size glPushMatrix(); glColor3f(0, 0, 0); glTranslatef(((1.0 - K)/2)*size + K*size/2, ((1.0 - K)/2)*size + K*size/2, ((1.0 - K)/2)*size + K*size/2); glutSolidCube(size * K); glPopMatrix(); // ok[i][j][k] показывает, находится ли в состоянии покоя деталь с координатами (i, j, k) memset(ok, true, sizeof(ok)); if (current != -1) { glPushMatrix(); int i, j, k; if (current == 0 || current == 1) { // 0 <= current <= 1 показывает, что сейчас крутится грань на плоскости X0Y // current = 0 - нижняя часть // current = 1 - верхняя часть k = (current & 1) * 2; // следовательно ok слоя k устанавливаем в false for(i = 0; i < 3; i++) for(j = 0; j < 3; j++) ok[i][j][k] = false; // теперь нужно покрутить грань под номером current на угол rotate[current] // относительно центра этой грани // для этого сдвинемся к центру, покрутим, сдвинемся обратно glTranslated(size / 2, size / 2, 0); // сдвигаемся к центру glRotatef(rotate[current], 0, 0, 1); // крутим glTranslated(-size / 2, -size / 2, 0); // сдвигаемся обратно // рисуем for(i = 0; i < 3; i++) for(j = 0; j < 3; j++) a[i][j][k].draw(size / 3 * i, size / 3 * j, size / 3 * k); } // аналагично с остальными четырмя гранями else if (current == 2 || current == 3) { j = (current & 1) * 2; for(i = 0; i < 3; i++) for(k = 0; k < 3; k++) ok[i][j][k] = false; glTranslated(size / 2, 0, size / 2); glRotatef(rotate[current], 0, 1, 0); glTranslated(-size / 2, 0, -size / 2); for(i = 0; i < 3; i++) for(k = 0; k < 3; k++) a[i][j][k].draw(size / 3 * i, size / 3 * j, size / 3 * k); } else if (current == 4 || current == 5) { i = (current & 1) * 2; for(j = 0; j < 3; j++) for(k = 0; k < 3; k++) ok[i][j][k] = false; glTranslated(0, size / 2, size / 2); glRotatef(rotate[current], 1, 0, 0); glTranslated(0, -size / 2, -size / 2); for(j = 0; j < 3; j++) for(k = 0; k < 3; k++) a[i][j][k].draw(size / 3 * i, size / 3 * j, size / 3 * k); } glPopMatrix(); } for(int i = 0; i < 3; i++) for(int j = 0; j < 3; j++) for(int k = 0; k < 3; k++) if (ok[i][j][k]) // теперь рисуем те детали, которые не поварачивались выше, // они отмечены ok[i][j][k] = true a[i][j][k].draw(size / 3 * i, size / 3 * j, size / 3 * k); } public: void rot90(int idx, int sign) { int i, j, k; // sign задаётся в зависимости он направления // sign = -1, sign = 1 // если sign = -1, значит крутим 3 раза if (sign == -1) sign = 3; while(sign--) { if (idx == 0 || idx == 1) { // низ/верх k = (idx & 1) * 2; // копируем повёрнутую на 90 градусов верхнюю/нижнюю грань // в массив tmp, затем грани присваиваем tmp // и не забываем повернуть каждую деталь этой грани for(i = 0; i < 3; i++) for(j = 0; j < 3; j++) tmp[j][2 - i] = a[i][j][k]; for(i = 0; i < 3; i++) for(j = 0; j < 3; j++) tmp[i][j].rotateZ(), a[i][j][k] = tmp[i][j]; } // аналогично с остальными четырмя гранями else if (idx == 2 || idx == 3) { // лево/право j = (idx & 1) * 2; for(i = 0; i < 3; i++) for(k = 0; k < 3; k++) tmp[k][2 - i] = a[i][j][k]; for(i = 0; i < 3; i++) for(k = 0; k < 3; k++) tmp[i][k].rotateX(), a[i][j][k] = tmp[i][k]; } else if (idx == 4 || idx == 5) { // впереди/сзади i = (idx & 1) * 2; for(j = 0; j < 3; j++) for(k = 0; k < 3; k++) tmp[k][2 - j] = a[i][j][k]; for(j = 0; j < 3; j++) for(k = 0; k < 3; k++) tmp[j][k].rotateY(), a[i][j][k] = tmp[j][k]; } } } // крутит грань под номером idx на угол angle (в градусах) void Rotate(int idx, int angle) { // мы пытаемся покрутить грань с номером idx // значит нужно проверить что другая грань уже не крутится if (current == -1 || current == idx) { // обновляем поворот rotate[idx] += angle; if (rotate[idx] % 90 != 0) { current = idx; } else { // если угол стал кратным 90, то поварачиваем на массиве if ((rotate[idx] < 0) ^ (current == 2 || current == 3)) rot90(idx, 1); else rot90(idx, -1); rotate[idx] = 0; current = -1; } } } private: int _angle[4]; bool ok[4][4][4]; Small_Cube tmp[4][4]; }; #endif; Файл "Cube.cpp" #include "Cube.h" #include <time.h> #include <stdlib.h> #define CUBE_SIZE 13 #define TIMER 30 // обозначаем цвета: // (верх, низ, впереди, сзади, лево, право) unsigned int c[9] = {0xFFFFFF, 0xFFFF00, 0x0000FF, 0x00FF00, 0xFF0000, 0xCD853F}; // координаты источника света GLfloat lightPos[] = {0, 100, 200, 0}; // проекции угла поворота на оси int xRot = 24, yRot = 34, zRot = 0; // отдаление double translateZ = -35.0; // кубик-рубик Cube cube; // флаг того, крутится куб сам, или нет (будет переключаться правой кнопкой мыши) int timerOn = 0; void display() { glPushMatrix(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glColor3f(1, 0, 0); glLightfv(GL_LIGHT0, GL_POSITION, lightPos); glTranslatef(0, 0, translateZ); glRotatef(xRot, 1, 0, 0); glRotatef(yRot, 0, 1, 0); glTranslatef(CUBE_SIZE / -2.0, CUBE_SIZE / -2.0, CUBE_SIZE / -2.0); cube.draw(); glPopMatrix(); glutSwapBuffers(); } void reshape(int w, int h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); GLfloat fAspect = (GLfloat)w/(GLfloat)h; gluPerspective(60, fAspect, 1, 1000.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void init() { glClearColor(1.0, 1.0, 1.0, 0.0); // инициализируем случайные числа srand(time(0)); // освещение float mat_specular[] = {0.3, 0.3, 0.3, 0}; float diffuseLight[] = {0.2, 0.2, 0.2, 1}; float ambientLight[] = {0.9, 0.9, 0.9, 1.0}; glShadeModel(GL_SMOOTH); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMateriali(GL_FRONT, GL_SHININESS, 128); glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight); glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight); glEnable(GL_LIGHT0); glEnable(GL_COLOR_MATERIAL); glColorMaterial(GL_FRONT, GL_AMBIENT); glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); // инициализируем куб cube.clear(CUBE_SIZE, c); } void specialKeys(int key, int, int) { // клавиши влево/вправо вращают по Y // клавиши вверх/вниз вращают по X // F1 - возвращает в начальное положение if (key == GLUT_KEY_DOWN) { xRot += 3; if (xRot >= 360) xRot -= 360; glutPostRedisplay(); } if (key == GLUT_KEY_UP) { xRot -= 3; if (xRot < 0) xRot += 360; glutPostRedisplay(); } if (key == GLUT_KEY_RIGHT) { yRot += 3; if (yRot >= 360) yRot -= 360; glutPostRedisplay(); } if (key == GLUT_KEY_LEFT) { yRot -= 3; if (yRot < 0) yRot += 360; glutPostRedisplay(); } if (key == GLUT_KEY_HOME) { translateZ += 5; glutPostRedisplay(); } if (key == GLUT_KEY_END) { translateZ -= 5; glutPostRedisplay(); } if (key == GLUT_KEY_F1) { cube.clear(CUBE_SIZE, c); glutPostRedisplay(); } } void keys(unsigned char key, int, int) { // если нажали клавишу от 0 до 5 - начинаем поворот на 3 градуса if (cube.current == -1 && key >= '0' && key < '6') { cube.Rotate(key - '0', 3); display(); } } void mouse(int key, int state, int, int) { if (key == GLUT_RIGHT_BUTTON && state == GLUT_DOWN) { // переключаем флаг timerOn = 1 - timerOn; } } void timer(int) { glutTimerFunc(TIMER, timer, 0); if (timerOn) { // если включен автоматический поворот, и смотрим // если сейчас никакая грань не крутится, то начинаем крутить случайную, // иначе крутим текущую if (cube.current == -1) keys(rand() % 6 + '0', 0, 0); else cube.Rotate(cube.current, 3); } else { if (cube.current != -1) cube.Rotate(cube.current, 3); } display(); } int main(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(800, 700); glutInitWindowPosition(1, 1); glutCreateWindow("Cube"); init(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutKeyboardFunc(keys); glutMouseFunc(mouse); glutTimerFunc(TIMER, timer, 0); glutSpecialFunc(specialKeys); glutMainLoop(); return 0; } P.S. Эту реализацию можно переделать на случай большего размера кубика.
Ключевые слова:
Кубик-рубик, моделирование, OpenGl, C++
|
|||||||