OPいじりPV3D
布シミュをOPに適用してみる by NOVO
* 暴れるのを何とかしたいところ。積分法の変更?
* Done 3Dにしてみたい(見た目)
* Done 3Dにしてみたい(計算)
* Done 単位系をそろえる
* Done Fの計算が終わる前にXを動かしているのが偏りの原因っぽい
* Done せん断抵抗、角度抵抗用のJointを設定したい
* Done 微小時間でシミュレート
* Doing 積分法を変えてみる?
* Done 初期化変更、必要ない変数も消す。描画方法もJointの描画だけでいいのでは?
* Done 片方向のJointを両方向に変更(力の計算場所も変更)
* Done ダンパ力を設定
* Done 簡易ベクトルクラスに置き換え
* Done ばねの自然長計算を変更
* Done 3Dのマウスみなおし
* Done Jointの種類によってパラメータを変える
* Done 形を半球にする
* Done 重力に負けないようにする(圧力バネ成分)
* 以下オリジナルコメント
GraphicsPathCommandでラインを描画するバージョン。
* drawLine関数の中が違います。
*
* 本格的な布のシミュレーションではありません。
*
* ドラッグでマウスに一番近いポイントを移動させる。
* ctrlキー押しながらドラッグで固定。
* ダブルクリックで固定を解除。
/**
* 布シミュをOPに適用してみる by NOVO
* 暴れるのを何とかしたいところ。積分法の変更?
* Done 3Dにしてみたい(見た目)
* Done 3Dにしてみたい(計算)
* Done 単位系をそろえる
* Done Fの計算が終わる前にXを動かしているのが偏りの原因っぽい
* Done せん断抵抗、角度抵抗用のJointを設定したい
* Done 微小時間でシミュレート
* Doing 積分法を変えてみる?
* Done 初期化変更、必要ない変数も消す。描画方法もJointの描画だけでいいのでは?
* Done 片方向のJointを両方向に変更(力の計算場所も変更)
* Done ダンパ力を設定
* Done 簡易ベクトルクラスに置き換え
* Done ばねの自然長計算を変更
* Done 3Dのマウスみなおし
* Done Jointの種類によってパラメータを変える
* Done 形を半球にする
* Done 重力に負けないようにする(圧力バネ成分)
* 以下オリジナルコメント
*/
// forked from miniapp's GraphicsPathCommand使ったバージョン forked from: 布
/**
* GraphicsPathCommandでラインを描画するバージョン。
* drawLine関数の中が違います。
*
* 本格的な布のシミュレーションではありません。
*
* ドラッグでマウスに一番近いポイントを移動させる。
* ctrlキー押しながらドラッグで固定。
* ダブルクリックで固定を解除。
*/
package {
import adobe.utils.CustomActions;
import flash.display.DisplayObject;
import flash.display.GraphicsPathCommand;
import flash.display.Sprite;
import flash.display.StageQuality;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.filters.ColorMatrixFilter;
import flash.sampler.NewObjectSample;
import net.hires.debug.Stats;
import org.papervision3d.core.geom.TriangleMesh3D;
import org.papervision3d.core.proto.MaterialObject3D;
import org.papervision3d.core.utils.Mouse3D;
import org.papervision3d.core.utils.virtualmouse.VirtualMouse;
import org.papervision3d.materials.ColorMaterial;
import org.papervision3d.materials.shadematerials.FlatShadeMaterial;
import org.papervision3d.materials.shadematerials.GouraudMaterial;
import org.papervision3d.materials.utils.MaterialsList;
import org.papervision3d.objects.DisplayObject3D;
import org.papervision3d.objects.primitives.Cube;
import org.papervision3d.objects.primitives.Plane;
import org.papervision3d.view.BasicView;
import org.papervision3d.core.proto.LightObject3D;
import org.papervision3d.core.math.Number3D;
[SWF(backgroundColor="0x8080ff", width="465", height="465", frameRate="60")]
public class OP_PV3D extends BasicView {
private var light:LightObject3D = null;
private var op:OP = null;
private var backGroundPlane:Plane = null;
private var _isCtrlPress:Boolean = false;
private var _isMouseDown:Boolean = false;
private var _draggedPoint:Point;
public static const STAGE_WIDTH:uint = 465;
public static const STAGE_HEIGHT:uint = 465;
private var mouse3D:Mouse3D = null;
public function OP_PV3D() {
super(STAGE_WIDTH, STAGE_HEIGHT, false, true);
init();
}
private function init(e:Event = null):void {
light = new LightObject3D();
light.z = -800;
light.x = 0;
light.y = 800;
/*
scene.addChild(
new Cube(
new MaterialsList(
{
all:
//new FlatShadeMaterial(
// light,
// 0xffffff,
// 0x404040,
// 0
//)
new ColorMaterial()
}
),
100,
100,
100
)
);
*/
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.doubleClickEnabled = true;
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDwonHandler);
stage.addEventListener(MouseEvent.DOUBLE_CLICK, doubleClickHandler);
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDonwHandler);
stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
Mouse3D.enabled = true;
viewport.interactive = true;
this.camera.z = -300;
//this.camera.x = 800;
//this.camera.z = 0;
mouse3D = viewport.interactiveSceneManager.mouse3D;
var mat:FlatShadeMaterial = new FlatShadeMaterial(light, 0xEBD7BE, 0x404040, 0);
// var mat:GouraudMaterial = new GouraudMaterial(light, 0xffffff, 0x404040, 0);
mat.doubleSided = true;//両面表示
op = new OP(mat);
op.rotationY -= 45;
scene.addChild(op);
//マウス座標検出用
backGroundPlane = new Plane(new ColorMaterial(0xff80ff, 1, true), 10000, 10000);
backGroundPlane.z = 100;
scene.addChild(backGroundPlane);
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
addChild(new Stats());
startRendering();
}
/**
* 一番カーソルに近いポイントを捜す。
*/
private function searchPoint():Point {
return op.searchPoint(mouse3D.x,mouse3D.y);
}
private function enterFrameHandler(e:Event):void {
if (_isMouseDown) {
_draggedPoint._position.x = mouse3D.x;
_draggedPoint._position.y = mouse3D.y;
}
op.update(1.0 / stage.frameRate);
}
private function keyDonwHandler(e:KeyboardEvent = null):void{
if(e.ctrlKey) _isCtrlPress = true;
}
private function keyUpHandler(e:KeyboardEvent = null):void{
_isCtrlPress = false;
}
private function doubleClickHandler(e:MouseEvent = null):void{
searchPoint().isPinned = false;
}
private function mouseDwonHandler(e:MouseEvent):void {
_isMouseDown = true;
_draggedPoint = searchPoint();
_draggedPoint.isDragging = true;
}
private function mouseUpHandler(e:MouseEvent):void {
_isMouseDown = false;
if (_draggedPoint)
{
if (_isCtrlPress)
_draggedPoint.isPinned = true;
_draggedPoint.isDragging = false;
_draggedPoint = undefined;
}
}
}
}
import org.papervision3d.core.geom.TriangleMesh3D;
import org.papervision3d.core.geom.renderables.Triangle3D;
import org.papervision3d.core.proto.LightObject3D;
import org.papervision3d.core.geom.renderables.Vertex3D;
import org.papervision3d.core.proto.MaterialObject3D;
class OP extends TriangleMesh3D
{
private var _joints:Vector.<Joint> = new Vector.<Joint>();
private var _points:Vector.<Point> = new Vector.<Point>();
//ポリゴン用配列 面の数*3
private var _faces:Vector.<int> = new Vector.<int>();
private var _cols:uint = 12;//横の数
private var _rows:uint = 6;//縦の数
public static const RADIUS:int = 90;//[mm]
public function OP(inMaterial:MaterialObject3D)
{
super(inMaterial, [], []);
putPointAndJoint();
}
/**
* 一番カーソルに近いポイントを捜す。
*/
public function searchPoint(inMouseX:Number,inMouseY:Number):Point
{
var lastMinDist:Number = Infinity;
var target:Point;
for each(var point:Point in _points) {
var pos:MyVector3D = point._position;
var m:MyVector3D=new MyVector3D(inMouseX,inMouseY,pos.z);
var dist:Number = pos.subtract(m).length();
if (dist < lastMinDist) {
lastMinDist = dist;
target = point;
}
}
return target;
}
private function putPointAndJoint():void
{
//Xを経度
//Yを緯度
const DEG_TO_RAD:Number=Math.PI/180.0;
var diffRotX:Number = DEG_TO_RAD*360.0 / _cols;
var diffRotY:Number = DEG_TO_RAD*90.0 / (_rows-1);
_points.length = 0;
_joints.length = 0;
_faces.length = 0;
for (var newIndex:int = 0; newIndex < getOpCount();++newIndex )
{
_points[newIndex] = new Point();
}
//ポイントとジョイントを一気に配置したほうが楽なので
for(var y:uint=0;y<_rows;++y)
{
for(var x:uint=0;x<_cols;++x)
{
var point:Point = _points[getOpIndex(y, x)];
point.name = String(y) + "-" + String(x) + " ";//デバッグ用
//var noize:Number = Math.random()*10;//0から1
//よこ
//var tmp:MyVector3D = new MyVector3D(0,Math.cos(diffRotX*x),Math.sin(diffRotX*x));
//point._position = new MyVector3D(Math.sin(diffRotY * y) * RADIUS, Math.cos(diffRotY*y)*tmp.y*RADIUS, Math.cos(diffRotY*y)*tmp.z*RADIUS);
var tmp:MyVector3D = new MyVector3D(Math.sin(diffRotX*x),Math.cos(diffRotX*x),0);
point._position = new MyVector3D(
Math.cos(diffRotY * y) * tmp.x * RADIUS,
Math.cos(diffRotY * y) * tmp.y * RADIUS,
-Math.sin(diffRotY * y) * RADIUS
);
//面
if (y > 0)
{
_faces[_faces.length] = getOpIndex(y - 1,x - 1);
_faces[_faces.length] = getOpIndex(y + 0,x - 1);
_faces[_faces.length] = getOpIndex(y - 1,x + 0);
if (y < _rows - 1)
{
_faces[_faces.length] =getOpIndex(y - 1, x + 0);
_faces[_faces.length] =getOpIndex(y + 0, x - 1);
_faces[_faces.length] =getOpIndex(y + 0, x + 0);
}
}
if(y>0)
{
var pointUp:Point = _points[getOpIndex(y - 1, x)];
_joints.push(new Joint(point, pointUp));//たて
if (y < _rows - 1)
{
var pointNaname1:Point = _points[getOpIndex(y - 1, x - 1)];
var jointNaname1:Joint = new Joint(point, pointNaname1);
jointNaname1.SPRING *= 0.75;
_joints.push(jointNaname1);//ななめ1(せん断抵抗)
var pointNaname2:Point = _points[getOpIndex(y - 1, x + 1)];
var jointNaname2:Joint = new Joint(point, pointNaname2);
jointNaname2.SPRING *= 0.75;
_joints.push(jointNaname2);//ななめ2(せん断抵抗)
}
}
if (y < _rows - 1)
{
var pointLeft:Point = _points[getOpIndex(y, x - 1)];
_joints.push(new Joint(point, pointLeft));//よこ
}
if(y>1)
{
var pointUp2:Point = _points[getOpIndex(y - 2, x)];
var jointUp2:Joint = new Joint(point, pointUp2);
jointUp2.SPRING *= 1.0;
_joints.push(jointUp2);//たて ひとつ飛ばし(角度抵抗)
}
if (y < _rows - 1)
{
var pointLeft2:Point = _points[getOpIndex(y,x - 2)];
var jointLeft2:Joint = new Joint(point, pointLeft2);
jointLeft2.SPRING *= 1.0;
_joints.push(jointLeft2);//よこ ひとつ飛ばし(角度抵抗)
// var pointDiagonal:Point = _points[getOpIndex(y,x+_cols/2)];
// var jointDiagonal:Joint = new Joint(point, pointDiagonal);
// jointDiagonal.SPRING *= 0.5;
// _joints.push(jointDiagonal);//よこ 対角線
}
}
//極部分はバネが集中するので重くする
var pointPole:Point = _points[_points.length - 2];
pointPole.mass *= 1.5;
//中心部分を設定
var pointCenter:Point = _points[_points.length - 1];
pointCenter._position = new MyVector3D();
pointCenter.isPinned = true;
for (var volIndex:int = 0; volIndex < _points.length -1;++volIndex)
{//圧力
var jointVol:Joint = new Joint(pointCenter, _points[volIndex]);
jointVol.SPRING *= 0.25;
_joints.push(jointVol);
}
for each(var joint:Joint in _joints)
{
joint.resetNaturalLength();
}
//根元を固定します。
for (var i:int = 0; i < _cols;++i)
{
_points[i].isPinned = true;
_points[i+_cols].isPinned = true;
}
}
trace("_poinits.length"+_points.length);
trace("_joints.length"+_joints.length);
_points.fixed = true;
_joints.fixed = true;
_faces.fixed = true;
}
private function getOpCount():int
{
return _cols * (_rows - 1) + 1+1;//周囲+極+中心
}
private function getOpIndex(inY:int, inX:int):int
{
if (inY >= _rows-1)
{
return inY * _cols;
}
else
{
return inY * _cols + (_cols + inX) % _cols;
}
}
public function update(inDt:Number):void
{
//フェーズを二つに分ける。力更新と位置更新
// update force
for each(var joint1:Joint in _joints) {
joint1.updateForce();
}
for each(var point1:Point in _points) {
point1.updateForce();//多分下のループに入れても影響はないけど、明示的に分ける。
}
// update position
for each(var point2:Point in _points) {
const times:int = 10;
for (var i:int = 0; i < times;++i)
{
point2.updatePosition(inDt/times);
}
}
//drawLine();
transfer();
}
//頂点更新
private function transfer():void
{
var vs:Array = geometry.vertices;
if (vs.length != _points.length)
{
vs.length = 0;
var fs:Array = geometry.faces;
fs.length = 0;
for each(var point:Point in _points)
{
vs[vs.length] = new Vertex3D(point._position.x, point._position.y, point._position.z);
}
for (var i:int = 0; i < _faces.length/3;++i)
{
fs[fs.length] = new Triangle3D(
this,
[
vs[_faces[i * 3 + 0]],
vs[_faces[i * 3 + 1]],
vs[_faces[i * 3 + 2]]
]
);
}
}
else
{
for(var j:int = 0; j < _points.length;++j )
{
var point2:Point = _points[j];
var v:Vertex3D = vs[j];
v.x = point2._position.x;
v.y = point2._position.y;
v.z = point2._position.z;
}
}
for each(var face:Triangle3D in geometry.faces)
{
face.createNormal();//法線再計算
}
}
}
//簡易ベクトルクラス
class MyVector3D
{
public var x:Number = 0.0;
public var y:Number = 0.0;
public var z:Number = 0.0;
public function MyVector3D(inX:Number=0.0, inY:Number=0.0,inZ:Number=0.0)
{
x = inX;
y = inY;
z = inZ;
}
//+=
public function addTo(rhs:MyVector3D):MyVector3D
{
this.x += rhs.x;
this.y += rhs.y;
this.z += rhs.z;
return this;
}
//+
public function add(rhs:MyVector3D):MyVector3D
{
return new MyVector3D(this.x + rhs.x, this.y + rhs.y, this.z + rhs.z);
}
//-=
public function subtractTo(rhs:MyVector3D):MyVector3D
{
this.x -= rhs.x;
this.y -= rhs.y;
this.z -= rhs.z;
return this;
}
//-
public function subtract(rhs:MyVector3D):MyVector3D
{
return new MyVector3D(this.x - rhs.x, this.y - rhs.y, this.z - rhs.z);
}
//*=
public function multiplyTo(rhs:Number):MyVector3D
{
this.x *= rhs;
this.y *= rhs;
this.z *= rhs;
return this;
}
//*
public function multiply(rhs:Number):MyVector3D
{
return new MyVector3D(this.x*rhs,this.y*rhs,this.z*rhs);
}
///=
public function divideTo(rhs:Number):MyVector3D
{
this.x /= rhs;
this.y /= rhs;
this.z /= rhs;
return this;
}
///
public function divide(rhs:Number):MyVector3D
{
return new MyVector3D(this.x/rhs,this.y/rhs,this.z/rhs);
}
public function length2():Number
{
return this.x * this.x + this.y * this.y+ this.z * this.z;
}
public function length():Number
{
return Math.sqrt(length2());
}
//単位ベクトル
public function normalize():MyVector3D
{
var l:Number = this.length();
if (l > 0)
{
return this.divide(l);
}
else
{
return new MyVector3D();
}
}
}
class Joint {
public var SPRING:Number = 50.0;//[N/mm]
public var DAMPER:Number = 0.025;//[N/(mm/s)]
public var _naturalLength:Number = 0.0;
public var _point:Point=null;
public var _target:Point=null;
public function Joint(point:Point, target:Point) {
_point=point;
_target = target;
resetNaturalLength();
}
public function resetNaturalLength():void
{
_naturalLength = _point._position.subtract(_target._position).length();
//trace( _point.name+ " " +_target.name +" length:"+ _naturalLength);
}
public function updateForce():void
{
//バネの力
var dx:MyVector3D = _target._position.subtract(_point._position);
var nx:MyVector3D = dx.normalize();//単位ベクトル
var springForce:MyVector3D = nx.multiply((dx.length() - _naturalLength) * SPRING);
//ダンパの力
var dv:MyVector3D = _target._velocity.subtract(_point._velocity);
var damperForce:MyVector3D = dv.multiply(DAMPER);
//合力
var totalForce:MyVector3D = springForce.add(damperForce);
_point._force.addTo(totalForce);
//逆の力をかける
_target._force.addTo(totalForce.multiply(-1));
}
}
class Point {
public var name:String;
public var _position:MyVector3D = new MyVector3D();//[mm]
public var _velocity:MyVector3D = new MyVector3D();//[mm/s]
public var _force:MyVector3D = new MyVector3D();//[N]
public var mass:Number=3.0/1000;//[kg]
public var isPinned:Boolean = false;
public var isDragging:Boolean = false;
public static var GRAVITY:Number = 9.8 * 1000;//[mm/(s*s)]
public static var AIR_FRICTION:Number = 0.005;//[N/(mm/s)]
public function updateForce():void
{
_force.addTo(_velocity.multiply(AIR_FRICTION*-1));
}
public function updatePosition(inDt:Number):void
{
if (isDragging || isPinned)
{
_velocity = new MyVector3D();
_force = new MyVector3D();
}
else
{
var a:MyVector3D = _force.divide(mass);
// a.y -= GRAVITY/Math.sqrt(2);
// a.z -= GRAVITY/Math.sqrt(2);//むりやり
a.y -= GRAVITY;
if (0)
{
_velocity.addTo(a.multiply(inDt));
_position.addTo(_velocity.multiply(inDt));
}
else
{
var k:MyVector3D = new MyVector3D();
var l:MyVector3D = new MyVector3D();
rungeKutta(a, _velocity, _position,inDt, k, l);
_position.addTo(k);
_velocity.addTo(l);
}
_force = new MyVector3D();
}
}
//オイラー法で暴走したので、ルンゲクッタ法という積分法を使う
//http://www6.ocn.ne.jp/~simuphys/runge-kutta.html
private function rungeKutta(inA:MyVector3D, inV:MyVector3D, inX:MyVector3D, inDt:Number, outK:MyVector3D, outL:MyVector3D):void
{
var x1:MyVector3D = inV.multiply(inDt);
var v1:MyVector3D = inA.multiply(inDt);
var x2:MyVector3D = inV.add(v1.multiply(0.5)).multiply(inDt);
var v2:MyVector3D = inA.multiply(inDt * 0.5);//あってる? Aを求めなおす必要がある?
var x3:MyVector3D = inV.add(v2.multiply(0.5)).multiply(inDt);
var v3:MyVector3D = inA.multiply(inDt * 0.5);//あってる? Aを求めなおす必要がある?
var x4:MyVector3D = inV.add(v3).multiply(inDt);
var v4:MyVector3D = inA.multiply(inDt);//あってる? Aを求めなおす必要がある?
outK.addTo(x1.add(x2.multiply(2)).add(x3.multiply(2)).add(x4).divide(6));//代入の代わり
outL.addTo(v1.add(v2.multiply(2)).add(v3.multiply(2)).add(v4).divide(6));//代入の代わり
}
}