マウスやキーボードを使った3D図形の幾何学変換

前回:ポリゴンの集合による3D図形の描写
今回は、前回OpenGLで作った図形の幾何学変換を、マウスやキーボードのコールバック関数でしてみる。

初期設定

  • ライブラリの参照とグローバル変数
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include  <GL/glut.h>

// Global variables
#define ROTATE 1
#define TRANSLATE 2
float angle = 0.0f, a_x = 0.0f, a_y = 0.0f, a_z = 0.0f, cur_angle_x = 0.0f, cur_angle_y = 0.0f;
float move = 0, move_x = 0.0f, move_y = 0.0f, move_z = 0.0f;
int clicking = 0;
int prev_x = 0, prev_y = 0;
float x_in = 1, y_in = 1, z_in = 1, px_in = 0, py_in = 0, pz_in = 0;
int mode = ROTATE;
  • 初期化は前回と同様
void init()
{
	glClearColor(0.0, 0.0, 0.0, 1.0);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(-10.0, 10.0, -10.0, 10.0, -10.0, 100.0);
	glEnable(GL_DEPTH_TEST);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

3D図形情報

  • 今回は、ポイント、カラー、ポリゴンの点の数などの図形情報をあらかじめ配列に格納しておく。
//stroe all the vertecies I use
float vertex[] = {-0.5, 5.2, 3.0,     0.5, 5.2, 3.0,     0.5, 4.7, 3.0,     -0.5, 4.7, 3.0,
	// ...略
};

//color for each face
float color[] = {0.0, 0.7, 0.1,     0.0, 0.8, 0.1,     0.0, 0.7, 0.2,     0.1, 0.7, 0.1,     0.9, 0.9, 0.0,
	// ...略
};

//number of vertecies for each face
int nVertex[] = {4,4,4,4,4,4,4,4,4,4,
	// ...略
};

幾何学変換

glRotatefを使った回転
void rotate(float an, float x, float y, float z)
{
	float current[16]; //OpenGLは、16要素の配列を4*4行列として扱う
	glGetFloatv(GL_MODELVIEW_MATRIX, current);//現在の行列を取得
	glMatrixMode(GL_MODELVIEW);//現在の行列をモデルビュー行列に変換
	glLoadIdentity();//単位行列に初期化

	glRotatef(an, x, y, z);//回転

	glMultMatrixf(current);
}
glTranslatefを使った移動
void translate(float dx, float dy, float dz)
{
	float current[16];
	glGetFloatv(GL_MODELVIEW_MATRIX, current);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	glTranslatef(dx, dy, dz);

	glMultMatrixf(current);
}

キーボード、マウスからの入力

マウス関数
void mouse(int button, int state, int x, int y)
{
	if (button == GLUT_LEFT_BUTTON)//左クリック
	{
		if (state == GLUT_DOWN)//マウスが押された状態
		{
			prev_x = x;
			prev_y = y;
			clicking = 1; //clicking is 1 while the mouse is kept pressed
		}
		else if (state == GLUT_UP)
		{
			clicking = 0;
		}
	}
}
  • 左クリックが押されている間中、clickingがtureかつ、prev_x, prev_yで座標を更新し続ける。
  • 指が話された段階でflagを下げる
マウスのモーション関数
  • 以下の2通りのモードを作る
    • ROTATE: マウスの動いた量によって物体を回転するモード
    • TRANSLATE: マウスの動いた量によって物体を移動するモード
void motion(int x, int y)
{
	if (mode == ROTATE)//マウスが動いた方向に回転
	{
		if (clicking == 1)
		{
			if (fabs(x - prev_x) > fabs(y - prev_y)) //choose either x-axis or y-axis, whichever moved more.
			{
				rotate((float) ((x - prev_x)), 0, 1, 0); //if user drags mouse along x axis, rotate by y-axis(looks more reasonable)
				prev_x = x;
			} else
			{
				rotate((float) ((y - prev_y)), 1, 0, 0); //if user drags mouse along y axis, rotate by x-axis
				prev_y = y;
			}
			glutPostRedisplay();
		}
	} else if (mode == TRANSLATE)//マウスが動いた方向に移動
	{
		if (clicking == 1)
		{
			translate((x-prev_x)/10, (prev_y-y)/10, 0);
			prev_x = x;
			prev_y = y;
			glutPostRedisplay();
		}
	}
}
キーボード
  • 以下のキーに対応して違う機能を呼ぶ
    • R: 回転モードに変更
      • x: x軸に沿って正の回転
      • X: x軸に沿って負の回転
      • y: y軸に沿って正の回転
      • Y: y軸に沿って負の回転
      • z: z軸に沿って正の回転
      • Z: z軸に沿って負の回転
    • T: 移動モードに変更
      • x: x軸に沿って正の移動
      • X: x軸に沿って負の移動
      • y: y軸に沿って正の移動
      • Y: y軸に沿って負の移動
      • z: z軸に沿って正の移動
      • Z: z軸に沿って負の移動
void keyboard(unsigned char key, int x, int y)
{
	//Determine the mode
	if((key == 'r')||(key == 'R'))
	{
		printf("x y z to decrease / X Y Z to increase ROTATION angles.\n");
		mode = ROTATE;
	}
	else if((key == 't')||(key == 'T'))
	{
		printf("x y z to decrease / X Y Z to increase TRANSLATION distance.\n");
		mode = TRANSLATE;
	}

	//Handle ROTATE
	if(mode == ROTATE)
	{
		if(key == 'x')
			rotate(-5, 1, 0, 0);
		else if(key == 'y')
			rotate(-5, 0, 1, 0);
		else if(key == 'z')
			rotate(-5, 0, 0, 1);
		else if(key == 'X')
			rotate(5, 1, 0, 0);
		else if(key == 'Y')
			rotate(5, 0, 1, 0);
		else if(key == 'Z')
			rotate(5, 0, 0, 1);
		glutPostRedisplay();
	}

	// Handle TRANSLATE
	if (mode == TRANSLATE)
	{
		if(key == 'x')
			translate(-0.1, 0, 0);
		else if(key == 'y')
			translate(0, -0.1, 0);
		else if(key == 'z')
			translate(0, 0, -0.1);
		else if(key == 'X')
			translate(0.1, 0, 0);
		else if(key == 'Y')
			translate(0, 0.1, 0);
		else if(key == 'Z')
			translate(0, 0, 0.1);
		glutPostRedisplay();
	}
}

描写関連

  • 基本的に前回と同じだが、配列に点を格納したことにより少しの変更
void print(float x, float y, float z, float px, float py, float pz)
{
	glVertex3f(x + px, y + py, z + pz);
}

void dragon(float x, float y, float z, float px, float py, float pz)
{
	int i=0, j=0, pt=0;	//pt is for tracking pointor of the vertex set
	for(i=0;i<75;i++)	//75 is the number of faces
	{
		glBegin(GL_POLYGON);
		glColor3f(color[3*i],color[(3*i)+1],color[(3*i)+2]);	//set color
		for(j=0;j<nVertex[i];j++)
		{
			print(vertex[pt+(j*3)]*x, vertex[pt+(j*3)+1]*y, vertex[pt+(j*3)+2]*z, px, py, pz);
		}
		pt+=3*nVertex[i];	//each point consists of each value of x, y and z.
		glEnd();
	}
}

void display()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	dragon(x_in, y_in, z_in, px_in, py_in, pz_in);

	glFlush();
}

