In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

forked from: バーチャル鷹匠(VirtualHawk)

<バーチャル鷹匠>

別の作品を作るために翼構造の勉強

背景もバーチャルにしようと思っていたけど時間がかかりそうなのでやめ。丁度携帯カメラで撮った良い背景があったので
合成してみた。  この場所は親戚が実際に鷹の訓練をしている場所なのでいい感じになったと思う。鷹をもっとリアルにすべきか。

遊び方:
マウスを動かすと視点変更。
マウスクリックで鷹を呼べます。

気づいて欲しい点:羽の上げ下げの際に羽根先のばらつき方が違う...
ボーンの影響具合を変えて実現しているけど、動いているとわからない。現状だとかなり重くて複数匹表示
できないので、羽根の数やポリゴン数、ボーンなどを省略してもいいと思う。

TODO:直したい->急ターン時の挙動が変。速度低下時のモーションが不自然。
TODO:ヒヨコちゃんやワンコを捕まえてくるようにしようと思ってたけどブラックになりそうなのでやめました。

...
@author TMaeda
/**
 * Copyright fluxus ( http://wonderfl.net/user/fluxus )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/iaM0
 */

// forked from zendenmushi's バーチャル鷹匠(VirtualHawk)
// <バーチャル鷹匠>
//
// 別の作品を作るために翼構造の勉強
//
// 背景もバーチャルにしようと思っていたけど時間がかかりそうなのでやめ。丁度携帯カメラで撮った良い背景があったので
// 合成してみた。  この場所は親戚が実際に鷹の訓練をしている場所なのでいい感じになったと思う。鷹をもっとリアルにすべきか。
//
//
//
// 遊び方:
// マウスを動かすと視点変更。
// マウスクリックで鷹を呼べます。
//
//
// 気づいて欲しい点:羽の上げ下げの際に羽根先のばらつき方が違う...
// ボーンの影響具合を変えて実現しているけど、動いているとわからない。現状だとかなり重くて複数匹表示
// できないので、羽根の数やポリゴン数、ボーンなどを省略してもいいと思う。
//
//
// TODO:直したい->急ターン時の挙動が変。速度低下時のモーションが不自然。
// TODO:ヒヨコちゃんやワンコを捕まえてくるようにしようと思ってたけどブラックになりそうなのでやめました。
package  
{
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.BlendMode;
    import flash.display.Loader;
    import flash.display.MovieClip;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Matrix;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import flash.net.URLRequest;
    import flash.system.LoaderContext;
    import flash.text.TextField;
    import org.papervision3d.cameras.Camera3D;
    import org.papervision3d.core.geom.Lines3D;
    import org.papervision3d.core.geom.renderables.Line3D;
    import org.papervision3d.core.geom.renderables.Vertex3D;
    import org.papervision3d.core.math.NumberUV;
    import org.papervision3d.core.proto.LightObject3D;
    import org.papervision3d.lights.PointLight3D;
    import org.papervision3d.materials.shadematerials.FlatShadeMaterial;
    import org.papervision3d.materials.shaders.FlatShader;
    import org.papervision3d.materials.shaders.ShadedMaterial;
    import org.papervision3d.materials.shaders.ShaderCompositeModes;
    import org.papervision3d.materials.special.LineMaterial;
    import org.papervision3d.materials.WireframeMaterial;
    import org.papervision3d.objects.primitives.Sphere;
    import org.papervision3d.render.BasicRenderEngine;
    import org.papervision3d.scenes.Scene3D;
    import org.papervision3d.view.Viewport3D;
    /**
     * ...
     * @author TMaeda
     */
    [SWF(width = 465, height = 465, backgroundColor = 0, frameRate = 60)]
    public class Winglove extends Sprite
    {
        private var backimage : BitmapData;
        private var backscreen : BitmapData;
        private var backrect : Rectangle = new Rectangle;
        private var frontimage : BitmapData;
        private var txtInfo : TextField;
        // pv3d
        private var scene : Scene3D;
        private var camera : Camera3D;
        private var renderer : BasicRenderEngine;
        private var viewport : Viewport3D;
        private var light :  PointLight3D;
        
        private var zeroPoint : Point = new Point(0, 0);
        private var subPoint : Point = new Point(0, 0);
        
        private var mirrorSpr : Sprite = new Sprite();
        private var mirrorMatrix : Matrix = new Matrix;
        private var sphereHawk : SphereHawk;
        
        private var loaders : Array;
           private const url:Array = [
            "http://assets.wonderfl.net/images/related_images/4/42/429b/429b455c16fcb5c4b9c1f3594d6fb824ac025672"
            //"./HawkBack.png"
            ];
            
        public function Winglove() 
        {
            Wonderfl.capture_delay( 3 );
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }
        
        private function init(e : Event = null) : void 
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);

               loaders = new Array();
            for (var i : int = 0; i < url.length; i++) {
                loaders[i] = new Loader();
                loaders[i].contentLoaderInfo.addEventListener(Event.COMPLETE, loaded);
                loaders[i].load(new URLRequest(url[i]), new LoaderContext(true));
            }
            
        }
        
        private var loaded_count : int = 0;
        private function loaded(e : Event = null) : void
        {
            loaded_count++;
            if (loaded_count < url.length) return;

               addEventListener(Event.ENTER_FRAME, enterFrame);
            stage.addEventListener(MouseEvent.CLICK, mouseClick);

            backimage = Bitmap(loaders[0].content).bitmapData;

            var mc : MovieClip = new MovieClip();
            addChild(mc);
            
            backscreen = new BitmapData(stage.stageWidth, stage.stageHeight);
            var backbmp : Bitmap = new Bitmap(backscreen);
            adjustBackscreen(0, 0);
            mc.addChild(backbmp);
            
            // 背景:
            // 池の直径=50mくらい。 立っている場所から池の水面までの高さ=2m、顔の位置を1.6mくらいに設定。1cm=2.0くらいかな
            // 横465画素の範囲が大体20mくらいと想定すると視野角22度
            
            // pv3d
            scene = new Scene3D();
            camera = new Camera3D();
            camera.x = 0;
            camera.y = 720.0;  // 水面を0.顔までが3.6m
            camera.z = -5000.0; // 池の中心が0.中心から顔までが25m
            camera.ortho = false;
            camera.orthoScale = 0.1;
            camera.fov = 22;
            light = new PointLight3D(false, false); //ライトと太陽の位置は合わせていない。
            light.x = 0;
            light.y = 100;
            light.z = 0.0;
            light.pitch(80);
            
            renderer = new BasicRenderEngine();
            viewport = new Viewport3D(stage.stageWidth, stage.stageHeight, false);
            //mc.addChild(viewport); 
            frontimage = new BitmapData(stage.stageWidth, stage.stageHeight, true, 0);
            
            sphereHawk = new SphereHawk(scene, light);
            
            scene.addChild(light);
            //
            txtInfo = new TextField();
            txtInfo.width = 400;
            mc.addChild(txtInfo);
            
            //camera.target = sphereHawk.sphere;
            sphereHawk.camera = camera;

        }
        
        private function mouseClick(e:MouseEvent):void 
        {
            sphereHawk.call();
        }
        
        private function adjustBackscreen(x : Number, y : Number):void
        {
            var left : Number = (backimage.width - stage.stageWidth) / 2;
            var top : Number = (backimage.height - stage.stageHeight) / 2;
        
            backrect.left = left + x*left;
            backrect.top = top + y*top;
            backrect.width = stage.stageWidth;
            backrect.height = stage.stageHeight;
            
            backscreen.copyPixels( backimage, backrect, zeroPoint);
            
        }
        
        private var camera_yaw : Number = 0;
        private var camera_pitch : Number = 0;
        
        private function enterFrame(e : Event) : void 
        {
            // カメラ操作
            var p : Number = (stage.mouseY / stage.stageHeight) * 2 - 1;
            var y : Number =  (stage.mouseX / stage.stageWidth) * 2 - 1;
            if (y > 1) y = 1; else if (y < -1) y = -1;
            if (p > 1) p = 1; else if (p < -1) p = -1;        
            
            camera_pitch += (p - camera_pitch) / 8;
            camera_yaw += (y - camera_yaw) / 8;
            
            adjustBackscreen(camera_yaw, camera_pitch);
            camera.rotationX = camera_pitch * 5; // 角度は適当に現物あわせ
            camera.rotationY = camera_yaw * 11; // 角度は適当に現物あわせ
            
            sphereHawk.stepFrame();
            
            //txtInfo.text = sphereHawk.debMsg;
            renderer.renderScene( scene, camera, viewport );
            // 解像度を背景にあわせたい&水面処理のために一度BitmapDataにdraw
            frontimage.fillRect(frontimage.rect, 0);
            frontimage.draw(viewport);
            // 水面に写る姿
            var shadow_y : Number = frontimage.height / 2 + sphereHawk.shadow.screen.y;
            var shadow_h : Number = frontimage.height- shadow_y;
            if ((shadow_h > 0) && (shadow_y < stage.stageHeight) ) {
            // 本当はもう少し輝度を落したいけど簡単な方法がわからなかった
                mirrorSpr.graphics.clear();//いつも忘れる
                mirrorMatrix.identity();
                mirrorMatrix.scale(1, -1);
                mirrorMatrix.translate(0, shadow_y-shadow_h);
                mirrorSpr.graphics.beginBitmapFill(frontimage, mirrorMatrix);
                mirrorSpr.graphics.drawRect(0, shadow_y, frontimage.width, shadow_h);
                mirrorSpr.graphics.endFill();
                
                backscreen.draw(mirrorSpr);
                
            }
            // 背景と合成
            backscreen.copyPixels(frontimage, frontimage.rect, zeroPoint, null, null, true);

        }
        
    }

}

