// // コンピュータグラフィックス特論Ⅱ // キーフレームアニメーション サンプルプログラム // // 基本的なヘッダファイルのインクルード #ifdef _WIN32 #include #include #endif #include #include // GLUTヘッダファイルのインクルード #include // vecmathヘッダファイルのインクルード #include #include #include #include #include #include #include "vecmath_gl.h" // 複数オブジェクトの位置・向きをマウスで操作するためのクラス #include "ObjectLayout.h" // 幾何形状オブジェクト、及び、読み込み・描画関数 #include "Obj.h" // 標準算術関数・定数の定義 #define _USE_MATH_DEFINES #include // // カメラ・GLUTの入力処理に関するグローバル変数 // // カメラの回転のための変数 static float camera_yaw = 15.0f; // 30.0; // Y軸を中心とする回転角度 static float camera_pitch = -20.0f; // -30.0; // X軸を中心とする回転角度 static float camera_distance = 5.0f; // 15.0; // 中心からカメラの距離 // マウスのドラッグのための変数 static int drag_mouse_r = 0; // 右ボタンがドラッグ中かどうかのフラグ(1:ドラッグ中, 0:非ドラッグ中) static int drag_mouse_l = 0; // 左ボタンがドラッグ中かどうかのフラグ(1:ドラッグ中, 0:非ドラッグ中) static int drag_mouse_m = 0; // 中ボタンがドラッグ中かどうかのフラグ(1:ドラッグ中, 0:非ドラッグ中) static int last_mouse_x, last_mouse_y; // 最後に記録されたマウスカーソルの座標 // ウィンドウのサイズ static int win_width, win_height; // // オブジェクトの配置・表示に関するグローバル変数 // // 複数オブジェクトの位置・向きをマウスで操作するためのモジュール ObjectLayout * layout = NULL; // 表示用の幾何形状オブジェクト Obj * object; Vector3f object_size; // 点光源の位置(影の投影方向) Vector3f light_pos( 0.0f, 10.0f, 0.0f ); // 影の色 Color4f shadow_color( 0.2f, 0.2f, 0.2f, 0.5f ); // // 位置・向きの補間に関するグローバル変数 // // 位置補間方法を表す列挙型 enum PositionInterpolationEnum { PI_LINEAR, PI_HERMIT, PI_BEZIER, PI_BSPLINE, NUM_PI_METHOD }; // 向き補間方法を表す列挙型 enum OrientationInterpolationEnum { OI_NONE, OI_EULAR, OI_QUAT, NUM_OI_METHOD }; // 位置補間方法の名前を表す文字列(表示用) const char * pi_name[] = { "Linear", "Hermit", "Bezier", "B-Spline" }; // 向き補間方法の名前を表す文字列(表示用) const char * oi_name[] = { "None", "Eular", "Quat" }; // 使用する位置・向き補間方法 PositionInterpolationEnum pos_method = PI_LINEAR; OrientationInterpolationEnum ori_method = OI_EULAR; // // キーフレーム情報に関するグローバル変数 // // キーフレーム情報 struct Keyframe { float time; // 時刻 Point3f pos; // 位置 Matrix3f ori; // 向き }; // 設定されている全キーフレーム情報(可変長配列) vector< Keyframe > keyframes; // // アニメーション関連のグローバル変数 // // アニメーション中かどうかを表すフラグ bool on_animation = false; // 全フレーム描画モード・軌道描画モード bool on_draw_frames = false; bool on_draw_trajectory = true; // アニメーションの再生時間 float animation_time = 0.0f; // アニメーション中のオブジェクトの位置・向きを表す変換行列 float model_mat[ 16 ]; // // キーフレームアニメーションのための処理 // // // オブジェクト配置にもとづいて全キーフレーム情報を更新 // void UpdateKeyframes() { Keyframe key; // キーフレーム数を設定 int num_keyframes = layout->GetNumObjects(); keyframes.resize( num_keyframes ); // 各キーフレームの情報を設定 for ( int i=0; iGetPosition( i ); key.ori = layout->GetOrientation( i ); // キーフレームの情報を設定 keyframes[ i ] = key; } } // // 回転行列からオイラー角への変換(yaw → pitch → roll の順の場合)(vecmathの行列を引数とする) // void ConvMatToEular( const Matrix3f & m, float & yaw, float & pitch, float & roll ) { Vector3f y_axis, z_axis; m.getColumn( 1, &y_axis ); m.getColumn( 2, &z_axis ); yaw = atan2( z_axis.x, z_axis.z ); float cos_yaw = cos( yaw ); pitch = atan2( -z_axis.y, sqrt( z_axis.x * z_axis.x + z_axis.z * z_axis.z ) ); float sin_yaw = sin( yaw ); float cos_pitch = cos( pitch ); roll = atan2( cos_pitch * ( sin_yaw * y_axis.z - cos_yaw * y_axis.x ), y_axis.y ); } // // 回転行列からオイラー角への変換(yaw → pitch → roll の順の場合)(配列表現の行列を引数とする) // void ConvMatToEular( const float m[9], float & yaw, float & pitch, float & roll ) { struct Vector3f { float x, y, z; }; Vector3f y_axis, z_axis; z_axis.x = m[2]; z_axis.y = m[5]; z_axis.z = m[8]; y_axis.x = m[1]; y_axis.y = m[4]; y_axis.z = m[7]; yaw = atan2( z_axis.x, z_axis.z ); float cos_yaw = cos( yaw ); pitch = atan2( cos_yaw * z_axis.y, fabs( z_axis.z ) ); float sin_yaw = sin( yaw ); float cos_pitch = cos( pitch ); roll = atan2( cos_pitch * ( sin_yaw * y_axis.z - cos_yaw * y_axis.x ), y_axis.y ); } // // 物体の位置・向きを更新 // (キーフレーム数、キーフレーム配列、時刻を入力として、その時刻における位置・向きを表す変換行列を出力) // void UpdateModelMat( int num_keyframes, const Keyframe * keyframes, float time, float mat[ 16 ] ) { if ( num_keyframes <= 1 ) return; // 指定時刻に対応する区間の番号と区間内での正規化時間(0.0~1.0) int seg_no = -1; float t = 0.0f; // 指定時刻に対応する区間の番号を取得 for ( int i=0; i= keyframes[ i ].time ) && ( time <= keyframes[ i+1 ].time ) ) { seg_no = i; // 区間内での正規化時間を計算 t = ( time - keyframes[ i ].time ) / ( keyframes[ i+1 ].time - keyframes[ i ].time ); break; } } if ( seg_no == -1 ) { // 最初のキーフレームより前の時刻が指定されたら、最初の区間の開始時刻を使用 if ( time < keyframes[ 0 ].time ) { seg_no = 0; t = 0.0f; } // 最後のキーフレームより後の時刻が指定されたら、最後の区間の終了時刻を使用 else { seg_no = num_keyframes - 2; t = 1.0f; } } // 指定時刻におけるオブジェクトの位置・向き Vector3f p; Matrix3f o; // 位置を線形補間により計算 if ( pos_method == PI_LINEAR ) { // 区間の両端点の位置を取得 const Point3f & p0 = keyframes[ seg_no ].pos; const Point3f & p1 = keyframes[ seg_no + 1 ].pos; // 両端点を線形に補間 p.scaleAdd( t, p1 - p0, p0 ); // 両端点を線形に補間(下記の書き方でも可) // p = t * ( p1 - p0 ) + p0; } // エルミート補間 else if ( pos_method == PI_HERMIT ) { // 区間の両端点の位置を取得 const Point3f & p0 = keyframes[ seg_no ].pos; const Point3f & p1 = keyframes[ seg_no + 1 ].pos; // 区間の両端点の傾きを取得 Vector3f v0, v1; const Matrix3f & o0 = keyframes[ seg_no ].ori; const Matrix3f & o1 = keyframes[ seg_no + 1 ].ori; o0.getColumn( 2, &v0 ); o1.getColumn( 2, &v1 ); v0.negate(); v1.negate(); // Hermite関数の値を計算 // 各自実装(式・プログラムは講義資料を参照) // ※レポート課題 } // Bezierによる補間 else if ( pos_method == PI_BEZIER ) { // 指定時刻に対応するBezier補間の区間の番号と区間内での正規化時間(0.0~1.0) int bezier_seg_no = -1; float s = 0.0f; // Bezier補間の区間番号を計算 // 連続する4つのキーフレーム(3区間)をまとめて1つの区間として扱う // 区間数×3+1個のキーフレームが必要 bezier_seg_no = ( seg_no == 0 ) ? 0 : (int) floor( seg_no / 3 ); // 最後の区間でキーフレーム数が4つに足りない場合は、前の区間の最後の時刻を使用 if ( ( bezier_seg_no + 1 ) * 3 + 1 > num_keyframes ) { bezier_seg_no --; s = 1.0f; } // 区間内での正規化時間(0.0~1.0)を計算 else { s = ( time - keyframes[ bezier_seg_no * 3 ].time ) / ( keyframes[ bezier_seg_no * 3 + 3 ].time - keyframes[ bezier_seg_no * 3 ].time ); } // 一つも区間が存在しない場合(キーフレーム数が3個以下の場合)は、最初のキーフレームの位置を出力 if ( num_keyframes < 4 ) { p = keyframes[ 0 ].pos; } // Bezier補間を計算 else { // Bezier補間の区間の4つの制御点(両端点と、中間の2つの点)の位置を取得 const Point3f & p0 = keyframes[ bezier_seg_no * 3 ].pos; const Point3f & p1 = keyframes[ bezier_seg_no * 3 + 1 ].pos; const Point3f & p2 = keyframes[ bezier_seg_no * 3 + 2 ].pos; const Point3f & p3 = keyframes[ bezier_seg_no * 3 + 3 ].pos; // Bezier関数の値を計算 // 各自実装(式は講義資料を参照) // ※ 媒介変数は、t ではなく s を使うことに注意 // ※レポート課題 } } // B-Splineによる補間 else if ( pos_method == PI_BSPLINE ) { // 区間の両端点と、さらにその隣の点(もしあれば)の位置を取得 int k0, k1, k2, k3; k0 = ( seg_no > 0 ) ? ( seg_no - 1 ) : seg_no; k1 = seg_no; k2 = seg_no + 1; k3 = ( seg_no + 2 == num_keyframes ) ? ( seg_no + 1 ) : ( seg_no + 2); const Point3f & p0 = keyframes[ k0 ].pos; const Point3f & p1 = keyframes[ k1 ].pos; const Point3f & p2 = keyframes[ k2 ].pos; const Point3f & p3 = keyframes[ k3 ].pos; // B-Spline 関数の値を計算 // 各自実装(式は講義資料を参照) // ※レポート課題 } // 向きの補間なし if ( ori_method == OI_NONE ) { o = layout->GetOrientation( seg_no ); } // 向きをオイラー角で補間 else if ( ori_method == OI_EULAR ) { // 区間の両端点の向きを取得 const Matrix3f & o0 = keyframes[ seg_no ].ori; const Matrix3f & o1 = keyframes[ seg_no + 1 ].ori; // オイラー角に変換 float y0, p0, r0; float y1, p1, r1; ConvMatToEular( o0, y0, p0, r0 ); ConvMatToEular( o1, y1, p1, r1 ); // 各回転角度を線形補間 float y, p, r; if ( y0 < y1 - M_PI ) y0 += 2.0f * M_PI; else if ( y0 > y1 + M_PI ) y0 -= 2.0f * M_PI; y = ( y1 - y0 ) * t + y0; p = ( p1 - p0 ) * t + p0; r = ( r1 - r0 ) * t + r0; // 行列に変換 Matrix3f rot; o.rotY( y ); rot.rotX( p ); o.mul( o, rot ); rot.rotZ( r ); o.mul( o, rot ); } // 向きを四元数と球面線形補間により計算 else if ( ori_method == OI_QUAT ) { // 区間の両端点の向きを取得 const Matrix3f & o0 = keyframes[ seg_no ].ori; const Matrix3f & o1 = keyframes[ seg_no + 1 ].ori; // 四元数を使って球面線形補間を計算 // 各自実装(式は講義資料を参照) // vecmath の Quat4f.interpolate() メソッドを使用すれば、容易に計算できる // ※レポート課題 } // オブジェクトの位置・向きを表す変換行列を配列にコピー Matrix4f f; f.set( o, p, 1.0f ); f.transpose(); memcpy( mat, &f.m00, sizeof( float ) * 16 ); } // // 以下、プログラムのメイン処理 // // // あらかじめ定義されたオブジェクト配置を設定 // void SetupScene( int no ) { if ( !layout ) return; Matrix3f ori, rot; if ( no == 1 ) { layout->DeleteAllObjects(); layout->AddObject(); layout->SetObjectSize( 0, object_size ); layout->SetObjectPos( 0, Point3f( -1.0f, 0.5f, -1.5f ) ); ori.rotY( M_PI * 0.6f ); layout->SetObjectOri( 0, ori ); layout->AddObject(); layout->SetObjectSize( 1, object_size ); layout->SetObjectPos( 1, Point3f( 0.0f, 1.0f, 0.0f ) ); ori.rotY( M_PI * 1.2f ); rot.rotX( M_PI / 3.0f ); ori.mul( ori, rot ); rot.rotZ( M_PI / 4.0f ); ori.mul( ori, rot ); layout->SetObjectOri( 1, ori ); layout->AddObject(); layout->SetObjectSize( 2, object_size ); layout->SetObjectPos( 2, Point3f( -2.0f, 0.5f, 1.0f ) ); ori.rotY( M_PI ); rot.rotX( - M_PI / 6.0f ); ori.mul( ori, rot ); layout->SetObjectOri( 2, ori ); layout->AddObject(); layout->SetObjectSize( 3, object_size ); layout->SetObjectPos( 3, Point3f( 0.0f, 0.4f, 1.5f ) ); ori.rotY( M_PI * 1.2f ); layout->SetObjectOri( 3, ori ); layout->AddObject(); layout->SetObjectSize( 4, object_size ); layout->SetObjectPos( 4, Point3f( 1.0f, 0.35f, 1.75f ) ); ori.rotY( M_PI * -0.4f ); rot.rotX( M_PI / 12.0f ); ori.mul( ori, rot ); layout->SetObjectOri( 4, ori ); layout->AddObject(); layout->SetObjectSize( 5, object_size ); layout->SetObjectPos( 5, Point3f( 1.5f, 0.6f, 0.0f ) ); ori.rotY( 0.0f ); layout->SetObjectOri( 5, ori ); layout->AddObject(); layout->SetObjectSize( 6, object_size ); layout->SetObjectPos( 6, Point3f( 1.0f, 1.0f, -1.0f ) ); ori.rotY( M_PI / 3 ); layout->SetObjectOri( 6, ori ); camera_yaw = 15.0f; camera_pitch = -20.0f; camera_distance = 6.0f; } else if ( no == 2 ) { layout->DeleteAllObjects(); layout->AddObject(); layout->SetObjectSize( 0, object_size ); layout->SetObjectPos( 0, Point3f(-1.0f, 0.5f, 0.0f ) ); ori.rotY( M_PI * 0.6f ); layout->SetObjectOri( 0, ori ); layout->AddObject(); layout->SetObjectSize( 1, object_size ); layout->SetObjectPos( 1, Point3f( 1.0f, 0.5f, 0.0f ) ); ori.rotY( M_PI * 1.2f ); rot.rotX( M_PI / 3.0f ); ori.mul( ori, rot ); rot.rotZ( M_PI / 4.0f ); ori.mul( ori, rot ); layout->SetObjectOri( 1, ori ); camera_yaw = 0.0f; camera_pitch = -20.0f; camera_distance = 5.0f; } // オブジェクト配置にもとづいて全キーフレーム情報を更新 UpdateKeyframes(); } // // 格子模様の床を描画 // void DrawFloor( float tile_size, int num_x, int num_z, float r0, float g0, float b0, float r1, float g1, float b1 ) { int x, z; float ox, oz; glBegin( GL_QUADS ); glNormal3d( 0.0, 1.0, 0.0 ); ox = - ( num_x * tile_size ) / 2; for ( x=0; xGetNumObjects()-1.0f+0.001f; t+=0.1f ) { UpdateModelMat( keyframes.size(), &keyframes.front(), t, mat ); glVertex3f( mat[12], mat[13], mat[14] ); } glEnd(); glEnable( GL_LIGHTING ); // キーフレームのオブジェクトを描画 if ( on_animation ) { for ( int i=0; iGetNumObjects(); i++ ) { // オブジェクトを描画 glPushMatrix(); glMultMatrixf( layout->GetFrame( i ) ); RenderObj( object ); glPopMatrix(); // オブジェクトの影を描画 RenderShadow( object, layout->GetFrame( i ) ); } } } // 全フレームのオブジェクトを描画 else if ( on_draw_frames ) { float mat[ 16 ]; for ( float t=0.0f; t<=layout->GetNumObjects()-1.0f+0.001f; t+=0.2f ) { UpdateModelMat( keyframes.size(), &keyframes.front(), t, mat ); // オブジェクトを描画 glPushMatrix(); glMultMatrixf( mat ); RenderObj( object ); glPopMatrix(); // オブジェクトの影を描画 RenderShadow( object, mat ); } } // アニメーション中のオブジェクトを描画 if ( on_animation && !on_draw_frames ) { // アニメーション中のオブジェクトを描画 glPushMatrix(); glMultMatrixf( model_mat ); RenderObj( object ); glPopMatrix(); // オブジェクトの影を描画 RenderShadow( object, model_mat ); } // 編集モード中の描画 else { // 各オブジェクトを描画 for ( int i=0; iGetNumObjects(); i++ ) { glPushMatrix(); glMultMatrixf( layout->GetFrame( i ) ); RenderObj( object ); glPopMatrix(); // オブジェクトの影を描画 RenderShadow( object, layout->GetFrame( i ) ); } // 操作用の情報を描画 layout->Render(); } // 現在の描画モードを表示 if ( on_animation ) DrawTextInformation( 0, "Animation Mode" ); else DrawTextInformation( 0, "Layout Mode" ); // 現在の補間モードを表示 if ( on_draw_frames || on_draw_trajectory || on_animation ) { char message[ 64 ] = ""; sprintf( message, "Position: %s, Orientation: %s", pi_name[ pos_method ], oi_name[ ori_method ] ); DrawTextInformation( 1, message ); } // 現在の操作モードを表示 else { DrawTextInformation( 1, layout->GetOperationMode() ); } // 現在の時刻を表示 if ( on_animation && !on_draw_frames ) { char message[ 64 ] = ""; sprintf( message, "Time: %2.2f", animation_time ); DrawTextInformation( 2, message ); } // バックバッファに描画した画面をフロントバッファに表示 glutSwapBuffers(); } // // ウィンドウサイズ変更時に呼ばれるコールバック関数 // void ReshapeCallback( int w, int h ) { // ウィンドウ内の描画を行う範囲を設定(ここではウィンドウ全体に描画) glViewport(0, 0, w, h); // カメラ座標系→スクリーン座標系への変換行列を設定 glMatrixMode( GL_PROJECTION ); glLoadIdentity(); gluPerspective( 45, (double)w/h, 1, 500 ); // ウィンドウのサイズを記録(テキスト描画処理のため) win_width = w; win_height = h; } // // マウスクリック時に呼ばれるコールバック関数 // void MouseClickCallback( int button, int state, int mx, int my ) { // 左ボタンが押されたらドラッグ開始 if ( ( button == GLUT_LEFT_BUTTON ) && ( state == GLUT_DOWN ) ) drag_mouse_l = 1; // 左ボタンが離されたらドラッグ終了 else if ( ( button == GLUT_LEFT_BUTTON ) && ( state == GLUT_UP ) ) drag_mouse_l = 0; // 右ボタンが押されたらドラッグ開始 if ( ( button == GLUT_RIGHT_BUTTON ) && ( state == GLUT_DOWN ) ) drag_mouse_r = 1; // 右ボタンが離されたらドラッグ終了 else if ( ( button == GLUT_RIGHT_BUTTON ) && ( state == GLUT_UP ) ) drag_mouse_r = 0; // 中ボタンが押されたらドラッグ開始 if ( ( button == GLUT_MIDDLE_BUTTON ) && ( state == GLUT_DOWN ) ) drag_mouse_m = 1; // 中ボタンが離されたらドラッグ終了 else if ( ( button == GLUT_MIDDLE_BUTTON ) && ( state == GLUT_UP ) ) drag_mouse_m = 0; // シーン配置機能に左クリックを通知 if ( ( button == GLUT_LEFT_BUTTON ) && ( state == GLUT_DOWN ) ) layout->OnMouseDown( mx, my ); else if ( ( button == GLUT_LEFT_BUTTON ) && ( state == GLUT_UP ) ) layout->OnMouseUp( mx, my ); // 再描画 glutPostRedisplay(); // 現在のマウス座標を記録 last_mouse_x = mx; last_mouse_y = my; } // // マウス移動時に呼ばれるコールバック関数 // void MouseMotionCallback( int mx, int my ) { // シーン配置機能にマウス移動を通知 if ( layout ) layout->OnMoveMouse( mx, my ); // 再描画 glutPostRedisplay(); } // // マウスドラッグ時に呼ばれるコールバック関数 // void MouseDragCallback( int mx, int my ) { // 右ボタンのドラッグ中は視点を回転する if ( drag_mouse_r ) { // 前回のマウス座標と今回のマウス座標の差に応じて視点を回転 // マウスの横移動に応じてY軸を中心に回転 camera_yaw -= ( mx - last_mouse_x ) * 1.0; if ( camera_yaw < 0.0 ) camera_yaw += 360.0; else if ( camera_yaw > 360.0 ) camera_yaw -= 360.0; // マウスの縦移動に応じてX軸を中心に回転 camera_pitch -= ( my - last_mouse_y ) * 1.0; if ( camera_pitch < -90.0 ) camera_pitch = -90.0; else if ( camera_pitch > 90.0 ) camera_pitch = 90.0; } // 中ボタンのドラッグ中は視点とカメラの距離を変更する if ( drag_mouse_m ) { // 前回のマウス座標と今回のマウス座標の差に応じて視点を回転 // マウスの縦移動に応じて距離を移動 camera_distance += ( my - last_mouse_y ) * 0.2; if ( camera_distance < 2.0 ) camera_distance = 2.0; } // シーン配置機能にマウス移動を通知 if ( layout ) { layout->Update(); layout->OnMoveMouse( mx, my ); // オブジェクト配置にもとづいて全キーフレーム情報を更新 UpdateKeyframes(); } // 今回のマウス座標を記録 last_mouse_x = mx; last_mouse_y = my; // 再描画 glutPostRedisplay(); } // // キーボードのキーが押されたときに呼ばれるコールバック関数 // void KeyboardCallback( unsigned char key, int mx, int my ) { // s キーでアニメーションの停止・再開 if ( key == 's' ) on_animation = !on_animation; // 数字キーであらかじめ定義されたオブジェクト配置を設定 if ( ( key >= '1' ) && ( key <= '9' ) ) { SetupScene( key - '0' ); } // スペースキーでアニメーションを開始 if ( key == ' ' ) { on_animation = ! on_animation; if ( on_animation ) animation_time = 0.0f; on_draw_frames = false; } // pキーで位置補間方法を変更 if ( key == 'p' ) { pos_method = (PositionInterpolationEnum)( ( pos_method + 1 ) % NUM_PI_METHOD ); } // oキーで向き補間方法を変更 if ( key == 'o' ) { ori_method = (OrientationInterpolationEnum)( ( ori_method + 1 ) % NUM_OI_METHOD ); if ( ori_method == OI_NONE ) ori_method = (OrientationInterpolationEnum)( ori_method + 1 ); } // fキーで通常・描画全フレーム描画・軌道描画を切り替え if ( key == 'f' ) { if ( !on_draw_frames && !on_draw_trajectory ) on_draw_trajectory = true; else if ( !on_draw_frames && on_draw_trajectory ) { on_draw_frames = true; on_draw_trajectory = false; } else on_draw_frames = false; } // aキーでオブジェクトを追加 if ( key == 'a' ) { layout->AddObject(); // オブジェクト配置にもとづいて全キーフレーム情報を更新 UpdateKeyframes(); } // dキーでオブジェクトを削除 if ( key == 'd' ) { layout->DeleteObject(); // オブジェクト配置にもとづいて全キーフレーム情報を更新 UpdateKeyframes(); } // tキーで軸の描画モードを変更 if ( key == 't' ) { bool & flag = layout->GetRenderOption().enable_xray_mode; flag = ! flag; } glutPostRedisplay(); } // // アイドル時に呼ばれるコールバック関数 // void IdleCallback( void ) { // アニメーション処理 if ( on_animation ) { #ifdef WIN32 // システム時間を取得し、前回からの経過時間に応じてΔtを決定 static DWORD last_time = 0; DWORD curr_time = timeGetTime(); float delta = ( curr_time - last_time ) * 0.001f; if ( delta > 0.1f ) delta = 0.1f; last_time = curr_time; animation_time += delta; #else // 固定のΔtを使用 animation_time += 0.02f; #endif // アニメーションの繰り返し(最後まで再生が終わったら時間を0に戻す) if ( animation_time >= layout->GetNumObjects() - 1 ) animation_time = 0.0f; // アニメーション中のオブジェクトの位置・向きを更新 UpdateModelMat( keyframes.size(), &keyframes.front(), animation_time, model_mat ); // 再描画の指示を出す(この後で再描画のコールバック関数が呼ばれる) glutPostRedisplay(); } } // // 環境初期化関数 // void initEnvironment( void ) { // 光源を作成する float light0_position[] = { 0.0, 10.0, 0.0, 1.0 }; float light0_diffuse[] = { 0.8, 0.8, 0.8, 1.0 }; float light0_specular[] = { 1.0, 1.0, 1.0, 1.0 }; float light0_ambient[] = { 0.1, 0.1, 0.1, 1.0 }; glLightfv( GL_LIGHT0, GL_POSITION, light0_position ); glLightfv( GL_LIGHT0, GL_DIFFUSE, light0_diffuse ); glLightfv( GL_LIGHT0, GL_SPECULAR, light0_specular ); glLightfv( GL_LIGHT0, GL_AMBIENT, light0_ambient ); glEnable( GL_LIGHT0 ); // 光源計算を有効にする glEnable( GL_LIGHTING ); // 物体の色情報を有効にする glEnable( GL_COLOR_MATERIAL ); // Zテストを有効にする glEnable( GL_DEPTH_TEST ); // 背面除去を有効にする glCullFace( GL_BACK ); glEnable( GL_CULL_FACE ); // 背景色を設定 glClearColor( 0.5, 0.5, 0.8, 0.0 ); // オブジェクトの読み込み object = LoadObj( "car.obj" ); if ( !object || ( object->num_triangles == 0 ) ) { // 読み込みに失敗したら終了 printf( "Failed to load the object file." ); exit( -1 ); } ScaleObj( object, 1.0f, &object_size.x, &object_size.y, &object_size.z ); // オブジェクト配置機能の初期化 layout = new ObjectLayout(); // あらかじめ定義されたオブジェクト配置を設定 SetupScene( 1 ); } // // メイン関数(プログラムはここから開始) // int main( int argc, char ** argv ) { // GLUTの初期化 glutInit( &argc, argv ); glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGBA | GLUT_STENCIL ); glutInitWindowSize( 640, 640 ); glutInitWindowPosition( 0, 0 ); glutCreateWindow("Keyframe Animation"); // コールバック関数の登録 glutDisplayFunc( DisplayCallback ); glutReshapeFunc( ReshapeCallback ); glutMouseFunc( MouseClickCallback ); glutMotionFunc( MouseDragCallback ); glutPassiveMotionFunc( MouseMotionCallback ); glutKeyboardFunc( KeyboardCallback ); glutIdleFunc( IdleCallback ); // 環境初期化 initEnvironment(); // GLUTのメインループに処理を移す glutMainLoop(); return 0; }