float input()
{
	float input;
	char str[10], *trm;
	int flag = 1;
	while (flag)
	{
		flag = 0;
		scanf("%s", str);
		input = strtod(str, &trm); //convert str -> double
		if ((trm != NULL) && (*trm != '\0'))
		{
			printf("error: type any numbers\n");
			flag = 1;
		}
		rewind(stdin);
	}
	return input;
}

int main(int argc, char* argv[])
{
	printf("* coordinates for polygon of dragon *\n");
	printf("height of the dragon?\n");
	y_in = input();
	printf("width of the dragon?\n");
	x_in = input();
	printf("depth of the dragon?\n");
	z_in = input();
	printf("x position\n");
	px_in = input();
	printf("y position?\n");
	py_in = input();
	printf("z position?\n");
	pz_in = input();

	printf("Type R to enter ROTATE mode or T to enter TRANSLATE mode.\n");

	// Create OpenGL window
	glutInit(&argc, argv);
	glutInitWindowSize(640, 480);
	glutInitWindowPosition(250, 250);
	glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE | GLUT_DEPTH);

	glutCreateWindow("Rotate dragon");

	init();

	// callback functions
	glutDisplayFunc(display);
	glutKeyboardFunc(keyboard);
	glutMouseFunc(mouse);
	glutMotionFunc(motion);
	glutMainLoop();
	return 0;
}

実行結果

移動

回転


こんなかんじ