import flash.geom.Vector3D;
import org.papervision3d.cameras.Camera3D;
import org.papervision3d.core.animation.curve.Curve3D;
import org.papervision3d.core.geom.Lines3D;
import org.papervision3d.core.geom.renderables.Line3D;
import org.papervision3d.core.geom.renderables.Triangle3D;
import org.papervision3d.core.geom.renderables.Vertex3D;
import org.papervision3d.core.geom.TriangleMesh3D;
import org.papervision3d.core.math.Matrix3D;
import org.papervision3d.core.math.Number2D;
import org.papervision3d.core.math.Number3D;
import org.papervision3d.core.math.Quaternion;
import org.papervision3d.core.proto.MaterialObject3D;
import org.papervision3d.materials.shadematerials.FlatShadeMaterial;
import org.papervision3d.core.proto.LightObject3D;
import org.papervision3d.core.math.NumberUV;
import org.papervision3d.materials.special.LineMaterial;
import org.papervision3d.materials.utils.LightMatrix;
import org.papervision3d.objects.DisplayObject3D;
import org.papervision3d.scenes.Scene3D;
import org.papervision3d.objects.primitives.Sphere;

// 翼の各パーツをPapervision3Dオブジェクトとして生成&簡単なボーンデフォームを行う
class Modelbuilder
{
    public var obj3d : TriangleMesh3D = null;
    public var mirror : Boolean;
    public var matrix : Matrix3D = new Matrix3D();
    public var posm : Matrix3D = new Matrix3D();
    public var worldm : Matrix3D = new Matrix3D();
    public var ownerm : Matrix3D = new Matrix3D();
    
