VisualC++ と OpenGL を利用した仮想物理実験室
【0-2-2】仮想物理実験室の構築 (ver1.1)
「【0日目】仮想物理実験室の構築 (ver1.0)」をベースに、仮想物理実験室の構築 (ver1.1)を構築します。 ver1.0 からの追加として、2点間(x_0,y_0,z_0)と(x_1,y_1,z_1)をばねでつなぐ関数を定義します。
仮想物理実験室(ver 1.1)
「【0日目】仮想物理実験室の構築 (ver1.0)」と同様に、「gl_screenshot.h」と「gl_material.h」を予め準備してください。
//////////////////////////////////////////////////////////////////////////
// 仮想物理実験室(ver 1.1)
// 1.0+ばね関数
//////////////////////////////////////////////////////////////////////////
#include <math.h>
#include <fstream>
#include <sstream>
#include <iostream>
#include <direct.h>
#include <time.h>
#include <GL/glut.h>
#include <GL/gl_material.h>
#include <GL/gl_screenshot.h>
using namespace std;
double PI = acos(-1.0);
//////////////////////////////////////////////////////////////////////////
// 変数の定義
//////////////////////////////////////////////////////////////////////////
#define _BITMAP 1//アニメーション作成用ビットマップの保存 0:しない 1:する
//--------------------------------------------------------
// 仮想物理実験室変数の定義
//--------------------------------------------------------
double t = 0.0; //時刻
double dt= 0.03; //時間刻み
int tn = 0; //ステップ数
//箱の位置と一辺の長さ
double box_x = 30.0, box_y = 0.0, box_z = 20.0, box_l = 10.0;
//ボールの位置と半径
double ball1_x =-20.0, ball1_y = 15.0, ball1_z = 30.0, ball1_r =8.0;
double ball2_x =-10.0, ball2_y = -10.0, ball2_z = 10.0, ball2_r =4.0;
//--------------------------------------------------------
// OpenGL用変数定義
//--------------------------------------------------------
//////////////////////////////////////////
// ウィンドウ生成用
int WindowPositionX = 200; //生成するウィンドウ位置のX座標
int WindowPositionY = 200; //生成するウィンドウ位置のY座標
int WindowWidth = 512; //生成するウィンドウの幅
int WindowHeight = 512; //生成するウィンドウの高さ
char WindowTitle[] = "仮想物理実験室(ver 1.1)"; //ウィンドウのタイトル
//////////////////////////////////////////
// アニメーション用 ビットマップ保存
gl_screenshot gs; //「gl_screenshot」クラスのインスタンス「gs」を宣言
//////////////////////////////////////////
// 床描画用
static GLfloat floor_planar[4];
static GLfloat floor_s = 50.0f; //床の広さ(1辺=2.0*floor_s)
static GLfloat pM[16];
static GLfloat LightPosition[4] = { -10, -20, 70, 1 }; //光源の位置
typedef struct _QUADS_VERTEX{
GLfloat v0[3];
GLfloat v1[3];
GLfloat v2[3];
GLfloat v3[3];
}QUADS_VERTEX;
static QUADS_VERTEX floor_v = {
{ floor_s, floor_s, 0.0f },
{ -floor_s, floor_s, 0.0f },
{ -floor_s, -floor_s, 0.0f },
{ floor_s, -floor_s, 0.0f },
};
void findPlane(GLfloat plane[4], GLfloat v0[3], GLfloat v1[3], GLfloat v2[3]);
void DrawFloor(bool bTexture);
//////////////////////////////////////////
// マウスドラッグ用
int cx, cy; // ドラッグ開始位置
double sx, sy; // マウスの絶対位置→ウィンドウ内での相対位置の換算係数
double cq[4] = { 1.0, 0.0, 0.0, 0.0 }; // 回転の初期値 (クォータニオン)
double tq[4]; // ドラッグ中の回転 (クォータニオン)
double rt[16]; // 回転の変換行列
unsigned int listNumber;
float camera_z_pos =200.0;
//////////////////////////////////////////
// 文字描画用
int text_list;
char t_char[20];
char t_char2[20];
void DRAW_STRING(int x, int y, char *string, void *font = GLUT_BITMAP_TIMES_ROMAN_24);
void DISPLAY_TEXT(int x, int y, char *string);
//--------------------------------------------------------
// 回転用
//--------------------------------------------------------
void qmul(double r[], const double p[], const double q[]);
void qrot(double r[], double q[]);
//--------------------------------------------------------
// 関数のプロトタイプ
//--------------------------------------------------------
//////////////////////////////////////////
// メイン関数用
void Initialize(void);
void Display(void);
void Resize(int w, int h);
void Idle(void);
void Keyboard(unsigned char key, int x, int y);
void mouse_motion(int x, int y);
void mouse_on(int button, int state, int x, int y);
void mouse_wheel(float);
//////////////////////////////////////////
// 描画用
void Calculate(void); //計算
void DrawStructure(void); //描画
void Ground(void); //大地の描画
//////////////////////////////////////////
// 影描画用
void shadowMatrix(GLfloat *m, GLfloat plane[4], GLfloat light[4]);
void DrawShadow(void);
//////////////////////////////////////////
// ばね描画用
void drowSolidSpring(double x0, double y0, double z0, double x1, double y1, double z1);//
void skSolidSpring(int Nm, int Ns, int Np, double radius, double ratio, double length);
//--------------------------------------------------------
// メイン関数
//--------------------------------------------------------
int main(int argc, char *argv[]){
srand((unsigned)time(NULL));
#if _BITMAP
_mkdir("bitmap"); //bmpファイル保存用のフォルダの作成
#endif
glutInit(&argc, argv); //環境の初期化
glutInitWindowPosition(WindowPositionX, WindowPositionY); //ウィンドウの位置の指定
glutInitWindowSize(WindowWidth, WindowHeight); //ウィンドウサイズの指定
glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE); //ディスプレイモードの指定
glutCreateWindow(WindowTitle); //ウィンドウの作成
glutDisplayFunc(Display); //描画時に呼び出される関数を指定する(関数名:Display)
glutReshapeFunc(Resize); //リサイズにより呼び出される関数
glutMouseFunc(mouse_on); //マウスクリック時に呼び出される関数
glutMotionFunc(mouse_motion); //マウスドラッグ解除時に呼び出される関数
glutKeyboardFunc(Keyboard); //キーボード入力時に呼び出される関数を指定する(関数名:Keyboard)
glutIdleFunc(Idle); //プログラムアイドル状態時に呼び出される関数
Initialize(); //初期設定の関数を呼び出す
glutMainLoop();
return 0;
}
//--------------------------------------------------------
// 初期設定の関数
//--------------------------------------------------------
void Initialize(void){
glClearColor(1.0, 1.0, 1.0, 1.0); //背景色
glEnable( GL_DEPTH_TEST ); //デプスバッファを使用:glutInitDisplayMode() で GLUT_DEPTH を指定する
glDepthFunc( GL_LEQUAL );
glClearDepth( 1.0 );
//////////////////////////////////////////
// ディスプレイリストを作成
listNumber = glGenLists(1);
glNewList( listNumber, GL_COMPILE );
glEndList();
//////////////////////////////////////////
// マウスポインタ位置のウィンドウ内の相対的位置への換算用
sx = 1.0 / (double)WindowWidth;
sy = 1.0 / (double)WindowHeight;
// 回転行列の初期化
qrot(rt, cq);
//////////////////////////////////////////
// 床
findPlane( floor_planar,
floor_v.v0,
floor_v.v1,
floor_v.v2 );
//////////////////////////////////////////
//透視変換行列の設定
glMatrixMode(GL_PROJECTION);//行列モードの設定(GL_PROJECTION : 透視変換行列の設定、GL_MODELVIEW:モデルビュー変換行列)
glLoadIdentity();//行列の初期化
gluPerspective(30.0, (double)WindowWidth/(double)WindowHeight, 0.1, 1000.0); //透視投影法の視体積gluPerspactive(th, w/h, near, far);
}
//--------------------------------------------------------
// 描画の関数
//--------------------------------------------------------
void Display(void) {
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//////////////////////////////////////////
//モデルビュー変換行列の設定
glMatrixMode(GL_MODELVIEW);//行列モードの設定(GL_PROJECTION : 透視変換行列の設定、GL_MODELVIEW:モデルビュー変換行列)
glLoadIdentity();//行列の初期化
glViewport(0, 0, WindowWidth, WindowHeight);
//////////////////////////////////////////
//視点の設定
gluLookAt(
0.0, 0.0, camera_z_pos, // 視点の位置x,y,z;
0.0, 0.0, 0.0, // 視界の中心位置の参照点座標x,y,z
0.0, 1.0, 0.0 ) ; //視界の上方向のベクトルx,y,z
//////////////////////////////////////////
//ステンシルバッファクリア値の設定
glClearStencil( 0 );
glCullFace( GL_BACK );
glEnable( GL_CULL_FACE );
glEnable( GL_AUTO_NORMAL );
glEnable( GL_NORMALIZE );
//////////////////////////////////////////
// 平面射影行列の算出
shadowMatrix(pM,floor_planar,LightPosition);
//////////////////////////////////////////
// 回転
glMultMatrixd(rt);
//////////////////////////////////////////
// 光源ON
glEnable( GL_LIGHTING );
glEnable( GL_LIGHT0 );
glLightfv( GL_LIGHT0,GL_POSITION,LightPosition );
//////////////////////////////////////////
// 描画
glPushMatrix();
Calculate(); //計算
DrawStructure(); //物体
DrawShadow(); //影
glPopMatrix();
//////////////////////////////////////////
//陰影OFF
glDisable(GL_AUTO_NORMAL);
glDisable(GL_NORMALIZE);
glDisable(GL_LIGHTING);
Ground(); // 地面の描画
//////////////////////////////////////////
//文字の描画
strcpy_s(t_char2, "t = ");
sprintf_s(t_char, "%5.2f", t);
strcat_s(t_char2, t_char);
DISPLAY_TEXT(5, 95, t_char2 );
//////////////////////////////////////////
//ビットマップの保存
#if _BITMAP
ostringstream fname;
int tt = tn +10000;
fname << "bitmap/" << tt << ".bmp" ;//出力ファイル名
string name = fname.str();
gs.screenshot(name.c_str(), 24);
#endif
glutSwapBuffers(); //glutInitDisplayMode(GLUT_DOUBLE)でダブルバッファリングを利用可
}
//--------------------------------------------------------
// 計算と物体の描画
//--------------------------------------------------------
void Calculate(){
t = dt * double(tn);
tn++;
}
void DrawStructure(){
glPushMatrix();
glMaterialfv(GL_FRONT, GL_AMBIENT, ms_ruby.ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, ms_ruby.diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, ms_ruby.specular);
glMaterialfv(GL_FRONT, GL_SHININESS, &ms_ruby.shininess);
glTranslated(0.0, 0.0, 20.0);//平行移動値の設定
glutSolidCube(10.0);//引数:(一辺の長さ)
glPopMatrix();
//ボール
glPushMatrix();
glMaterialfv(GL_FRONT, GL_AMBIENT, ms_silver.ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, ms_silver.diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, ms_silver.specular);
glMaterialfv(GL_FRONT, GL_SHININESS, &ms_silver.shininess);
glTranslated(ball1_x, ball1_y, ball1_z); //平行移動値の設定
glutSolidSphere(ball1_r, 20, 20); //引数:(半径, Z軸まわりの分割数, Z軸に沿った分割数)
glPopMatrix();
glPushMatrix();
glTranslated(ball2_x, ball2_y, ball2_z); //平行移動値の設定
glutSolidSphere(ball2_r, 20, 20); //引数:(半径, Z軸まわりの分割数, Z軸に沿った分割数)
glPopMatrix();
//バネ
glPushMatrix();
glMaterialfv(GL_FRONT, GL_AMBIENT, ms_yellow_rubber.ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, ms_yellow_rubber.diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, ms_yellow_rubber.specular);
glMaterialfv(GL_FRONT, GL_SHININESS, &ms_yellow_rubber.shininess);
drowSolidSpring(ball1_x, ball1_y, ball1_z, ball2_x, ball2_y, ball2_z); //引数:(始点x, 始点y, 始点z, 終点x, 終点y, 終点z )
glPopMatrix();
}
//--------------------------------------------------------
// 大地の描画
//--------------------------------------------------------
void Ground(void) {
double ground_max_x = 300.0;
double ground_max_y = 300.0;
glColor3d(0.8, 0.8, 0.8); // 大地の色
glBegin(GL_LINES);
for(double ly = -ground_max_y ;ly <= ground_max_y; ly+=10.0){
glVertex3d(-ground_max_x, ly, -1.1);
glVertex3d(ground_max_x, ly , -1.1);
}
for(double lx = -ground_max_x ;lx <= ground_max_x; lx+=10.0){
glVertex3d(lx, ground_max_y , -1.1);
glVertex3d(lx, -ground_max_y, -1.1);
}
glEnd();
}
//--------------------------------------------------------
// アイドル時に呼び出される関数
//--------------------------------------------------------
void Idle(){
glutPostRedisplay(); //glutDisplayFunc()を1回実行する
}
//---------------------------------------------------------------------------------------------------
// サイズ変更
//---------------------------------------------------------------------------------------------------
void Resize(int x, int y)
{
WindowWidth = x;
WindowHeight = y;
if ( WindowWidth < 1 ) WindowWidth = 1;
if ( WindowHeight < 1 ) WindowHeight = 1;
}
//--------------------------------------------------------
// キーボード入力時に呼び出される関数
//--------------------------------------------------------
void Keyboard(unsigned char key, int x, int y){
switch ( key )
{
case 'i':
camera_z_pos -= 10.0;
break;
case 'o':
camera_z_pos += 10.0;
break;
default:
break;
}
cout << camera_z_pos << endl;
}
//--------------------------------------------------------
// マウスドラッグ時
//--------------------------------------------------------
void mouse_motion(int x, int y){
double dx, dy, a;
// マウスポインタの位置のドラッグ開始位置からの変位
dx = (x - cx) * sx;
dy = (y - cy) * sy;
// マウスポインタの位置のドラッグ開始位置からの距離
a = sqrt(dx * dx + dy * dy);
if( a != 0.0 )
{
// マウスのドラッグに伴う回転のクォータニオン dq を求める
double ar = a * 2.0 * PI * 0.5;
double as = sin(ar) / a;
double dq[4] = { cos(ar), dy * as, dx * as, 0.0 };
// 回転の初期値 cq に dq を掛けて回転を合成
qmul(tq, dq, cq);
// クォータニオンから回転の変換行列を求める
qrot(rt, tq);
}
}
//--------------------------------------------------------
// マウスクリック時
//--------------------------------------------------------
void mouse_on(int button, int state, int x, int y){
switch (button) {
case 0:
switch (state) {
case 0:
// ドラッグ開始点を記録
cx = x;
cy = y;
break;
case 1:
// 回転の保存
cq[0] = tq[0];
cq[1] = tq[1];
cq[2] = tq[2];
cq[3] = tq[3];
break;
default:
break;
}
break;
default:
break;
}
cout << x << " " << y<<endl;
}
//--------------------------------------------------------
// 床平面の方程式と行列の計算
//--------------------------------------------------------
void findPlane(
GLfloat plane[4], // 作成する平面方程式の係数
GLfloat v0[3], // 頂点1
GLfloat v1[3], // 頂点2
GLfloat v2[3]) // 頂点3
{
GLfloat vec0[3], vec1[3];
// Need 2 vectors to find cross product.
vec0[0] = v1[0] - v0[0];
vec0[1] = v1[1] - v0[1];
vec0[2] = v1[2] - v0[2];
vec1[0] = v2[0] - v0[0];
vec1[1] = v2[1] - v0[1];
vec1[2] = v2[2] - v0[2];
// find cross product to get A, B, and C of plane equation
plane[0] = vec0[1] * vec1[2] - vec0[2] * vec1[1];
plane[1] = -(vec0[0] * vec1[2] - vec0[2] * vec1[0]);
plane[2] = vec0[0] * vec1[1] - vec0[1] * vec1[0];
plane[3] = -(plane[0] * v0[0] + plane[1] * v0[1] + plane[2] * v0[2]);
}
void shadowMatrix(
GLfloat *m, // 作成する行列のポインタ
GLfloat plane[4], // 射影する表面の平面方程式の係数
GLfloat light[4]) // 光源の同時座標値
{
GLfloat dot;
// Find dot product between light position vector and ground plane normal.
dot = plane[0] * light[0] +
plane[1] * light[1] +
plane[2] * light[2] +
plane[3] * light[3];
m[0] = dot - light[0] * plane[0];
m[4] = 0.f - light[0] * plane[1];
m[8] = 0.f - light[0] * plane[2];
m[12] = 0.f - light[0] * plane[3];
m[1] = 0.f - light[1] * plane[0];
m[5] = dot - light[1] * plane[1];
m[9] = 0.f - light[1] * plane[2];
m[13] = 0.f - light[1] * plane[3];
m[2] = 0.f - light[2] * plane[0];
m[6] = 0.f - light[2] * plane[1];
m[10] = dot - light[2] * plane[2];
m[14] = 0.f - light[2] * plane[3];
m[3] = 0.f - light[3] * plane[0];
m[7] = 0.f - light[3] * plane[1];
m[11] = 0.f - light[3] * plane[2];
m[15] = dot - light[3] * plane[3];
}
//--------------------------------------------------------
// 床の描画と影の描画
//--------------------------------------------------------
void DrawFloor(bool bTexture){
if( bTexture ){
// 床にテクスチャを使う時はココで設定する
// glBindTexture( GL_TEXTURE_2D, );
glDisable(GL_LIGHTING);
glBegin(GL_QUADS);
// glTexCoord2f( , );
glVertex3fv( floor_v.v0 );
// glTexCoord2f( , );
glVertex3fv( floor_v.v1 );
// glTexCoord2f( , );
glVertex3fv( floor_v.v2 );
// glTexCoord2f( , );
glVertex3fv( floor_v.v3 );
glEnd();
glEnable(GL_LIGHTING);
}else{
glDisable(GL_LIGHTING);
glBegin(GL_QUADS);
glVertex3fv( floor_v.v0 );
glVertex3fv( floor_v.v1 );
glVertex3fv( floor_v.v2 );
glVertex3fv( floor_v.v3 );
glEnd();
glEnable(GL_LIGHTING);
}
}
void DrawShadow(void){
/////////////////////////////////////////////
//床のステンシルを付ける
glEnable(GL_STENCIL_TEST);
glStencilFunc( GL_ALWAYS, 1, ~0);
//これから描画するもののステンシル値にすべて1タグをつける
glStencilOp(GL_KEEP,GL_KEEP ,GL_REPLACE);
//glColor4f(0.7f, 0.4f, 0.0f, 1.0f);//<-----------------------------床の色
glColor4f(0.91, 0.91, 0.91, 0.5f); //<-----------------------------床の色
DrawFloor( true );//床の描画
/////////////////////////////////////////////
//カラー・デプスバッファマスクをセットする
//これで以下の内容のピクセルの色の値は、書き込まれない。
glColorMask(0,0,0,0);
glDepthMask(0);
/////////////////////////////////////////////
//床にオブジェクトの影のステンシルを付ける
glEnable(GL_STENCIL_TEST);
glStencilFunc( GL_EQUAL, 1, ~0);
//これから描画するもののステンシル値にすべて1タグをつける
glStencilOp(GL_KEEP,GL_KEEP ,GL_INCR);
glDisable(GL_DEPTH_TEST);
glPushMatrix();
glMultMatrixf(pM);
DrawStructure();
glPopMatrix();
glEnable(GL_DEPTH_TEST);
/////////////////////////////////////////////
//ビットマスクを解除
glColorMask(1,1,1,1);
glDepthMask(1);
/////////////////////////////////////////////
//影をつける
glStencilFunc( GL_EQUAL, 2, ~0 );
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColor4f(0.1f, 0.1f, 0.1f, 0.5f);
glDisable(GL_DEPTH_TEST);
DrawFloor( false );//床の描画
glEnable(GL_DEPTH_TEST);
glDisable(GL_BLEND);
glDisable(GL_STENCIL_TEST);
}
//--------------------------------------------------------
// 文字描画
//--------------------------------------------------------
void DISPLAY_TEXT(int x, int y, char *string){
glDisable(GL_LIGHTING);
glDisable(GL_LIGHT0);
glPushAttrib(GL_ENABLE_BIT);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluOrtho2D(0, 100, 0, 100);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glColor3f(0.0, 0.0, 0.0);
glCallList(text_list);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glPopAttrib();
glMatrixMode(GL_MODELVIEW);
text_list=glGenLists(1);
glNewList(text_list,GL_COMPILE);
DRAW_STRING(x, y, string , GLUT_BITMAP_TIMES_ROMAN_24);
glEndList();
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
}
void DRAW_STRING(int x, int y, char *string, void *font){
int len, i;
glRasterPos2f(x, y);
len = (int) strlen(string);
for (i = 0; i < len; i++){
glutBitmapCharacter(font, string[i]);
}
}
//--------------------------------------------------------
// マウスドラッグによる回転
//--------------------------------------------------------
//////////////////////////////////////////////
// クォータニオンの積 r <- p x q
static void qmul(double r[], const double p[], const double q[])
{
r[0] = p[0] * q[0] - p[1] * q[1] - p[2] * q[2] - p[3] * q[3];
r[1] = p[0] * q[1] + p[1] * q[0] + p[2] * q[3] - p[3] * q[2];
r[2] = p[0] * q[2] - p[1] * q[3] + p[2] * q[0] + p[3] * q[1];
r[3] = p[0] * q[3] + p[1] * q[2] - p[2] * q[1] + p[3] * q[0];
}
/////////////////////////////////////////////
// 回転の変換行列 r <- クォータニオン q
static void qrot(double r[], double q[]){
double x2 = q[1] * q[1] * 2.0;
double y2 = q[2] * q[2] * 2.0;
double z2 = q[3] * q[3] * 2.0;
double xy = q[1] * q[2] * 2.0;
double yz = q[2] * q[3] * 2.0;
double zx = q[3] * q[1] * 2.0;
double xw = q[1] * q[0] * 2.0;
double yw = q[2] * q[0] * 2.0;
double zw = q[3] * q[0] * 2.0;
r[ 0] = 1.0 - y2 - z2;
r[ 1] = xy + zw;
r[ 2] = zx - yw;
r[ 4] = xy - zw;
r[ 5] = 1.0 - z2 - x2;
r[ 6] = yz + xw;
r[ 8] = zx + yw;
r[ 9] = yz - xw;
r[10] = 1.0 - x2 - y2;
r[ 3] = r[ 7] = r[11] = r[12] = r[13] = r[14] = 0.0;
r[15] = 1.0;
}
/////////////////////////////////////////////
// ばねの描画
void drowSolidSpring(double x0, double y0, double z0, double x1, double y1, double z1){//
double radian_theta, radian_phi, theta, l;
l = sqrt(pow(x1-x0,2)+pow(y1-y0,2)+pow(z1-z0,2));
if(acos((x1-x0)/(sqrt(pow((x1-x0),2)+pow((y1-y0),2)))) <= PI/2.0 && asin((y1-y0)/(sqrt(pow((x1-x0),2)+pow((y1-y0),2))))>=0){
radian_theta = asin((y1-y0)/sqrt(pow((x1-x0),2)+pow((y1-y0),2)));
radian_phi = acos((z1-z0)/l);
}else if(acos((x1-x0)/(sqrt(pow((x1-x0),2)+pow((y1-y0),2)))) > PI/2.0&& asin((y1-y0)/(sqrt(pow((x1-x0),2)+pow((y1-y0),2))))>0){
radian_theta = acos((x1-x0)/sqrt(pow((x1-x0),2)+pow((y1-y0),2)));
radian_phi = acos((z1-z0)/l);
}else if(acos((x1-x0)/(sqrt(pow((x1-x0),2)+pow((y1-y0),2)))) > PI/2.0 && asin((y1-y0)/(sqrt(pow((x1-x0),2)+pow((y1-y0),2))))<=0){
radian_theta = PI-PI-asin((y1-y0)/sqrt(pow((x1-x0),2)+pow((y1-y0),2)));
radian_phi = -acos((z1-z0)/l);
}else if(acos((x1-x0)/(sqrt(pow((x1-x0),2)+pow((y1-y0),2))))<= PI/2.0 && asin((y1-y0)/(sqrt(pow((x1-x0),2)+pow((y1-y0),2))))<0){
radian_theta = PI-2.0*PI+asin((y1-y0)/sqrt(pow((x1-x0),2)+pow((y1-y0),2)));
radian_phi = -acos((z1-z0)/l);
}
glTranslated(x0, y0, z0);
theta = radian_theta/(2.0*PI) * 360.0;
glRotated( theta , 0.0,0.0,1.0);
theta = radian_phi/(2.0*PI) * 360.0 ;
glRotated(theta, 0.0,1.0,0.0);
theta = radian_theta/(2.0*PI) * 360.0;
glRotated( theta , 0.0,0.0,1.0);
skSolidSpring(10, 10, 5, 1.0, 0.8, l);
}
void skSolidSpring(int Nm, int Ns, int Np, double radius, double ratio, double length)
{
//
//Nm:主円周分割点数,Ns:断面円周分割点数,Np:ピッチ数
//radius:主円周軸半径,ratio:断面半径/主円周軸半径,length:全長
int i, ii, j, jj, k, k1, k2;
double phai, ph1, ph2; //主円周分割点のx軸に対する偏角
double theta, th1, th2; //断面円周分割点のx-z平面に対する偏角
float p[21][21][101][3];//p[44100][3]; //各点の座標
float p1[3], p2[3], p3[3], p4[3];
double rr[21], zz[21];
double pitch, dp, hh;
double r1, r2;
r1 = radius; //主円周軸半径
r2 = ratio * r1;//断面半径
if( Nm > 20 ) Nm = 20;
if( Ns > 20 ) Ns = 20;
if( Np > 100 ) Np = 100;
pitch = length / (double)Np;
//縮んだときの制限
if(pitch < 2 * r2) pitch = 2.0f * r2 ;
dp = pitch / (double)Nm;
//基本断面(x-z)の座標
for(j = 0; j < Ns; j++)
{ theta = PI - 2.0 * PI*(double)j/(double)Ns;
rr[j] = r1 + r2 * cos(theta); //原点からの距離
zz[j] = r2 * sin(theta);//z
}
//他の断面の座標
hh = 0;
for(k = 0; k < Np; k++)
for(i = 0; i < Nm; i++)
{ phai = 2.0 * PI * (double)i/(double)Nm;
for(j = 0; j < Ns; j++)
{
p[i][j][k][0] = (float)(rr[j] * cos(phai)); //x座標
p[i][j][k][1] = (float)(rr[j] * sin(phai)); //y
p[i][j][k][2] = (float)(zz[j] + hh) ; //z
}
hh += dp;//中心軸の高さがdpずつ増加
}
//最終端(k=Np-1,i=Nm)
k = Np - 1; i = Nm;
for(j = 0; j < Ns; j++){
phai = 0.0;
p[i][j][k][0] = (float)(rr[j] * cos(phai)); //x座標
p[i][j][k][1] = (float)(rr[j] * sin(phai)); //y
p[i][j][k][2] = (float)(zz[j] + hh) ; //z
}
//頂点列を定義し描画
for(k = 0; k < Np; k++)
for(i = 0; i < Nm; i++){
ii = i+1;
k1 = k; k2 = k;
if(ii == Nm) {
if(k < Np-1) { ii = 0; k2 = k + 1; }
}
ph1 = 2.0*PI*(double)i / (double)Nm;
ph2 = 2.0*PI*(double)ii / (double)Nm;
for(j = 0;j < Ns; j++)
{
jj = j+1;
if(jj == Ns) jj = 0;
th1 = PI - 2.0 * PI * (double)j / (double)Ns;
th2 = PI - 2.0 * PI * (double)jj / (double)Ns;
//面の頂点 x座標 y座標 z座標
p1[0] = p[i][j][k1][0] ; p1[1] = p[i][j][k1][1] ; p1[2] = p[i][j][k1][2];
p2[0] = p[i][jj][k1][0] ; p2[1] = p[i][jj][k1][1] ; p2[2] = p[i][jj][k1][2];
p3[0] = p[ii][jj][k2][0] ; p3[1] = p[ii][jj][k2][1] ; p3[2] = p[ii][jj][k2][2];
p4[0] = p[ii][j][k2][0] ; p4[1] = p[ii][j][k2][1] ; p4[2] = p[ii][j][k2][2];
glBegin(GL_QUADS);
glNormal3d(cos(th1)*cos(ph1),cos(th1)*sin(ph1),sin(th1));glVertex3fv(p1);
glNormal3d(cos(th2)*cos(ph1),cos(th2)*sin(ph1),sin(th2));glVertex3fv(p2);
glNormal3d(cos(th2)*cos(ph2),cos(th2)*sin(ph2),sin(th2));glVertex3fv(p3);
glNormal3d(cos(th1)*cos(ph2),cos(th1)*sin(ph2),sin(th1));glVertex3fv(p4);
glEnd();
}
}
//始端(k=0)
glBegin(GL_POLYGON);
glNormal3d(0.0,-1.0,0.0);
for(j = Ns-1; j >= 0; j--) glVertex3fv(p[0][j][0]);
glEnd();
//終端(k=Np)
glBegin(GL_POLYGON);
glNormal3d(0.0,1.0,0.0);
for(j = 0; j < Ns; j++) glVertex3fv(p[Nm][j][Np-1]);
glEnd();
}
ばねを描画するための関数
2点間をつなぐ関数
2点の座標を引数とします。 次で定義されるばねを回転させることで、2点を結ぶばねを描画します。
/////////////////////////////////////////////
// ばねの描画
void drowSolidSpring(double x0, double y0, double z0, double x1, double y1, double z1){//
double l = sqrt(pow(x1-x0,2)+pow(y1-y0,2)+pow(z1-z0,2));
if(acos((x1-x0)/(sqrt(pow((x1-x0),2)+pow((y1-y0),2)))) <= PI/2.0 && asin((y1-y0)/(sqrt(pow((x1-x0),2)+pow((y1-y0),2))))>=0){
radian_theta = asin((y1-y0)/sqrt(pow((x1-x0),2)+pow((y1-y0),2)));
radian_phi = acos((z1-z0)/l);
}else if(acos((x1-x0)/(sqrt(pow((x1-x0),2)+pow((y1-y0),2)))) > PI/2.0&& asin((y1-y0)/(sqrt(pow((x1-x0),2)+pow((y1-y0),2))))>0){
radian_theta = acos((x1-x0)/sqrt(pow((x1-x0),2)+pow((y1-y0),2)));
radian_phi = acos((z1-z0)/l);
}else if(acos((x1-x0)/(sqrt(pow((x1-x0),2)+pow((y1-y0),2)))) > PI/2.0 && asin((y1-y0)/(sqrt(pow((x1-x0),2)+pow((y1-y0),2))))<=0){
radian_theta = PI-PI-asin((y1-y0)/sqrt(pow((x1-x0),2)+pow((y1-y0),2)));
radian_phi = -acos((z1-z0)/l);
}else if(acos((x1-x0)/(sqrt(pow((x1-x0),2)+pow((y1-y0),2))))<= PI/2.0 && asin((y1-y0)/(sqrt(pow((x1-x0),2)+pow((y1-y0),2))))<0){
radian_theta = PI-2.0*PI+asin((y1-y0)/sqrt(pow((x1-x0),2)+pow((y1-y0),2)));
radian_phi = -acos((z1-z0)/l);
}
glTranslated(x0, y0, z0);
theta = radian_theta/(2.0*PI) * 360.0;
glRotated( theta , 0.0,0.0,1.0);
theta = radian_phi/(2.0*PI) * 360.0 ;
glRotated(theta, 0.0,1.0,0.0);
theta = radian_theta/(2.0*PI) * 360.0;
glRotated( theta , 0.0,0.0,1.0);
skSolidSpring(10, 10, 5, 1.0, 0.8, l);
}
ばねの定義
ばねの定義を「OpenGLで作る力学アニメーション入門: C++Builder,Visual C++.NETによるWindowsアプリケーションの開発」のサンプルプログラムを参考にさせていただいております。
void skSolidSpring(int Nm, int Ns, int Np, double radius, double ratio, double length)
{
//
//Nm:主円周分割点数,Ns:断面円周分割点数,Np:ピッチ数
//radius:主円周軸半径,ratio:断面半径/主円周軸半径,length:全長
int i, ii, j, jj, k, k1, k2;
double phai, ph1, ph2; //主円周分割点のx軸に対する偏角
double theta, th1, th2; //断面円周分割点のx-z平面に対する偏角
float p[21][21][101][3];//p[44100][3]; //各点の座標
float p1[3], p2[3], p3[3], p4[3];
double rr[21], zz[21];
double pitch, dp, hh;
double r1, r2;
r1 = radius; //主円周軸半径
r2 = ratio * r1;//断面半径
if( Nm > 20 ) Nm = 20;
if( Ns > 20 ) Ns = 20;
if( Np > 100 ) Np = 100;
pitch = length / (double)Np;
//縮んだときの制限
if(pitch < 2 * r2) pitch = 2.0f * r2 ;
dp = pitch / (double)Nm;
//基本断面(x-z)の座標
for(j = 0; j < Ns; j++)
{ theta = PI - 2.0 * PI*(double)j/(double)Ns;
rr[j] = r1 + r2 * cos(theta); //原点からの距離
zz[j] = r2 * sin(theta);//z
}
//他の断面の座標
hh = 0;
for(k = 0; k < Np; k++)
for(i = 0; i < Nm; i++)
{ phai = 2.0 * PI * (double)i/(double)Nm;
for(j = 0; j < Ns; j++)
{
p[i][j][k][0] = (float)(rr[j] * cos(phai)); //x座標
p[i][j][k][1] = (float)(rr[j] * sin(phai)); //y
p[i][j][k][2] = (float)(zz[j] + hh) ; //z
}
hh += dp;//中心軸の高さがdpずつ増加
}
//最終端(k=Np-1,i=Nm)
k = Np - 1; i = Nm;
for(j = 0; j < Ns; j++){
phai = 0.0;
p[i][j][k][0] = (float)(rr[j] * cos(phai)); //x座標
p[i][j][k][1] = (float)(rr[j] * sin(phai)); //y
p[i][j][k][2] = (float)(zz[j] + hh) ; //z
}
//頂点列を定義し描画
for(k = 0; k < Np; k++)
for(i = 0; i < Nm; i++){
ii = i+1;
k1 = k; k2 = k;
if(ii == Nm) {
if(k < Np-1) { ii = 0; k2 = k + 1; }
}
ph1 = 2.0*PI*(double)i / (double)Nm;
ph2 = 2.0*PI*(double)ii / (double)Nm;
for(j = 0;j < Ns; j++)
{
jj = j+1;
if(jj == Ns) jj = 0;
th1 = PI - 2.0 * PI * (double)j / (double)Ns;
th2 = PI - 2.0 * PI * (double)jj / (double)Ns;
//面の頂点 x座標 y座標 z座標
p1[0] = p[i][j][k1][0] ; p1[1] = p[i][j][k1][1] ; p1[2] = p[i][j][k1][2];
p2[0] = p[i][jj][k1][0] ; p2[1] = p[i][jj][k1][1] ; p2[2] = p[i][jj][k1][2];
p3[0] = p[ii][jj][k2][0] ; p3[1] = p[ii][jj][k2][1] ; p3[2] = p[ii][jj][k2][2];
p4[0] = p[ii][j][k2][0] ; p4[1] = p[ii][j][k2][1] ; p4[2] = p[ii][j][k2][2];
glBegin(GL_QUADS);
glNormal3d(cos(th1)*cos(ph1),cos(th1)*sin(ph1),sin(th1));glVertex3fv(p1);
glNormal3d(cos(th2)*cos(ph1),cos(th2)*sin(ph1),sin(th2));glVertex3fv(p2);
glNormal3d(cos(th2)*cos(ph2),cos(th2)*sin(ph2),sin(th2));glVertex3fv(p3);
glNormal3d(cos(th1)*cos(ph2),cos(th1)*sin(ph2),sin(th1));glVertex3fv(p4);
glEnd();
}
}
//始端(k=0)
glBegin(GL_POLYGON);
glNormal3d(0.0,-1.0,0.0);
for(j = Ns-1; j >= 0; j--) glVertex3fv(p[0][j][0]);
glEnd();
//終端(k=Np)
glBegin(GL_POLYGON);
glNormal3d(0.0,1.0,0.0);
for(j = 0; j < Ns; j++) glVertex3fv(p[Nm][j][Np-1]);
glEnd();
}




