/*
* r 位置リセット
* m 射影切り替え
* w, a: 平行移動 Y
* s, d: 平行移動 X
* q, e: 平行移動 Z
* j, J: 回転 X
* k, K: 回転 Y
*/
#include <iostream>
#include <GL/glut.h>
#include <math.h>
#include <stdlib.h>
const double PI = 3.14159265;
/* 視点の位置 */
const double eye_pos[3] = {0.0, 0.0, 1.9};
//変換行列
double transformation_mat[4][4]={
{1.0, 0.0, 0.0, 0.0},
{0.0, 1.0, 0.0, 0.0},
{0.0, 0.0, 1.0, 0.0},
{0.0, 0.0, 0.0, 1.0}
};
// 変形させる対象
const int object_nv = 4; // 頂点数
const double object_vertices[][object_nv][3] = { // 頂点の座標
{ {+0.5, -0.5, +0.5}, // +X
{+0.5, -0.5, -0.5},
{+0.5, +0.5, -0.5},
{+0.5, +0.5, +0.5}, },
{ {-0.5, -0.5, +0.5}, // -X
{-0.5, +0.5, +0.5},
{-0.5, +0.5, -0.5},
{-0.5, -0.5, -0.5}, },
{ {+0.5, +0.5, +0.5}, // +Y
{+0.5, +0.5, -0.5},
{-0.5, +0.5, -0.5},
{-0.5, +0.5, +0.5}, },
{ {+0.5, -0.5, +0.5}, // -Y
{-0.5, -0.5, +0.5},
{-0.5, -0.5, -0.5},
{+0.5, -0.5, -0.5}, },
{ {-0.5, -0.5, +0.5}, // +Z
{+0.5, -0.5, +0.5},
{+0.5, +0.5, +0.5},
{-0.5, +0.5, +0.5}, },
{ {-0.5, -0.5, -0.5}, // -Z
{-0.5, +0.5, -0.5},
{+0.5, +0.5, -0.5},
{+0.5, -0.5, -0.5}, },
};
// 面数
const int object_nf = sizeof(object_vertices)/sizeof(object_vertices[1]);
/* 色 */
double object_color[object_nf][3] = {
{ 1.0, 0.0, 0.0},
{ 0.0, 1.0, 0.0},
{ 0.0, 0.0, 1.0},
{ 0.0, 1.0, 1.0},
{ 1.0, 0.0, 1.0},
{ 1.0, 1.0, 0.0},
};
/* 面情報構造体 */
typedef struct Face_ {
double depth;
int index;
} Face;
// 面情報
Face faces[object_nf];
// 射影
int projection_mode = 0;
/* 頂点コピー */
void copy_object(double dest[][object_nv][3], const double src[][object_nv][3], int nf) {
for (int i = 0; i < nf; i++) {
for (int j = 0; j < object_nv; j++) {
for (int k = 0; k < 3; k++) {
dest[i][j][k] = src[i][j][k];
}
}
}
}
//引数を強制的に単位行列にする
void setIdentity44(double mat[4][4]){
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
if(i == j) mat[i][j] = 1.0;
else mat[i][j] = 0.0;
}
}
}
void copy44(double out_mat[4][4], const double in_mat[4][4]){
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
out_mat[i][j] = in_mat[i][j];
}
}
}
//4*4行列の掛け算。out_matとin_mat1,in_mat2は同じでも大丈夫
void multiply44by44(double out_mat[4][4], const double in_mat1[4][4], const double in_mat2[4][4]){
double temp_mat1[4][4];
copy44(temp_mat1, in_mat1);
double temp_mat2[4][4];
copy44(temp_mat2, in_mat2);
for(int i=0; i<4; i++){
for(int j=0; j<4; j++){
out_mat[i][j] = temp_mat1[i][0] * temp_mat2[0][j] +
temp_mat1[i][1] * temp_mat2[1][j] +
temp_mat1[i][2] * temp_mat2[2][j] +
temp_mat1[i][3] * temp_mat2[3][j];
}
}
}
//平行移動行列の生成。mat:結果、tx:x方向の移動量、ty:y方向の移動量
void makeTranslationMatrix(double mat[4][4], const double tx, const double ty, const double tz){
mat[0][0] = 1.0; mat[0][1] = 0.0; mat[0][2] = 0.0; mat[0][3] = tx;
mat[1][0] = 0.0; mat[1][1] = 1.0; mat[1][2] = 0.0; mat[1][3] = ty;
mat[2][0] = 0.0; mat[2][1] = 0.0; mat[2][2] = 1.0; mat[2][3] = tz;
mat[3][0] = 0.0; mat[3][1] = 0.0; mat[3][2] = 0.0; mat[3][3] = 1.0;
}
void setTranslation(const double tx, const double ty, const double tz){
double translation_mat[4][4];
makeTranslationMatrix(translation_mat, tx, ty, tz);
multiply44by44(transformation_mat, translation_mat, transformation_mat);
}
//X軸周りの回転行列の生成。mat:結果、r:回転角
void makeRotationXMatrix(double mat[4][4], const double r){
mat[0][0] = 1; mat[0][1] = 0; mat[0][2] = 0; mat[0][3] = 0;
mat[1][0] = 0; mat[1][1] = cos(r); mat[1][2] = -sin(r); mat[1][3] = 0;
mat[2][0] = 0; mat[2][1] = sin(r); mat[2][2] = cos(r); mat[2][3] = 0;
mat[3][0] = 0; mat[3][1] = 0; mat[3][2] = 0; mat[3][3] = 1;
}
void setRotationX(const double r){
double rotation_mat[4][4];
makeRotationXMatrix(rotation_mat, r);
multiply44by44(transformation_mat, rotation_mat, transformation_mat);
}
//Y軸周りの回転行列の生成。mat:結果、r:回転角
void makeRotationYMatrix(double mat[4][4], const double r){
mat[0][0] = cos(r); mat[0][1] = 0; mat[0][2] = sin(r); mat[0][3] = 0;
mat[1][0] = 0; mat[1][1] = 1; mat[1][2] = 0; mat[1][3] = 0;
mat[2][0] = -sin(r); mat[2][1] = 0; mat[2][2] = cos(r); mat[2][3] = 0;
mat[3][0] = 0; mat[3][1] = 0; mat[3][2] = 0; mat[3][3] = 1;
}
void setRotationY(const double r){
double rotation_mat[4][4];
makeRotationYMatrix(rotation_mat, r);
multiply44by44(transformation_mat, rotation_mat, transformation_mat);
}
//4*4行列Aと4(実際は3)*1行列vの掛け算Avの計算。vertex:v(破壊されて結果が入る)、mat:A
void transform(double vertex[], double mat[4][4]){
double temp_x = vertex[0];
double temp_y = vertex[1];
double temp_z = vertex[2];
for(int i=0; i<3; i++){
vertex[i] = temp_x * mat[i][0] + temp_y * mat[i][1] +
temp_z * mat[i][2] + 1.0 * mat[i][3];
}
}
/* 重心深度算出 ※ 結果は比較に使用するだけなのでsqrtしない */
double face_depth(const double eye[3], const double vertices[][3]) {
double c[3] = {0.0, 0.0, 0.0}; /* 重心 */
double d = 0.0;
for (int i = 0; i < object_nv; i++) {
for (int j = 0; j < 3; j++) {
c[j] += vertices[i][j];
}
}
for (int j = 0; j < 3; j++) {
c[j] /= object_nv;
double t = eye[j] - c[j];
d += t * t;
}
return d; /* return sqrt(d); としないでよい */
}
/* 面の重心深度比較 */
int compare_faces_depth(const void *lhs, const void *rhs) {
Face *l = (Face*)(lhs), *r = (Face*)(rhs);
return l->depth > r->depth ? -1 : (l->depth < r->depth ? 1 : 0);
}
/* 面表示 */
void display_faces(Face faces[], double vertices[][object_nv][3], double color[][3],
int nf) {
glBegin(object_nv == 4 ? GL_QUADS : object_nv == 3 ? GL_TRIANGLES : GL_LINE_STRIP); {
double s = 1.0;
for(int i = 0; i < nf; i++) {
int idx = faces[i].index;
glColor3f(color[idx][0], color[idx][1], color[idx][2]);
for(int j = 0; j < object_nv; j++) {
double t = projection_mode == 0 ?
s / (eye_pos[2] - vertices[idx][j][2]) : 1.0;
glVertex2d(t * vertices[idx][j][0], t * vertices[idx][j][1]);
}
}
} glEnd();
}
/* 物体描画 */
void display_object(double vertices[][object_nv][3], double color[][3], int nf){
// 座標変換
for(int i = 0; i < nf; i++) {
for(int j = 0; j < object_nv; j++) {
transform(vertices[i][j], transformation_mat);
}
}
/* 面 */
for(int i = 0; i < nf; i++) {
faces[i].depth = face_depth(eye_pos, vertices[i]);
faces[i].index = i;
}
qsort(&faces, nf, sizeof(Face), compare_faces_depth); /* 深度ソート */
// 表示
display_faces(faces, vertices, color, nf);
}
/* 描画 */
void display(void){
glClear(GL_COLOR_BUFFER_BIT);
//座標軸の表示
glBegin(GL_LINES); {
glColor3f(1.0, 1.0, 1.0);
glVertex2d(-1.0, 0.0);
glVertex2d(1.0, 0.0);
glVertex2d(0.0, -1.0);
glVertex2d(0.0, 1.0);
} glEnd();
/* 物体描画 */
double vertices[object_nf][object_nv][3];
copy_object(vertices, object_vertices, object_nf);
display_object(vertices, object_color, object_nf);
glFlush();
}
/* インプット */
void key(unsigned char key, int x, int y) {
switch (key) {
case 'r': setIdentity44(transformation_mat); break;
case 'm': projection_mode ^= 1; break;
case 'w': setTranslation( 0.0, +0.1, 0.0); break;
case 'a': setTranslation(-0.1, 0.0, 0.0); break;
case 's': setTranslation( 0.0, -0.1, 0.0); break;
case 'd': setTranslation(+0.1, 0.0, 0.0); break;
case 'q': setTranslation( 0.0, 0.0, -0.1); break;
case 'e': setTranslation( 0.0, 0.0, +0.1); break;
case 'j': setRotationX( M_PI/8); break;
case 'J': setRotationX(-M_PI/8); break;
case 'k': setRotationY( M_PI/8); break;
case 'K': setRotationY(-M_PI/8); break;
default: break;
}
glutPostRedisplay();
}
int main(int argc, char *argv[]){
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA);
glutCreateWindow(argv[0]);
glClearColor(0.0, 0.0, 0.0, 0.0);
glutDisplayFunc(display);
glutKeyboardFunc(key);
glutMainLoop();
return 0;
}