    private var pointstock : Vector.<Number>;
    private var facestock : Vector.<Number>;
    private var weightstock : Vector.<Number>;
    
    private var owner : Modelbuilder;
    private var ownerObjMatrix : Matrix3D;
    private var root : DisplayObject3D;
    
    public function Modelbuilder(root : DisplayObject3D, owner : Modelbuilder)
    {
        this.owner = owner;
        this.root = root;
        resetMatrix();
        mirror = false;
    }
    
    public function connectOwenrMatrix(ownerObjMatrix : Matrix3D) : void
    {
        this.ownerObjMatrix = ownerObjMatrix;
    }
    
    public function build( material : MaterialObject3D, light : LightObject3D, points : Vector.<Number>, weights : Vector.<Number>, faces : Vector.<Number>, mirror : Boolean) : void
    {
        this.pointstock = points;
        this.weightstock = weights;
        this.mirror = mirror;
        
        obj3d = new TriangleMesh3D(material, new Array, new Array);
        
        for (var i : int = 0; i < points.length; i += 3) {
            var vtx : Vertex3D = new Vertex3D( points[i], points[i + 1], points[i + 2]);
            tuneVertex(vtx);
            obj3d.geometry.vertices.push( vtx );
        }
        
        for (var f : int = 0; f < faces.length; f+=3) {
            obj3d.geometry.faces.push(new Triangle3D(obj3d, [ obj3d.geometry.vertices[faces[f + 0]], obj3d.geometry.vertices[faces[f + 1]], obj3d.geometry.vertices[faces[f + 2] ]], null, [new NumberUV(0, 0), new NumberUV(1, 0), new NumberUV(1, 1)])); 
        }
        root.addChild(obj3d);
    }
    
    public function rebuild(w0Mat : Matrix3D = null, w1Mat : Matrix3D = null, subweight : Number = 1.0) : void
    {
        var i : int;
        if (w0Mat) {
            worldm.calculateMultiply(w1Mat, matrix);
            ownerm.calculateMultiply(w0Mat, matrix);
        } else worldMatrix();
        
        for (i = 0; i < obj3d.geometry.vertices.length; i++) {
            var v : Vertex3D = obj3d.geometry.vertices[i];
            v.x = pointstock[i * 3 + 0];// * (mirror? -1:1);
            v.y = pointstock[i * 3 + 1];
            v.z = pointstock[i * 3 + 2];
            tuneVertex(v);
            deform(v, weightstock[i] * subweight);
        }

        for (i = 0; i < obj3d.geometry.faces.length; i++) {
            var t : Triangle3D = obj3d.geometry.faces[i];
            t.createNormal();
        }
        
        rebuildOtherElement();
    }
    
    public function deform(/*inout*/v : Vertex3D, weight : Number) : void
    {
        var n1 : Number3D = v.toNumber3D();
        Matrix3D.multiplyVector(ownerm, n1);
        var n2 : Number3D = v.toNumber3D();
        Matrix3D.multiplyVector(worldm, n2);
        v.x = n1.x*weight + n2.x*(1-weight);
        v.y = n1.y*weight + n2.y*(1-weight);
        v.z = n1.z*weight + n2.z*(1-weight);
    }
    
    public function resetMatrix() : void
    {
        matrix.reset();
        posm.reset();
    }
    
    public function translate(x : Number, y : Number, z : Number) : void
    {
        matrix.n14 = x;
        matrix.n24 = y;
        matrix.n34 = z;
        posm.n14 = x;
        posm.n24 = y;
        posm.n34 = z;
    }
    
    public function rotateX(deg : Number) : void 
    {
        matrix.calculateMultiply(matrix, Matrix3D.rotationX(deg*Math.PI/180));
    }
    
    public function rotateY(deg : Number) : void 
    {
        matrix.calculateMultiply(matrix, Matrix3D.rotationY(deg*Math.PI/180));
    }
    
    public function rotateZ(deg : Number) : void 
    {
        matrix.calculateMultiply(matrix, Matrix3D.rotationZ(deg*Math.PI/180));
    }
    
    private function worldMatrix() : Matrix3D
    {
        if (owner) ownerm.copy(owner.worldMatrix());
        else if (ownerObjMatrix) ownerm.copy(ownerObjMatrix);
        else ownerm.reset();
        
        worldm.calculateMultiply(ownerm, matrix);
        ownerm.calculateMultiply(ownerm, posm);
        
        return worldm;
    }
    
    /*override*/
    protected function tuneVertex(/*inout*/ point : Vertex3D) : void
    {
        point.x = (mirror? -1:1) * point.x;
    }
    
    protected function rebuildOtherElement() : void
    {
        
    }
    
}

// 羽根
class Penna extends Modelbuilder
{
    private var left : Boolean;
    private var scale_x : Number;
    private var scale_y : Number;
    
    private var points : Vector.<Number> = Vector.<Number>([ 0,0,0, 10,0,0, 20,0,0, 30,0,0, 25,-1,-3, 20,-1,-4, 10,-1,-3, 0,-1,-1]);
    private var points2 : Vector.<Number> = Vector.<Number>([ 0, 0, 0, 10, 0, 0, 20, 0, 0, 25, 0, 0, 30, -1, -2, 25, -1, -5, 10, -1, -4, 0, -1, -2]);
    private var weights : Vector.<Number> = Vector.<Number>([ 1, 1, 1, 1, 1,1,1,1]);
    private var faces : Vector.<Number> = Vector.<Number>([ 0, 1, 6, 0, 6, 7,  1, 2, 5, 1, 5, 6,  2, 3, 4,  2, 4, 5 ]);
    
    private var sharp : Boolean;

