/* * ★ジンバルロックを体験してみよう! * * 0.左上のrotationXYZの立方体に注目 * 1.初期状態(又はReset後)に「Y+=30」を三回クリックして、 * Y軸90度回転をする。 * 2.「Z+=30」をクリックしてみる。Z軸回転したことを確認。 * 3.「Z-=30」をクリックして戻す。 * 4.「X-=30」をクリックすると、、、アレレ!「Z+=30」と同じ * Z軸回転しちゃうぞ! * これがジンバルロックであります。 * いろいろ回転させていろいろ試してくださいませ。 * * 課題 * ジャイロスコープを作ったらもうちょっとわかりやすくなるよね、、、。そのうち、、、。 * あと、FlashPlayer10のビルトイン関数のMatrix3Dではどうなるかのほうが * 一般性があるよね。遠くないうちにやります。 * ============================= * * Papervision3Dにおいて、rotationX,localRotationX,Quaternion * の使い分け。 * * 左下のボタン、一回クリックごとにそれぞれの値を変化させる。 * * ■rotationXYZ * 簡単なものなら、rotationXで足りるものもあるんだろうけど、 * 自在に回転させるとなると、どうしてもジンバルロックが問題になる。 * 値がわかりやすいってのが強み。 * * ■localRotationXYZ * localRotationXってのは、なんだろうと思ってたけど、比較すると良くわかった。 * ジンバルロック回避だけなら、これでなんとかなっちゃうこともあるかも。 * * ■Quaternion * 本命はやっぱりQuaternion。 * ジンバルロックは無いし、画面上と同じX,Y軸回転をするからUIとの * 統合もしやすい。 * 内部的にはちょっとめんどくさくなるのは嫌だけど。 * * * */ package { import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.text.TextField; import org.papervision3d.lights.PointLight3D; import org.papervision3d.materials.shadematerials.FlatShadeMaterial; import org.papervision3d.objects.primitives.Cube; import org.papervision3d.view.BasicView; import org.papervision3d.materials.utils.MaterialsList; import org.papervision3d.objects.DisplayObject3D; import flash.display.SimpleButton; [SWF(width = "465", height = "465", frameRate = "30", backgroundColor = "#000000")] public class Test3 extends Sprite{ private var dodai:DisplayObject3D = new DisplayObject3D(); public function Test3():void { //立方体を生成 var c0:CubeView = new CubeView(); c0.x = -110; c0.y = -110; addChild(c0); var c1:CubeView = new CubeView(); c1.x = 110; c1.y = -110; addChild(c1); var c2:CubeView = new CubeView(); c2.x = 0; c2.y = 65; addChild(c2); //Button var btnX1:SimpleButton = Create.newSimpleButton([5,396,60,20,"X+=30"]); var btnX0:SimpleButton = Create.newSimpleButton([70, 396, 60, 20, "X-=30"]); var btnY1:SimpleButton = Create.newSimpleButton([5,418,60,20,"Y+=30"]); var btnY0:SimpleButton = Create.newSimpleButton([70, 418, 60, 20, "Y-=30"]); var btnZ1:SimpleButton = Create.newSimpleButton([5,440,60,20,"Z+=30"]); var btnZ0:SimpleButton = Create.newSimpleButton([70, 440, 60, 20, "Z-=30"]); var reset:SimpleButton = Create.newSimpleButton([400, 440, 60, 20, "Reset"]); btnX1.name = "X1"; btnX0.name = "X0"; btnY1.name = "Y1"; btnY0.name = "Y0"; btnZ1.name = "Z1"; btnZ0.name = "Z0"; btnX1.addEventListener(MouseEvent.CLICK, CLICK); btnX0.addEventListener(MouseEvent.CLICK, CLICK); btnY1.addEventListener(MouseEvent.CLICK, CLICK); btnY0.addEventListener(MouseEvent.CLICK, CLICK); btnZ1.addEventListener(MouseEvent.CLICK, CLICK); btnZ0.addEventListener(MouseEvent.CLICK, CLICK); reset.addEventListener(MouseEvent.CLICK, resetCLICK); addChild(btnX1); addChild(btnX0); addChild(btnY1); addChild(btnY0); addChild(btnZ1); addChild(btnZ0); addChild(reset); //TextField var tfc0:TextField = Create.newTextField([90, 110, 100, 20,"rotationXYZ",0xFFFFFF],[["selectable",false]]); var tfc1:TextField = Create.newTextField([295, 110, 100, 20,"localRotationXYZ",0xFFFFFF],[["selectable",false]]); var tfc2:TextField = Create.newTextField([200, 290, 100, 20,"Quaternion",0xFFFFFF],[["selectable",false]]); addChild(tfc0); addChild(tfc1); addChild(tfc2); //rotationXYZ var tf0x:TextField = Create.newTextField([5, 5, 120, 20, "rotationX:0", 0xFFFFFF],[["selectable",false]]); var tf0y:TextField = Create.newTextField([5, 25, 120, 20, "rotationY:0", 0xFFFFFF],[["selectable",false]]); var tf0z:TextField = Create.newTextField([5, 45, 120, 20, "rotationZ:0", 0xFFFFFF],[["selectable",false]]); addChild(tf0x) addChild(tf0y) addChild(tf0z) //localRotationXYZ var tf1x:TextField = Create.newTextField([340, 5, 120, 20, "rotationX:0", 0xFFFFFF],[["selectable",false]]); var tf1y:TextField = Create.newTextField([340, 25, 120, 20, "rotationY:0", 0xFFFFFF],[["selectable",false]]); var tf1z:TextField = Create.newTextField([340, 45, 120, 20, "rotationZ:0", 0xFFFFFF],[["selectable",false]]); addChild(tf1x) addChild(tf1y) addChild(tf1z) //Quaternion var tf2x:TextField = Create.newTextField([300, 315, 120, 20, "rotationX:0", 0xFFFFFF],[["selectable",false]]); var tf2y:TextField = Create.newTextField([300, 335, 120, 20, "rotationY:0", 0xFFFFFF],[["selectable",false]]); var tf2z:TextField = Create.newTextField([300, 355, 120, 20, "rotationZ:0", 0xFFFFFF],[["selectable",false]]); addChild(tf2x) addChild(tf2y) addChild(tf2z) function CLICK(e:MouseEvent = null):void { if (count > 0) { return }; var axis:String = e.currentTarget.name.substr(0,1); var d:Number = Number(e.currentTarget.name.substr(1, 1))*60-30; if ("X" == axis) { toRotationXYZ[0] = d; }else if ("Y" == axis) { toRotationXYZ[1] = d; }else if ("Z" == axis) { toRotationXYZ[2] = d; } count = 10; } function resetCLICK(e:MouseEvent):void { c0.reset(); c1.reset(); c2.reset(); count = 1; ENTER_FRAME(e); tf0x.text = tf1x.text = tf2x.text = "rotationX:0"; tf0y.text = tf1y.text = tf2y.text = "rotationY:0"; tf0z.text = tf1z.text = tf2z.text = "rotationZ:0"; } var toRotationXYZ:Array = [0,0,0]; var count:int = 0; addEventListener(Event.ENTER_FRAME, ENTER_FRAME); function ENTER_FRAME(e:Event):void { if (count <= 0) { return }; count --; var _x:Number = toRotationXYZ[0] * 0.1; var _y:Number = toRotationXYZ[1] * 0.1; var _z:Number = toRotationXYZ[2] * 0.1; if (count == 0) { toRotationXYZ[0] = toRotationXYZ[1] = toRotationXYZ[2] = 0; } //左上の立方体に対してはrotationX,Y,Zの値を変更 c0.rotationXYZ(_x, _y, _z); //右上の立方体に対してはlocalRotationX,Y,Zの値を変更 c1.localRotationXYZ(_x, _y, _z); //中央下の立方体に対してはQuaternionでX,Y,Zの値を変更 c2.quaternionXYZ(_x, _y, _z); //回転数表示を更新 tf0x.text = "rX:" + near(c0._cube.rotationX); tf0y.text = "rY:" + near(c0._cube.rotationY); tf0z.text = "rZ:" + near(c0._cube.rotationZ); tf1x.text = "rX:" + near(c1._cube.rotationX); tf1y.text = "rY:" + near(c1._cube.rotationY); tf1z.text = "rZ:" + near(c1._cube.rotationZ); tf2x.text = "rX:" + near(c2._cube.rotationX); tf2y.text = "rY:" + near(c2._cube.rotationY); tf2z.text = "rZ:" + near(c2._cube.rotationZ); } } private function near(num:Number):Number { return Math.round(num*1000000)/ 1000000; } } } import flash.events.Event; import org.papervision3d.lights.PointLight3D; import org.papervision3d.materials.shadematerials.FlatShadeMaterial; import org.papervision3d.objects.primitives.Cube; import org.papervision3d.view.BasicView; import org.papervision3d.materials.utils.MaterialsList; import org.papervision3d.objects.DisplayObject3D; import org.papervision3d.core.math.Quaternion; import org.papervision3d.core.math.Matrix3D; import flash.display.SimpleButton; class CubeView extends BasicView{ public var _cube:DisplayObject3D = GetCubeClass.getCube(200); public function CubeView():void{ scene.addChild(_cube); startRendering(); } public function rotationXYZ(degreeX:Number = 0, degreeY:Number = 0, degreeZ:Number = 0):void { _cube.rotationX += degreeX; _cube.rotationY += degreeY; _cube.rotationZ += degreeZ; } public function localRotationXYZ(degreeX:Number = 0, degreeY:Number = 0, degreeZ:Number = 0):void { _cube.localRotationX -= degreeX; _cube.localRotationY -= degreeY; _cube.localRotationZ -= degreeZ; } public function quaternionXYZ(degreeX:Number = 0,degreeY:Number = 0,degreeZ:Number = 0):void { var pos1:Quaternion = Quaternion.createFromMatrix(_cube.transform); var pos2:Quaternion = Quaternion.createFromEuler(degreeY, degreeZ, degreeX, true); pos2.normalize(); pos2.mult(pos1); pos2.normalize(); _cube.copyTransform(Matrix3D.quaternion2matrix(pos2.x, pos2.y, pos2.z, pos2.w)); } public function reset():void { _cube.copyTransform(new Matrix3D()); _cube.rotationX = _cube.rotationY = _cube.rotationZ = 0; } } class GetCubeClass{ public static function getCube(size:Number = 100):DisplayObject3D { var light:PointLight3D = new PointLight3D(); var colors_array:Array = [ 0x00FF00, 0xFF0000, 0x00FFFF, 0xFF00FF,0x0000FF, 0xFFFF00 ]; var base:DisplayObject3D = new DisplayObject3D(); var ml:MaterialsList = new MaterialsList( { front:new FlatShadeMaterial(light, colors_array[0], 0x333333), back:new FlatShadeMaterial(light, colors_array[1], 0x333333), right:new FlatShadeMaterial(light, colors_array[2], 0x333333), left:new FlatShadeMaterial(light, colors_array[3], 0x333333), top:new FlatShadeMaterial(light, colors_array[4], 0x333333), bottom:new FlatShadeMaterial(light, colors_array[5], 0x333333) }) var _cube:Cube = new Cube(ml, size, size, size); base.addChild(_cube); ml = new MaterialsList( { all:new FlatShadeMaterial(light, colors_array[2], 0x333333)}) _cube = new Cube(ml, size, 10, 10); _cube.x = size; base.addChild(_cube); ml = new MaterialsList( { all:new FlatShadeMaterial(light, colors_array[3], 0x333333)}) _cube = new Cube(ml, size, 10, 10); _cube.x = -size; base.addChild(_cube); ml = new MaterialsList( { all:new FlatShadeMaterial(light, colors_array[0], 0x333333)}) _cube = new Cube(ml, 10, size, 10); _cube.z = size; base.addChild(_cube); ml = new MaterialsList( { all:new FlatShadeMaterial(light, colors_array[1], 0x333333)}) _cube = new Cube(ml, 10, size, 10); _cube.z = -size; base.addChild(_cube); ml = new MaterialsList( { all:new FlatShadeMaterial(light, colors_array[4], 0x333333)}) _cube = new Cube(ml, 10, 10, size); _cube.y = size; base.addChild(_cube); ml = new MaterialsList( { all:new FlatShadeMaterial(light, colors_array[5], 0x333333)}) _cube = new Cube(ml, 10, 10, size); _cube.y = -size; base.addChild(_cube); return base; } } ///////////// import flash.display.DisplayObject; import flash.display.Graphics; import flash.text.TextField; import flash.text.TextFieldType; import flash.text.TextFormat; import flash.display.Sprite; import flash.display.Shape; import flash.display.SimpleButton; //import flash.events.MouseEvent; //import flash.events.Event; //import flash.events.KeyboardEvent; class Create{ public static var defaultTextFormat:TextFormat = new TextFormat(); public static function newSimpleButton(x_y_w_h_txt:Array = null,property:Array=null,graphics:Array=null):SimpleButton{ var upState:Sprite = newSprite([x_y_w_h_txt[0],x_y_w_h_txt[1]],null,[["beginFill",[0xCCCCCC,1]],["drawRoundRect",[0,0,x_y_w_h_txt[2],x_y_w_h_txt[3],8]]]); upState.addChild(newShape([2,2],null,[["beginFill",[0xE5E5E5,1]],["drawRoundRect",[0,0,x_y_w_h_txt[2]-4,x_y_w_h_txt[3]-4,6]]])) var overState:Sprite = newSprite([x_y_w_h_txt[0],x_y_w_h_txt[1]],null,[["beginFill",[0xBBBBBB,1]],["drawRoundRect",[0,0,x_y_w_h_txt[2],x_y_w_h_txt[3],8]]]); overState.addChild(newShape([2,2],null,[["beginFill",[0xEEEEEE,1]],["drawRoundRect",[0,0,x_y_w_h_txt[2]-4,x_y_w_h_txt[3]-4,6]]])) var downState:Sprite = newSprite([x_y_w_h_txt[0],x_y_w_h_txt[1]],null,[["beginFill",[0xAAAAAA,1]],["drawRoundRect",[0,0,x_y_w_h_txt[2],x_y_w_h_txt[3],8]]]); downState.addChild(newShape([2,2],null,[["beginFill",[0xDDDDDD,1]],["drawRoundRect",[0,0,x_y_w_h_txt[2]-4,x_y_w_h_txt[3]-4,6]]])) var hitTestState:Shape = newShape([x_y_w_h_txt[0],x_y_w_h_txt[1]],null,[["beginFill",[0,1]],["drawRoundRect",[0,0,x_y_w_h_txt[2],x_y_w_h_txt[3],8]]]); if(x_y_w_h_txt[4]){ upState.addChild(newTextField([0,2,x_y_w_h_txt[2],x_y_w_h_txt[3]-2],[["defaultTextFormat",new TextFormat("_sans", null, null, null, null, null, null, null, "center")],["text",x_y_w_h_txt[4]]])); overState.addChild(newTextField([0,2,x_y_w_h_txt[2],x_y_w_h_txt[3]-2],[["defaultTextFormat",new TextFormat("_sans", null, null, null, null, null, null, null, "center")],["text",x_y_w_h_txt[4]]])); downState.addChild(newTextField([0,3,x_y_w_h_txt[2],x_y_w_h_txt[3]-3],[["defaultTextFormat",new TextFormat("_sans", null, null, null, null, null, null, null, "center")],["text",x_y_w_h_txt[4]]])); } var sb:SimpleButton = new SimpleButton(upState,overState,downState,hitTestState); return sb; } public static function newShape(x_y_w_h_sh:Array = null,property:Array=null,graphics:Array=null):Shape{ var i:int; var sh:Shape; if(x_y_w_h_sh && x_y_w_h_sh[4]){ sh = x_y_w_h_sh[4]; }else{ sh = new Shape(); } if(x_y_w_h_sh){ if (x_y_w_h_sh[0]) { sh.x = x_y_w_h_sh[0] }; if (x_y_w_h_sh[1]) { sh.y = x_y_w_h_sh[1] }; } if(property){ for (i = 0; i < property.length; i++) { if(property[i] && property[i].length > 1){ sh[property[i][0]] = property[i][1]; } } } if(graphics){ for (i = 0; i < graphics.length; i++) { if(graphics[i] && graphics[i].length > 1){ sh.graphics[graphics[i][0]].apply(null, graphics[i][1]); } } } if(x_y_w_h_sh){ if (x_y_w_h_sh[2]) { sh.width = x_y_w_h_sh[2] }; if (x_y_w_h_sh[3]) { sh.height = x_y_w_h_sh[3] }; } return sh; } public static function newSprite(x_y_w_h_sp:Array = null,property:Array=null,graphics:Array=null,addChild:DisplayObject = null):Sprite{ var i:int; var sp:Sprite; if(x_y_w_h_sp && x_y_w_h_sp[4]){ sp = x_y_w_h_sp[4]; }else{ sp = new Sprite(); } if(x_y_w_h_sp){ if (x_y_w_h_sp[0]) { sp.x = x_y_w_h_sp[0] }; if (x_y_w_h_sp[1]) { sp.y = x_y_w_h_sp[1] }; } if(property){ for (i = 0; i < property.length; i++) { if(property[i] && property[i].length > 1){ sp[property[i][0]] = property[i][1]; } } } if(graphics){ for (i = 0; i < graphics.length; i++) { if(graphics[i] && graphics[i].length > 1){ sp.graphics[graphics[i][0]].apply(null, graphics[i][1]); } } } if(addChild){ sp.addChild(addChild); } if(x_y_w_h_sp){ if (x_y_w_h_sp[2]) { sp.width = x_y_w_h_sp[2] }; if (x_y_w_h_sp[3]) { sp.height = x_y_w_h_sp[3] }; } return sp; } public static function newTextField(x_y_w_h_txt_color_alpha:Array = null,property:Array=null,method:Array=null):TextField{ var i:int; var ta:TextField = new TextField(); ta.defaultTextFormat = defaultTextFormat; if(x_y_w_h_txt_color_alpha){ if (x_y_w_h_txt_color_alpha[0]) { ta.x = x_y_w_h_txt_color_alpha[0] }; if (x_y_w_h_txt_color_alpha[1]) { ta.y = x_y_w_h_txt_color_alpha[1] }; if (x_y_w_h_txt_color_alpha[2]) { ta.width = x_y_w_h_txt_color_alpha[2] }; if (x_y_w_h_txt_color_alpha[3]) { ta.height = x_y_w_h_txt_color_alpha[3] }; if (x_y_w_h_txt_color_alpha[4]) { ta.text = x_y_w_h_txt_color_alpha[4] }; if (x_y_w_h_txt_color_alpha[5]) { ta.textColor = x_y_w_h_txt_color_alpha[5] }; if (x_y_w_h_txt_color_alpha[6]) { ta.alpha = x_y_w_h_txt_color_alpha[6] }; } if(property){ for (i = 0; i < property.length; i++) { if(property[i] && property[i].length > 1){ ta[property[i][0]] = property[i][1]; } } } if(method){ for (i = 0; i < method.length; i++) { if(method[i] && method[i].length > 1){ ta[method[i][0]].apply(null, method[i][1]); } } } return ta; } } ジンバルロックを体験してみよう!(PV3D編)