class IntEnv3D { PApplet ap; IntCamera3D cam; IntMouse3D m3d; float x1, y1, z1, x2, y2, z2; float grav; float fric_bound; float dt, fps; IntObj3D[] objs; //オブジェクトタイプの定数 final int TYPE_BALL = 0; IntEnv3D(PApplet iap, float ix1, float iy1, float iz1, float ix2, float iy2, float iz2, float framerate){ ap = iap; x1 = ix1; y1 = iy1; z1 = iz1; x2 = ix2; y2 = iy2; z2 = iz2; fps = framerate; dt = 1 / fps; grav = 1600; fric_bound = 50; cam = new IntCamera3D(this, PI/4, (x2+x1)/2, (y2+y1)/2, (z2+z1)/2, 1800, PI/3, -0.3); m3d = new IntMouse3D(this, cam.r); objs = new IntObj3D[1]; objs[0] = new IntBall3D(this, (x2+x1)/2, (y2+y1)/2, (z2+z1)/2, 300, color(128, 140, 128)); objs[0].enable_drag = false; objs[0].fixed = true; //objs[0].conf_rate = 1; } void update(){ int i, j; //時間変位 // #windowsのtimerは10msの精度しかないのでProcessingが // #持っているframeRateを使って計算 dt = 1 / frameRate; //カメラの操作 boolean fk = cam.operate(); //3D空間内のマウスカーソルを更新 if(!fk) m3d.operate(); //ボールのマウス操作 for(i=0; i 0 ) return -ix * grav / dd; else return 0; } float gravY(float ix, float iy, float iz){ //return grav; float dd = mag(ix, iy, iz); if( dd > 0 ) return -iy * grav / dd; else return 0; } float gravZ(float ix, float iy, float iz){ //return 0; float dd = mag(ix, iy, iz); if( dd > 0 ) return -iz * grav / dd; else return 0; } float gravMag(float ix, float iy, float iz){ return grav; } float fric(float ix, float iy, float iz, float ivx, float ivy, float ivz){ //return 0; return constrain( 350 - mag(ix, iy, iz), 0, 350 ); } float altitude(float ix, float iy, float iz){ //return (y2 - iy); return mag(ix, iy, iz); } boolean boundX(IntObj3D obj){ /* if( obj.obj_type == TYPE_BALL ){ IntBall3D b = (IntBall3D) obj; if( b.x <= x1 + b.r || x2 - b.r <= b.x ){ b.x = constrain( b.x, x1 + b.r, x2 - b.r ); return true; } }*/ return false; } boolean boundY(IntObj3D obj){ /* if( obj.obj_type == TYPE_BALL ){ IntBall3D b = (IntBall3D) obj; if( b.y <= y1 + b.r || y2 - b.r <= b.y ){ b.y = constrain( b.y, y1 + b.r, y2 - b.r ); return true; } }*/ return false; } boolean boundZ(IntObj3D obj){ /* if( obj.obj_type == TYPE_BALL ){ IntBall3D b = (IntBall3D) obj; if( b.z <= z1 + b.r || z2 - b.r <= b.z ){ b.z = constrain( b.z, z1 + b.r, z2 - b.r ); return true; } }*/ return false; } void bound(IntObj3D obj){ /* if( obj.obj_type == TYPE_BALL ){ IntBall3D b = (IntBall3D) obj; float adj_vel = 1; boolean bx = boundX(b); boolean by = boundY(b); boolean bz = boundZ(b); if(bx || by || bz){ //境界での位置補正によるエネルギーの誤差を減らす float v = mag(b.vx, b.vy, b.vz); float vv = 2 * ( b.eng / b.m - gravMag(b.x, b.y, b.z) * altitude(b.x, b.y, b.z) ) ; if(v > 0 && vv > 0) adj_vel = sqrt(vv) / v; } if(bx) b.vx *= - b.conf_rate * adj_vel; if(by) b.vy *= - b.conf_rate * adj_vel; if(bz) b.vz *= - b.conf_rate * adj_vel; if(bx || by || bz){ b.calc_eng(); b.on_collision = true; b.fric_rate = b.fric_rate_org + fric_bound; } } */ } void collision(IntObj3D o1, IntObj3D o2){ if( !o1.enable_collision || !o2.enable_collision ) return; IntBall3D b1, b2; if( o1.obj_type == TYPE_BALL && o2.obj_type == TYPE_BALL ){ b1 = (IntBall3D) o1; b2 = (IntBall3D) o2; //ボール1の中心からボール2の中心へのベクトル float px = b2.x - b1.x; float py = b2.y - b1.y; float pz = b2.z - b1.z; //中心間の距離 float dd = dist(0, 0, 0, px, py, pz); if( dd <= b1.r + b2.r ){ b1.on_collision = b2.on_collision = true; b1.fric_rate = b2.fric_rate = b1.fric_rate_org + b2.fric_rate_org; b1.stopped = b2.stopped = false; //中心間のベクトルをxz平面に投影したベクトルがx軸となす角度A float angA = atan2(pz, px); //中心間のベクトルがxz平面になす角度ee float angE = atan2(py, dist(0,0,px,pz)); float sinA = sin(angA); float cosA = cos(angA); float sinE = sin(angE); float cosE = cos(angE); //ボール1の速度ベクトルを回転 float tvx1 = cosA*cosE*b1.vx + sinE*b1.vy + sinA*cosE*b1.vz; float tvy1 = - cosA*sinE*b1.vx + cosE*b1.vy - sinA*sinE*b1.vz; float tvz1 = - sinA*b1.vx + cosA*b1.vz; //ボール2の速度ベクトルを回転 float tvx2 = cosA*cosE*b2.vx + sinE*b2.vy + sinA*cosE*b2.vz; float tvy2 = - cosA*sinE*b2.vx + cosE*b2.vy - sinA*sinE*b2.vz; float tvz2 = - sinA*b2.vx + cosA*b2.vz; //ボール1,2の質量 float m1 = b1.m; float m2 = b2.m; float conf = b1.conf_rate * b2.conf_rate; //運動量保存の法則により衝突後の速度ベクトルを求める float tvx1n = ( ( m1 - conf*m2 ) * tvx1 + m2 * ( 1 + conf ) * tvx2 ) / ( m1 + m2 ); float tvx2n = ( m1 * ( 1 + conf ) * tvx1 + ( m2 - conf*m1 ) * tvx2 ) / ( m1 + m2 ); //速度ベクトルを逆回転して速度の更新完了 b1.vx = cosA*cosE*tvx1n - cosA*sinE*tvy1 - sinA*tvz1; b1.vy = sinE*tvx1n + cosE*tvy1 ; b1.vz = sinA*cosE*tvx1n - sinA*sinE*tvy1 + cosA*tvz1; b2.vx = cosA*cosE*tvx2n - cosA*sinE*tvy2 - sinA*tvz2; b2.vy = sinE*tvx2n + cosE*tvy2 ; b2.vz = sinA*cosE*tvx2n - sinA*sinE*tvy2 + cosA*tvz2; //位置の更新(ボール同士が重なったままにならないように) //質量が小さい方のボールが逃げるように調整 if(b1.fixed){ b2.x += (b1.r + b2.r - dd) * px / dd; b2.y += (b1.r + b2.r - dd) * py / dd; b2.z += (b1.r + b2.r - dd) * pz / dd; b1.vx = b1.vy = b1.vz = 0; } else if(b2.fixed){ b1.x += (b1.r + b2.r - dd) * (-px) / dd; b1.y += (b1.r + b2.r - dd) * (-py) / dd; b1.z += (b1.r + b2.r - dd) * (-pz) / dd; b2.vx = b2.vy = b2.vz = 0; } else{ b1.x += (b1.r + b2.r - dd) * m2 / (m1 + m2) * (-px) / dd; b1.y += (b1.r + b2.r - dd) * m2 / (m1 + m2) * (-py) / dd; b1.z += (b1.r + b2.r - dd) * m2 / (m1 + m2) * (-pz) / dd; b2.x += (b1.r + b2.r - dd) * m1 / (m1 + m2) * px / dd; b2.y += (b1.r + b2.r - dd) * m1 / (m1 + m2) * py / dd; b2.z += (b1.r + b2.r - dd) * m1 / (m1 + m2) * pz / dd; } //エネルギーを再計算 b1.calc_eng(); b2.calc_eng(); } } } }