    public function Penna(root : DisplayObject3D, light : LightObject3D, ownerModel : Modelbuilder, left : Boolean, sharp : Boolean, len : Number, dy : Number = 0)
    {
        super(root, ownerModel);
        
        this.sharp = sharp;
        this.left = left;
        this.scale_x = len;
        this.scale_y = dy;
        
        var p : Vector.<Number>;
        if (sharp) p = points; else p = points2;
        var flat : FlatShadeMaterial = new FlatShadeMaterial(light, 0xffffff, 0x404080, 0);
        flat.doubleSided = true;
        
        build(flat, light, p, weights, faces, left);
        
    }
    
    protected override function tuneVertex( /*inout*/ point : Vertex3D ) : void
    {
        var xx : Number = point.x;
        point.x = (left? -1:1) * point.x * scale_x / 30;
        point.y = point.y + xx * scale_y / 30;
    }
    
}

// 初列
class WingPrimaries extends Modelbuilder
{
    private var points : Vector.<Number> = Vector.<Number>([0,0,0, 2,0,0, 4,0,0, 6,0,0, 8,0,0, 10,0,0,  8,1,-3, 6,1,-4, 4,1,-5, 2,1,-5, 0,1,-5]);
    private var weights : Vector.<Number> = Vector.<Number>([1, 0.8, 0.6, 0.4, 0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0]);
    private var faces : Vector.<Number> = Vector.<Number>([ 0, 1, 10, 1,9,10, 1,2,9, 2,8,9, 2,3,8, 3,7,8, 3,4,7, 4,6,7, 4,5,6 ]);
    private var pennas : Vector.<Penna> = new Vector.<Penna>;
    public var deforming : Number = 0;
    
    public function WingPrimaries(root : DisplayObject3D, light : LightObject3D, ownerModel : Modelbuilder, left : Boolean)
    {
        super(root, ownerModel);

        var flat : FlatShadeMaterial = new FlatShadeMaterial(light, 0xf0f0f0, 0x404080, 0);
        flat.doubleSided = true;
        
        build(flat, light, points, weights, faces, left);

        var alula : Penna = new Penna(root, light, this, left, true, 10);
        pennas.push(alula);

        for (var i : int = 0; i < 8; i++) {
            var penna : Penna = new Penna(root, light, this, left, true, 50 - i * 2);
            pennas.push(penna);
            
        }
    }
    
    
    protected override function rebuildOtherElement() : void
    {
        var cnt : int = pennas.length;
        for (var i : int = 0; i < cnt; i++) {
            var penna : Penna = pennas[i];
            var v : Vertex3D = new Vertex3D( (10 - i * 1) * (mirror ? -1:1), 0, 0);
            penna.resetMatrix();
            if (i == 0) {
                penna.translate(10 * (mirror ? -1:1), 0, 0);
                penna.rotateY(-20 * (mirror ? 1: -1));
            } else {
                penna.translate((10 - i * 1) * (mirror ? -1:1), 0, 0);
                penna.rotateY(i * 10 * (mirror ? 1: -1));
            }
            var w : Number = (cnt - i) / cnt;
            w = w * deforming + 1 * (1 - deforming);
            penna.rebuild(worldm, ownerm, w);
            
        }
        
    }
}

// 次列
class WingSecondaries extends Modelbuilder
{
    private var left : Boolean;
    private var points : Vector.<Number> = Vector.<Number>([0,0,0, 4,0,0.4, 8,0,0.8, 12,0,1.2, 16,0,1.6, 20,0,2,  20,1,-3, 16,1,-4, 12,1,-8, 8,1,-15, 4,1,-16, 0,1,-16]);
    private var weights : Vector.<Number> = Vector.<Number>([ 1, 0.8, 0.6, 0.4, 0.2, 0, 0, 0.2, 0.4, 0.6, 0.8, 1.0 ]);
    private var faces : Vector.<Number> = Vector.<Number>([ 0,1,11, 1,10,11, 1,2,10, 2,9,10, 2,3,9, 3,8,9, 3,4,8, 4,7,8, 4,5,7, 5,6,7 ]);
    private var pennas : Vector.<Penna> = new Vector.<Penna>;
    private var covers : Vector.<Penna> = new Vector.<Penna>;
    
    public function WingSecondaries(root : DisplayObject3D, light : LightObject3D, ownerModel : Modelbuilder, left : Boolean)
    {
        var i : int;
        super(root, ownerModel);
        
        this.left = left;
        var flat : FlatShadeMaterial = new FlatShadeMaterial(light, 0xf0f0f0, 0x404080, 0);
        flat.doubleSided = true;
        build(flat, light, points, weights, faces, left);

        //雨覆
        for (i = 0; i < 5; i++) {
            var cover : Penna = new Penna(root,  light, this, left, false, 30 - i * 3, 1);
            covers.push(cover);
        }
        // 風切羽根
        for (i = 0; i < 7; i++) {
            var penna : Penna = new Penna(root, light, this, left, false, 35-i*2);
            pennas.push(penna);
        }
    }

    protected override function rebuildOtherElement() : void
    {
        var i : int;
        var cnt : int = pennas.length;
        var penna : Penna, v : Vertex3D;
        
        for (i = 0; i < cnt; i++) {
            penna = pennas[i];
            v = new Vertex3D( (10 - i * 1) * (mirror ? -1:1), 0, 0);
            penna.resetMatrix();
            penna.translate((20 - i * 3) * (mirror ? -1:1), 0, 2-i*2);
            penna.rotateY((82+i * 1)*(mirror ? 1:-1));
            penna.rebuild(worldm, ownerm, (cnt - i) / cnt);
        }
        
        cnt = covers.length;
        for (i = 0; i < cnt; i++) {
            penna = covers[i];
            v = new Vertex3D( (10 - i * 1) * (mirror ? -1:1), 0, 0);
            penna.resetMatrix();
            penna.translate((20 - i * 3) * (mirror ? -1:1), 0, 2-i*2);
            penna.rotateY((82+i * 1)*(mirror ? 1:-1));
            penna.rotateX(5);
            penna.rebuild(worldm, ownerm, (cnt - i) / cnt);
        }
        
    }

}
// 三列
class WingTartials extends Modelbuilder
{
    //public var obj3d : TriangleMesh3D;
    private var left : Boolean;
    private var points : Vector.<Number> = Vector.<Number>([0,0,0, 5,0,-1, 10,0,-2, 10,1,-18, 3,1,-19, -4,1,-20]);
    private var weights : Vector.<Number> = Vector.<Number>([ 1,0.5,0, 0,0.5,1 ]);
    private var faces : Vector.<Number> = Vector.<Number>([ 0,1,5, 1,4,5, 1,2,4, 2,3,4 ]);
    private var pennas : Vector.<Penna> = new Vector.<Penna>;
    
    public function WingTartials(root : DisplayObject3D, light : LightObject3D, ownerModel : Modelbuilder, left : Boolean)
    {
        var i : int;
        super(root, ownerModel);
        
        this.left = left;
        
        var flat : FlatShadeMaterial = new FlatShadeMaterial(light, 0xf0f0f0, 0x404080, 0);
        flat.doubleSided = true;
        build(flat, light, points, weights, faces, left);

        for (i = 0; i < 5; i++) {
            var penna : Penna = new Penna(root, light, this, left, false, 25 - i * 2);
            pennas.push(penna);
        }
    }

    protected override function rebuildOtherElement() : void
    {
        var cnt : int = pennas.length;
        for (var i : int = 0; i < cnt; i++) {
            var penna : Penna = pennas[i];
            var v : Vertex3D = new Vertex3D( (10 - i * 1) * (mirror ? -1:1), 0, 0);
            penna.resetMatrix();
            penna.translate((10 - i * 3) * (mirror ? -1:1), 0, -14-i*2);
            penna.rotateY((88+i * 1)*(mirror ? 1:-1));
            penna.rebuild(worldm, ownerm, (cnt - i) / cnt);
            
        }
        
    }
}

// 翼
class Wing
{
    public static const WS_FLAPPED : int = 0;
    public static const WS_HOVER : int = 1;
    public static const WS_ATTACK : int = 2;
    public static const WS_STOPPING : int = 3;
    public static const WS_END : int = WS_STOPPING;
    
    public var primaries : WingPrimaries;
    private var secondaries : WingSecondaries;
    private var tartials : WingTartials;

    public var root : DisplayObject3D = new DisplayObject3D();
    private var flat : FlatShadeMaterial;

    private var points : Vector.<Number> = Vector.<Number>([ 10, 0, 0,  20, -2, 0,  40, 2, 0,  55, -2, 0,  42, -4,0,  20,-16,0,  8,-20,0]);
    private var faces : Vector.<Number> = Vector.<Number>([ 0, 1, 5, 0, 5, 6, 1, 2, 4, 1, 4, 5, 2, 3, 4 ]);
    
    private var left : Boolean;
    
    public var state : int = WS_FLAPPED;
    
    public function Wing(light : LightObject3D, left : Boolean)
    {
        flat = new FlatShadeMaterial(light, 0xf0f0f0, 0x404080, 0);
        flat.interactive = true;
        this.left = left;
        flat.doubleSided = true;

        tartials = new WingTartials(root, light, null,  left);
        secondaries = new WingSecondaries(root, light, tartials, left);
        primaries = new WingPrimaries(root, light, secondaries, left);
    }
    public var deg : Number = 0;
    private var cr_prim_xangle : Number = 0, cr_second_xangle : Number = 0, cr_tarti_xangle : Number = 0;
    private var cr_prim_yangle : Number = 0, cr_second_yangle : Number = 0, cr_tarti_yangle : Number = 0;
    private var cr_prim_zangle : Number = 0, cr_second_zangle : Number = 0, cr_tarti_zangle : Number = 0;
    
    public function stepFrame(base : Matrix3D) : void
    {
        var prim_xangle : Number, second_xangle : Number, tarti_xangle : Number;
        var prim_yangle : Number, second_yangle : Number, tarti_yangle : Number;
        var prim_zangle : Number, second_zangle : Number, tarti_zangle : Number;
        
        deg += 10;
        if (deg > 360) {
            deg -= 360;
        }
        tartials.connectOwenrMatrix(base);
        
        switch (state) {
            case WS_FLAPPED:
            {
                primaries.deforming = deg > 240 ? (deg-240)/120  : deg < 120 ? (120-deg)/120 : 0 ;
                prim_zangle =  ( -40 + Math.sin((deg - 70) * Math.PI / 180) * 40) * (this.left? -1:1);
                prim_xangle = 0;
                prim_yangle = 0;
                second_zangle = Math.sin(deg * Math.PI / 180) * 30 * (this.left? -1:1);
                second_xangle = 0;
                second_yangle = 0;
                tarti_zangle = ( 25 + Math.sin(deg * Math.PI / 180) * 50) * (this.left? -1:1);
                tarti_xangle = 0;
                tarti_yangle = 0;
                break;
            }
            case WS_HOVER:
            {
                prim_zangle = -10* (this.left? -1:1);;
                prim_xangle = 0;
                prim_yangle = 0;
                second_zangle = 0;
                second_xangle = 0;
                second_yangle = 0;
                tarti_zangle = 10* (this.left? -1:1);
                tarti_xangle = 0;
                tarti_yangle = 0;
                break;
            }
            case WS_ATTACK:
            {
                primaries.deforming = 1.0;
                prim_zangle = 0;
                prim_xangle = 0;
                prim_yangle = -90* (this.left? -1:1);
                second_zangle = 45* (this.left? -1:1);
                second_xangle = 0;
                second_yangle = 100* (this.left? -1:1);
                tarti_zangle = 0;// -40 * (this.left? -1:1);
                tarti_xangle = 0;
                tarti_yangle = -40* (this.left? -1:1);
                break;
            }
            case WS_STOPPING:
            {
                primaries.deforming = 1.0;
                prim_zangle =  ( -40 + Math.sin((deg - 70)*2 * Math.PI / 180) * 40) * (this.left? -1:1);
                prim_xangle = -45;
                prim_yangle = 0;
                second_zangle = Math.sin(deg*2 * Math.PI / 180) * 60 * (this.left? -1:1);
                second_xangle = -45;//  * (this.left? -1:1);
                second_yangle = 0;
                tarti_zangle = ( 25 + Math.sin(deg*2 * Math.PI / 180) * 50) * (this.left? -1:1);
                tarti_xangle = -60;
                tarti_yangle = 0;
                break;
            }
        }
        
        cr_prim_xangle += (prim_xangle - cr_prim_xangle) / 8.0;
        cr_prim_yangle += (prim_yangle - cr_prim_yangle) / 8.0;
        cr_prim_zangle += (prim_zangle - cr_prim_zangle) / 8.0;

        cr_second_xangle += (second_xangle - cr_second_xangle) / 4.0;
        cr_second_yangle += (second_yangle - cr_second_yangle) / 4.0;
        cr_second_zangle += (second_zangle - cr_second_zangle) / 4.0;

        cr_tarti_xangle += (tarti_xangle - cr_tarti_xangle) / 4.0;
        cr_tarti_yangle += (tarti_yangle - cr_tarti_yangle) / 4.0;
        cr_tarti_zangle += (tarti_zangle - cr_tarti_zangle) / 4.0;

        primaries.resetMatrix();
        primaries.rotateZ(cr_prim_zangle);
        primaries.rotateY(cr_prim_yangle);
        primaries.rotateX(cr_prim_xangle);
        primaries.translate(20 * (left ? -1:1), 0, 2);
        secondaries.resetMatrix();
        secondaries.rotateZ(cr_second_zangle);
        secondaries.rotateY(cr_second_yangle);
        secondaries.rotateX(cr_second_xangle);
        secondaries.translate(10 * (left ? -1:1), 0, -2);
        tartials.resetMatrix();
        tartials.rotateZ(cr_tarti_zangle);
        tartials.rotateY(cr_tarti_yangle);
        tartials.rotateX(cr_tarti_xangle);
        tartials.translate(10 * (left ? -1:1), 0, -2);
        
        tartials.rebuild();
        secondaries.rebuild();
        primaries.rebuild();
        
    }

}

function distance(v0 : Number3D, v1 : Number3D) : Number
{
    var x : Number , y : Number, z : Number;

    if (v1) {
        x = v0.x - v1.x;
        y = v0.y - v1.y;
        z = v0.z - v1.z;
    } else {
        x = v0.x;
        y = v0.y;
        z = v0.z;
    }
    
    return Math.sqrt(x * x + y * y +z * z);
}

function dotProduct(v0 : Number3D, v1 : Number3D) : Number
{
    return  v0.x * v1.x + v0.y * v1.y + v0.z * v1.z;
}

function unitVec(v0 : Number3D) : Number3D
{
    var l : Number = distance(v0, null);
    v0.x /= l;
    v0.y /= l;
    v0.z /= l;
    
    return v0;
}

function crossProduct(v0 : Number3D, v1 : Number3D, vo : Number3D) : void
{
    var x : Number = v0.y * v1.z - v0.z * v1.y;
    var y : Number = v0.z * v1.x - v0.x * v1.z;
    var z : Number = v0.x * v1.y - v0.y * v1.x;
    
    vo.x = x;
    vo.y = y;
    vo.z = z;
}

class SphereHawk
{
    public  var sphere : Sphere;
    public  var shadow : DisplayObject3D;
    private var wing_l : Wing;
    private var wing_r : Wing;
    
    private var speed : Number = 500;
    private var half  : Number = speed / 2;
    
    private var lines : Lines3D;
        
    private var t : Number;
    private var d : Number3D = new Number3D;
    private var start  : Number3D = new Number3D;
    private var pos : Number3D = new Number3D;
    private var prim_seg : Number3D = new Number3D;
    private var second_seg : Number3D = new Number3D;
    private var dir_quat : Quaternion = new Quaternion;
    private var tmp_quat : Quaternion = new Quaternion;
    private var tmp2_quat : Quaternion = new Quaternion;
    private var tmp_mtx : Matrix3D = new Matrix3D;
    
    private var p0  : Number3D = new Number3D;
    private var p1  : Number3D = new Number3D;
    private var p2  : Number3D = new Number3D;
    
    private var temp0 : Number3D = new Number3D;
    private var temp1 : Number3D = new Number3D;
    private var temp2 : Number3D = new Number3D;
    
    private var dir_h : Number; // 水平方向の向き
    private var dir_v : Number; // 垂直方向の向き
    private var dir_r : Number; // ロール
    
    private var descent : Boolean = false;
    private var force_flapped : Boolean = false;
    private var callFlg : Boolean = false;
    private var slow : int  = 0;
    
    public var debMsg : String;
    
    private var cr_pitch : Number;
    private var cr_yaw : Number;
    private var cr_roll : Number;
    
    private var sphere_axis_x : Number3D = new Number3D;
    private var sphere_axis_y : Number3D = new Number3D;
    private var sphere_axis_z : Number3D = new Number3D;
    
    public var camera : Camera3D;
        
    public function SphereHawk(scene:Scene3D, light : LightObject3D)
    {
        sphere = new Sphere(new FlatShadeMaterial(light, 0xf0f0f0, 0x404080), 15, 8, 2);
        shadow = new DisplayObject3D();
        shadow.autoCalcScreenCoords = true;
        scene.addChild(shadow);
        
        scene.addChild(sphere);
        wing_l = new Wing(light, true);
        scene.addChild(wing_l.root);
        wing_r = new Wing(light, false);
        scene.addChild(wing_r.root);
        
        t = -1.0;
        start.x = -400;
        start.y = 600;
        start.z = -5000;
        dir_h = 45*Math.PI/180;
        dir_v = 0.2;
        dir_r = 0;
        prim_seg.x = Math.sin( dir_h) * 1;
        prim_seg.z = Math.cos( dir_h) * 1;
        prim_seg.y = Math.sin( dir_v) * 1;
        second_seg.x = Math.sin( dir_h) * 1;
        second_seg.z = Math.cos( dir_h) * 1;
        second_seg.y = Math.sin( dir_v-0.2) * 1;
        
        sphere.x = start.x;
        sphere.y = start.y;
        sphere.z = start.z;
        shadow.x = sphere.x;
        shadow.y = 0;
        shadow.z = sphere.z;
        
        cr_pitch = dir_v;
        cr_yaw = dir_h;
        cr_roll = dir_r;
        
    }
    // 呼ばれ中
    private function calling() : void
    {
        temp0.x = start.x + prim_seg.x * speed;
        temp0.y = start.y + prim_seg.y * speed;
        temp0.z = start.z + prim_seg.z * speed;
        
        temp1.x = temp0.x - camera.x;
        temp1.y = temp0.y - (camera.y-100);
        temp1.z = temp0.z - (camera.z+500);

        var dist : Number = Math.sqrt(temp1.x * temp1.x + temp1.z * temp1.z);
        temp0.x = camera.x - temp0.x;
        temp0.y = (camera.y-100) - temp0.y;
        temp0.z = (camera.z + 500) - temp0.z;
        temp2.x = temp0.x;
        temp2.y = temp0.y;
        temp2.z = temp0.z;
        unitVec(temp0);
        temp1.x = prim_seg.x;
        temp1.y = prim_seg.y;
        temp1.z = prim_seg.z;
        unitVec(temp1);
        var a: Number = Math.acos(dotProduct(temp0, temp1));
        if (a > Math.PI / 2) slow = 60 * 2; // 呼ばれたときにターン角度が急の場合は一時的に速度を落とす
        /*
        if (a > Math.PI * 0.9) { // 更に急な場合は少し角度をずらす
            temp0.x += 8;
            unitVec(temp0);
        }
        */
        
        if (dist < speed) {
            callFlg = false;
            slow = 60 * 4;
            second_seg.x = temp2.x/speed
            second_seg.y = temp2.y/speed;
            second_seg.z = temp2.z/speed;
        } else {
            second_seg.x = temp0.x;
            second_seg.y = temp0.y;
            second_seg.z = temp0.z;
        }
        
    }
    private function nextPath() : void
    {
        var ah : Number = (Math.random()*10 - 5)*Math.PI/180;
        var av : Number = (Math.random()*40 - 10 )*Math.PI / 180;
        
        temp1.x = sphere.x - 0;
        temp1.y = sphere.y - 0;
        temp1.z = sphere.z - (-1000);
        
        var dist : Number = distance(temp1, null);//Math.sqrt(temp1.x * temp1.x + temp1.z * temp1.z);
        if (dist > 2000) { // 0,0,-1000から水平方向に2000以上離れたら0,0,-1000方向に軌道修正
            temp0.copyFrom(d);
            unitVec(temp0);
            unitVec(temp1);
            
            crossProduct(temp0, temp1, temp0);            
            
            ah = (temp0.y > 0 ? -1:1) * Math.random() * Math.PI / 2;
            dir_r =  (temp0.y > 0 ? 1: -1) * Math.PI / 4; // rollする
            force_flapped = true;
        } else {
            force_flapped = false;
            dir_r = 0;
        }
        if (descent || ((sphere.y > 1200) && (av > 0))) {// 上昇しすぎたら急降下
            descent = true;
            av = -Math.PI / 2;
        }
        second_seg.x = Math.sin(dir_h + ah);
        second_seg.z = Math.cos(dir_h + ah);
        second_seg.y = Math.sin(dir_v + av);
        
    }
    
    // モーションパスから移動量算出。 姿勢制御も行う
    private function computeD() : void
    {
        // tが1になったら次のパスを生成
        if (t >= 1.0) {
            t -= 1.0;
            // パス決定用セグメントをシフト
            start.x = start.x + prim_seg.x * speed;
            start.y = start.y + prim_seg.y * speed;
            start.z = start.z + prim_seg.z * speed;
            prim_seg.x = second_seg.x;
            prim_seg.y = second_seg.y;
            prim_seg.z = second_seg.z;

            // 新しいパスを生成
            if (callFlg) {
                calling();
            } else {
                nextPath();
            }
            
            // 水面につっこみそうになる場合は向きを↑に反転
            if (second_seg.y * speed + sphere.y < 100) {
                descent = false;
                second_seg.y *= -1;
            }
            
        }
        // 2直線で指定されるモーションパス上の位置を計算。tは0から1.0まで。ただし初期は-1から始まる。(制御点は各直線の真ん中と、ジョイント部の3点。2次ベジェ)
        if (t < 0) {
            pos.x = start.x + (prim_seg.x * half)*(1+t);
            pos.y = start.y + (prim_seg.y * half)*(1+t);
            pos.z = start.z + (prim_seg.z * half)*(1+t);
        } else {
            p0.x = start.x + prim_seg.x * half;
            p0.y = start.y + prim_seg.y * half;
            p0.z = start.z + prim_seg.z * half;
            
            p1.x = start.x + prim_seg.x * speed;
            p1.y = start.y + prim_seg.y * speed;
            p1.z = start.z + prim_seg.z * speed;
            
            p2.x = p1.x + second_seg.x * half;
            p2.y = p1.y + second_seg.y * half;
            p2.z = p1.z + second_seg.z * half;
            
            
            pos.x = (1 - t) * (1 - t) * p0.x + 2 * t * (1 - t) * p1.x + t * t * p2.x;
            pos.y = (1 - t) * (1 - t) * p0.y + 2 * t * (1 - t) * p1.y + t * t * p2.y;
            pos.z = (1 - t) * (1 - t) * p0.z + 2 * t * (1 - t) * p1.z + t * t * p2.z;
        }
        d.x = pos.x - sphere.x;
        d.y = pos.y - sphere.y;
        d.z = pos.z - sphere.z;
        
        // 姿勢制御 別件バグで試行錯誤したので無駄が多い。
        // yaw方向決定
        dir_h = Math.atan2(d.x, d.z);
        cr_yaw = dir_h;
        dir_quat.setFromAxisAngle(0, 1, 0, dir_h);
        tmp_mtx.copy3x3(dir_quat.matrix);
        // yaw方向回転後の姿勢ベクトル。 
        // matrixから基底ベクトル成分を抜きだすようにするべき。修正はおいおいやる
        sphere_axis_x.x = 1.0;    sphere_axis_x.y = 0.0;    sphere_axis_x.z = 0.0;
        sphere_axis_y.x = 0.0;    sphere_axis_y.y = 1.0;    sphere_axis_y.z = 0.0;
        sphere_axis_z.x = 0.0;    sphere_axis_z.y = 0.0;    sphere_axis_z.z = 1.0;
        Matrix3D.multiplyVector(tmp_mtx, sphere_axis_x);
        Matrix3D.multiplyVector(tmp_mtx, sphere_axis_y);
        Matrix3D.multiplyVector(tmp_mtx, sphere_axis_z);
        // pitch方向決定。 もっとシンプルにできる
        var l : Number = Math.sqrt(d.x * d.x + d.y * d.y + d.z * d.z);
        if (l == 0) {
            l = 1;
        }
        var a : Number = Math.acos(sphere_axis_z.x * d.x / l + sphere_axis_z.y * d.y / l + sphere_axis_z.z * d.z / l);
        var nv : Number3D = new Number3D();
        var di : Number3D = unitVec(d.clone());
        
        crossProduct( di, unitVec(sphere_axis_z) , nv);
        var b : Number = dotProduct( sphere_axis_x, unitVec(nv));
        if (b > 0) a = -a;
        
        dir_v = a;
        cr_pitch += (dir_v - cr_pitch) / 16;
        
        tmp_quat.setFromAxisAngle(sphere_axis_x.x,sphere_axis_x.y, sphere_axis_x.z, cr_pitch);
        Matrix3D.multiplyVector(tmp_quat.matrix, sphere_axis_z); // 新しい姿勢(進行方向)ベクトルの算出
        tmp2_quat.x = dir_quat.x;
        tmp2_quat.y = dir_quat.y;
        tmp2_quat.z = dir_quat.z;
        tmp2_quat.w = dir_quat.w;
        dir_quat.calculateMultiply(tmp_quat,tmp2_quat); // dir_quat.calculateMultiply(tmp_quat,dir_quat)と書きたいけど無理
        //sphere.transform.copy3x3(dir_quat.matrix); // debug
        
        // roll方向決定。目標角度はnextPath()内で決定している。
        cr_roll += (dir_r - cr_roll) / 100;

        tmp_quat.setFromAxisAngle(sphere_axis_z.x,sphere_axis_z.y, sphere_axis_z.z, cr_roll);
        tmp2_quat.x = dir_quat.x;
        tmp2_quat.y = dir_quat.y;
        tmp2_quat.z = dir_quat.z;
        tmp2_quat.w = dir_quat.w;
        dir_quat.calculateMultiply(tmp_quat,tmp2_quat);
        sphere.transform.copy3x3(dir_quat.matrix);

        // 水面位置。影表示用のつもりだったが手が回らなかったのでscreen座標を利用した水面反射用の座標計算にのみ利用
        shadow.x = sphere.x;
        shadow.y = 0;
        shadow.z = sphere.z;


    }
    
    // 呼ばれた
    public function call() : void
    {
        callFlg = true;
    }
    
    public function stepFrame() : void
    {
        computeD();
        if (slow > 0) {
            t += 0.01
            slow--;
        } else {
            t += 0.02;
        }
        
        sphere.x += d.x;
        sphere.y += d.y;
        sphere.z += d.z;

        debMsg = sphere.z.toString();
        
        var dist : Number = distance(d, null);
        // リアリティーを出したいとき用に左右の翼で別々のステートを設定できるようにしているが、今のところ同じステートにしている
        if (dist < 6) {
            wing_l.state = Wing.WS_STOPPING;
            wing_r.state = Wing.WS_STOPPING;
        } else if ((d.y > 0) || force_flapped || callFlg) {
            wing_l.state = Wing.WS_FLAPPED;
            wing_r.state = Wing.WS_FLAPPED;
        } else {
            if (d.y < -4 ) {
                wing_l.state = Wing.WS_ATTACK; // 急降下
                wing_r.state = Wing.WS_ATTACK;
            } else {
                wing_l.state = Wing.WS_HOVER;
                wing_r.state = Wing.WS_HOVER;
            }
        }
        
        wing_l.stepFrame(sphere.transform);
        wing_r.stepFrame(sphere.transform);    
        
    }
    
}