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

MeshUtility

/**
 * Copyright narutohyper ( http://wonderfl.net/user/narutohyper )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/bIia
 */

package  {
    import alternativa.engine3d.alternativa3d;
    import alternativa.engine3d.materials.TextureMaterial;
    import alternativa.engine3d.materials.VertexLightTextureMaterial;
    import alternativa.engine3d.materials.StandardMaterial;
    import alternativa.engine3d.objects.WireFrame;
    import alternativa.engine3d.objects.Mesh
    import alternativa.engine3d.primitives.GeoSphere;
    import alternativa.engine3d.resources.BitmapTextureResource;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.events.Event;
    
    
    use namespace alternativa3d;    
    
    public class main extends Sprite {
        private var scene:Template
        private const RADIAN:Number = Math.PI / 180;
        
        public function main():void {
            scene = new Template();
            scene.addEventListener(Template.VIEW_CREATE, initialize);
            addChild(scene);
        }
        
        private function initialize(event:Event):void {
            scene.removeEventListener(Template.VIEW_CREATE, initialize);
            //マテリアル用のリソースの用意
            var bmd:BitmapData = new BitmapData(256, 256, true, 0xFF000000);
            bmd.perlinNoise(64, 64, 1, 1, true , true);
            var textureResource:BitmapTextureResource = new BitmapTextureResource(bmd);

            
            //Textureの作成
            var diffuseMap:BitmapData = new BitmapData(16, 16, false, 0x6666FF);
            var normalMap:BitmapData = new BitmapData(16, 16, false, 0x8080FF);

            //マテリアルの作成
            var bitmapResource:BitmapTextureResource = new BitmapTextureResource(diffuseMap);
            var normalResource:BitmapTextureResource = new BitmapTextureResource(normalMap);
            var material:StandardMaterial = new StandardMaterial(bitmapResource, normalResource);
            //var material:TextureMaterial = new TextureMaterial(bitmapResource);
            
            var meshA:Mesh = new Cylinder(50,30, 100, 12,1,0,0,false);
            //MeshUtility.createTangent(meshA);
            meshA.geometry.calculateTangents(0);
            meshA.setMaterialToAllSurfaces(material)
            scene.controlObject.addChild(meshA);
            
            var meshB:Mesh = new Cylinder(50,30, 100, 12,1,0,0,false);
            MeshUtility.smoothShading(meshB, true);
            //MeshUtility.createTangent(meshB);
            meshB.geometry.calculateTangents(0);

            
            meshB.setMaterialToAllSurfaces(material)
            scene.controlObject.addChild(meshB);
            /*
            var normalFrameA:WireFrame = WireFrame.createNormals(meshA, 0xFF0000, 1, 1, 10);
            var tangentFrameA:WireFrame = WireFrame.createTangents(meshA, 0x0000FF, 1, 1, 10);
            var binormalFrameA:WireFrame = WireFrame.createBinormals(meshA, 0x00FF00, 1, 1, 10);
            //var wireFrameA:WireFrame = WireFrame.createEdges(meshA, 0xFF0000);
            meshA.addChild(normalFrameA);
            meshA.addChild(tangentFrameA);
            meshA.addChild(binormalFrameA);
            var normalFrameB:WireFrame = WireFrame.createNormals(meshB, 0xFF0000, 1, 1, 10);
            var tangentFrameB:WireFrame = WireFrame.createTangents(meshB, 0x0000FF, 1, 1, 10);
            var binormalFrameB:WireFrame = WireFrame.createBinormals(meshB, 0x00FF00, 1, 1, 10);
            //var wireFrameB:WireFrame = WireFrame.createEdges(meshB, 0xFF0000);
            meshB.addChild(normalFrameB);
            meshB.addChild(tangentFrameB);
            meshB.addChild(binormalFrameB);
            */
            meshA.x =-80
            meshB.x = 80
                      
            scene.initialize();
        }
    }
}    
import alternativa.engine3d.alternativa3d;
import alternativa.engine3d.primitives.GeoSphere;
import alternativa.engine3d.controllers.SimpleObjectController;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Resource;
import alternativa.engine3d.core.View;
import alternativa.engine3d.core.VertexAttributes;
import alternativa.engine3d.core.Transform3D;

import alternativa.engine3d.objects.Mesh;
import alternativa.engine3d.objects.Surface;
import alternativa.engine3d.objects.Joint;
import alternativa.engine3d.lights.AmbientLight;
import alternativa.engine3d.lights.DirectionalLight;
import alternativa.engine3d.resources.Geometry;
import flash.display.Sprite;
import flash.display.Stage3D;
import flash.display.StageAlign;
import flash.display.StageQuality;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.utils.Dictionary;
import flash.geom.Vector3D;
import flash.utils.Dictionary;
import flash.geom.*;


class Template extends Sprite {
    public static const VIEW_CREATE:String = 'view_create'
    
    private var stage3D:Stage3D
    private var camera:Camera3D
    public var scene:Object3D
    public var cameraController:SimpleObjectController;
    public var objectController:SimpleObjectController;
    public var controlObject:Object3D;
    
    protected var directionalLight:DirectionalLight;
    protected var ambientLight:AmbientLight;    
    
    public function Template() {
        addEventListener(Event.ADDED_TO_STAGE, init);
    }
    
    private function init(e:Event = null):void 
    {
        removeEventListener(Event.ADDED_TO_STAGE, init);
        
        stage.scaleMode = StageScaleMode.NO_SCALE;
        stage.align = StageAlign.TOP_LEFT;
        stage.quality = StageQuality.HIGH;            
        
        //Stage3Dを用意
        stage3D = stage.stage3Ds[0];
        //Context3Dの生成、呼び出し、初期化
        stage3D.addEventListener(Event.CONTEXT3D_CREATE, onContextCreate);
        stage3D.requestContext3D();
    }
    
    
    private function onContextCreate(e:Event):void {
        stage3D.removeEventListener(Event.CONTEXT3D_CREATE, onContextCreate);
        //View3D(表示エリア)の作成
        var view:View = new View(stage.stageWidth, stage.stageHeight);
        view.antiAlias = 4
        addChild(view);
        
        //Scene(コンテナ)の作成
        scene = new Object3D();

        //Camera(カメラ)の作成
        camera = new Camera3D(1, 100000);
        camera.view = view;
        scene.addChild(camera)
        camera.diagram
        addChild(camera.diagram)
        
        //Cameraをコントロールする場合は、CameraControlerの作成
        cameraController = new SimpleObjectController(stage, camera, 100);
        cameraController.mouseSensitivity = 0;
        cameraController.unbindAll();
        
        //Cameraの位置調整
        cameraController.setObjectPosXYZ(0, -300, 0);
        cameraController.lookAtXYZ(0, 0, 0);
        
        //Lightを追加
        ambientLight = new AmbientLight(0xFFFFFF);
        ambientLight.intensity = 0.5;
        scene.addChild(ambientLight);
        
        //Lightを追加
        directionalLight = new DirectionalLight(0xFFFFFF);
        //手前右上から中央へ向けた指向性light
        directionalLight.x = 0;
        directionalLight.y = -100;
        directionalLight.z = -100;
        directionalLight.lookAt(0, 0, 0);
        scene.addChild(directionalLight);
        //directionalLight.visible = false;
        
    
        //コントロールオブジェクトの作成
        controlObject = new Object3D()
        scene.addChild(controlObject);
        dispatchEvent(new Event(VIEW_CREATE));
    }
    
    
    public function initialize():void {
        for each (var resource:Resource in scene.getResources(true)) {
            trace(resource)
            resource.upload(stage3D.context3D);
        }
        
        //オブジェクト用のコントローラー(マウス操作)
        objectController = new SimpleObjectController(stage, controlObject, 100);
        objectController.mouseSensitivity = 0.2;
        
        //レンダリング
        camera.render(stage3D);
        
        addEventListener(Event.ENTER_FRAME, onRenderTick);
    }

    
    public function onRenderTick(e:Event):void {
        objectController.update()
        camera.render(stage3D);
    }
}


/**
 * Primitive
 */

    
class Primitive extends Mesh {
    protected const RADIAN:Number = Math.PI / 180;
    
    public var inSide:Surface = null;
    public var outSide:Surface = null;        
    protected var indices:Vector.<uint> = new Vector.<uint>();
    protected var positions:Vector.<Number> = new Vector.<Number>();
    protected var texcoords:Vector.<Number> = new Vector.<Number>();        
    
    //表用と裏用のpositionsとtexcoordsとindicesとを追加
    protected var positionsInSide:Vector.<Number> = new Vector.<Number>();
    protected var positionsOutSide:Vector.<Number> = new Vector.<Number>();
    protected var texcoordsInSide:Vector.<Number> = new Vector.<Number>();
    protected var texcoordsOutSide:Vector.<Number> = new Vector.<Number>();
    protected var indicesInSide:Vector.<uint> = new Vector.<uint>();
    protected var indicesOutSide:Vector.<uint> = new Vector.<uint>();            
    
    public function Primitive() {
        geometry = new Geometry();
        var pos:int = VertexAttributes.POSITION
        var tex:int = VertexAttributes.TEXCOORDS[0]
        geometry.addVertexStream([pos,pos,pos,tex,tex]);
    }
    
    protected function setGeometry():void {
        positions = positionsOutSide.concat(positionsInSide);
        texcoords = texcoordsOutSide.concat(texcoordsInSide);
        indices = indicesOutSide.concat();
        var indexStart:int = positionsOutSide.length / 3;
        var count:uint = indicesInSide.length;
        for (var i:uint = 0; i < count; i++) {
            indices.push(indicesInSide[i] + indexStart);
        }
        geometry.numVertices = positions.length / 3;
        geometry.setAttributeValues(VertexAttributes.POSITION, positions);
        geometry.setAttributeValues(VertexAttributes.TEXCOORDS[0], texcoords);
        geometry.indices = indices;
        if (indicesInSide.length) {
            inSide = this.addSurface(null, indicesOutSide.length, indicesInSide.length / 3);
        }
        if (indicesOutSide.length) {
            outSide = this.addSurface(null, 0, indicesOutSide.length / 3);
        }
        MeshUtility.createNormal(this);
    }
}




class RoundMesh extends Primitive {
    
    public function RoundMesh(lineList:Vector.<Point>, radialSegments:uint = 3,lastSegments:uint = 0,star:Number = 0, twoSide:Boolean = false, reverse:Boolean = false) {
        super();
        
        var rInterval:Number = 360 /  radialSegments;
        var heightSegments:uint = lineList.length - 1;
        var height:Number = lineList[heightSegments].y - lineList[0].y;
        
        var radian:Number
        var segmentCount:int = (lastSegments > 0) ? lastSegments : radialSegments;
        for (var i:int = 0; i < segmentCount; i++) {
            for (var j:int = 0; j < heightSegments; j++) {
                var vertices:Vector.<Vector3D> = new Vector.<Vector3D>(4);
                var uvs:Vector.<Point> = new Vector.<Point>(4);
                
                //時計周りで四角を作成していく
                var tempRadiusA:Number = (i % 2 == 1 && star > 0) ? lineList[j].x * star : lineList[j].x
                var tempRadiusB:Number = (i % 2 == 1 && star > 0) ? lineList[j+1].x * star : lineList[j+1].x
                
                radian = (rInterval * i - 90) * RADIAN;
                vertices[0] = new Vector3D(Math.cos(radian) * tempRadiusA, Math.sin(radian) * tempRadiusA, lineList[j].y-(height/2));
                vertices[3] = new Vector3D(Math.cos(radian) * tempRadiusB, Math.sin(radian) * tempRadiusB, lineList[j+1].y-(height/2));
                
                radian = (rInterval * (i + 1) - 90) * RADIAN;
                vertices[1] = new Vector3D(Math.cos(radian) * tempRadiusA, Math.sin(radian) * tempRadiusA, lineList[j].y-(height/2));
                vertices[2] = new Vector3D(Math.cos(radian) * tempRadiusB, Math.sin(radian) * tempRadiusB, lineList[j+1].y-(height/2));

                uvs[0] = new Point(1 / radialSegments * -i, 1 / heightSegments * j);
                uvs[3] = new Point(1 / radialSegments * -i, 1 / heightSegments * (j+1));
                uvs[1] = new Point(1 / radialSegments * -(i+1), 1 / heightSegments * j);
                uvs[2] = new Point(1 / radialSegments * -(i+1), 1 / heightSegments * (j+1));
                
                //Cylinderに巻きつける場合、裏表が逆になるので注意(UVも逆)
                if (reverse == false || twoSide == true) {
                    MeshUtility.createSquare(vertices, uvs, indicesOutSide, positionsOutSide, texcoordsOutSide, true)
                }
                if (reverse == true || twoSide == true) {
                    MeshUtility.createSquare(vertices, uvs, indicesInSide, positionsInSide, texcoordsInSide, false)
                }
                
            }
        }
        setGeometry();
    }
}






class Cylinder extends Primitive {
    public function Cylinder(topRadius:Number = 0, bottomRadius:Number = 50, height:Number = 100, radialSegments:uint = 3, heightSegments:uint = 1, lastSegments:uint = 0,star:Number = 0, twoSide:Boolean = false, reverse:Boolean = false) {
        
        var rInterval:Number = 360 / radialSegments;
        var hInterval:Number = height / heightSegments;
        
        var radian:Number
        var segmentCount:int = (lastSegments > 0) ? lastSegments : radialSegments;
        for (var i:int = 0; i < segmentCount; i++) {
            for (var j:int = 0; j < heightSegments; j++) {
                var vertices:Vector.<Vector3D> = new Vector.<Vector3D>(4);
                var uvs:Vector.<Point> = new Vector.<Point>(4);
                var px:Number=0;
                var py:Number = 0;
                var tmpR:Number = 0;
                var starRatio:Number = 0;
                radian = (rInterval * i - 90) * RADIAN;
                starRatio = (i % 2 == 1 && star > 0) ? star : 1;
                px = Math.cos(radian)
                py = Math.sin(radian)
                tmpR = getRadius(topRadius, bottomRadius, height, hInterval * j, starRatio);
                vertices[0] = new Vector3D(px * tmpR, py * tmpR, hInterval * j - (height / 2));
                tmpR = getRadius(topRadius, bottomRadius, height, hInterval * (j + 1), starRatio);
                vertices[3] = new Vector3D(px * tmpR, py * tmpR, hInterval * (j + 1) - (height / 2));
                
                radian = (rInterval * (i + 1) - 90) * RADIAN;
                starRatio = (i % 2 == 0 && star > 0) ? star : 1;
                px = Math.cos(radian);
                py = Math.sin(radian);
                tmpR = getRadius(topRadius, bottomRadius, height, hInterval * j, starRatio);
                vertices[1] = new Vector3D(px * tmpR, py * tmpR, hInterval * j - (height / 2));
                tmpR = getRadius(topRadius, bottomRadius, height, hInterval * (j + 1), starRatio);
                vertices[2] = new Vector3D(px * tmpR, py * tmpR, hInterval * (j + 1) - (height / 2));
                
                var u:Number = 1 / radialSegments;
                var v:Number = 1 / heightSegments;
                uvs[0] = new Point(u * -i, v * j);
                uvs[3] = new Point(u * -i, v * (j + 1));
                uvs[1] = new Point(u * -(i + 1), v * j);
                uvs[2] = new Point(u * -(i + 1), v * (j + 1));
                
                
                //Cylinderに巻きつける場合、裏表が逆になるので注意(UVも逆)
                if (reverse == false || twoSide == true) {
                    MeshUtility.createSquare(vertices, uvs, indicesOutSide, positionsOutSide, texcoordsOutSide, true)
                }
                if (reverse == true || twoSide == true) {
                    MeshUtility.createSquare(vertices, uvs, indicesInSide, positionsInSide, texcoordsInSide, false)
                }
                
            }
        }
        setGeometry()

    }
    
    /**
     * 半径を高さの比率から割り出す
     * @return
     */
    private function getRadius(topRadius:Number, bottomRadius:Number, height:Number, length:Number, starRatio:Number):Number {
        var result:Number
        var difference:Number
        var ratio:Number
        
        if (topRadius > bottomRadius) {
            difference = topRadius - bottomRadius;
            ratio = (length) ? (height - length) / height : 1
            result = (bottomRadius + (difference * ratio));
        } else {
            difference = bottomRadius - topRadius;
            ratio = (length) ? length / height : 0
            result = (topRadius + (difference * ratio));
        }
        result *= starRatio
        return result;
    }

}








/**
 * 頂点に隣接する面法線を収集するクラス
 */

class ExtraVertex {
    public var vertex:Vector3D;
    public var normals:Dictionary;
    public var indices:Vector.<uint>;
    public function ExtraVertex(x:Number, y:Number, z:Number) {
        vertex = new Vector3D(x, y, z);
        normals = new Dictionary();
        indices = new Vector.<uint>();
    }
    
}



/**
 * MeshUtility
 */


use namespace alternativa3d;

class MeshUtility {
    
    public function MeshUtility() {
    
    }
    
    /**
     * 3つの頂点座標から、Faceを作成し、indices、positionsに各値を登録する
     */
    public static function createTriangle(vertices:Vector.<Vector3D>, uvs:Vector.<Point>,
                                          indices:Vector.<uint>, positions:Vector.<Number>, 
                                          texcoords:Vector.<Number>, reverse:Boolean = false):void {
        if (reverse == false) {
            //三角形用の頂点を登録
            positions.push(vertices[0].x, vertices[0].y, vertices[0].z,
                           vertices[2].x, vertices[2].y, vertices[2].z,
                           vertices[1].x, vertices[1].y, vertices[1].z);
            
            //三角形用のUVを登録
            texcoords.push(uvs[0].x, uvs[0].y,uvs[2].x, uvs[2].y,uvs[1].x, uvs[1].y);
            
        } else {
            //三角形用の頂点を登録
            positions.push(vertices[0].x, vertices[0].y, vertices[0].z,
                           vertices[1].x, vertices[1].y, vertices[1].z,
                           vertices[2].x, vertices[2].y, vertices[2].z);
            
            //三角形用のUVを登録
            texcoords.push(uvs[0].x, uvs[0].y,uvs[1].x, uvs[1].y,uvs[2].x, uvs[2].y);
            
        }
        //Face用indexを登録
        var startIndex:uint = indices.length
        indices.push(startIndex + 0, startIndex + 1, startIndex + 2);
    }
    
    /**
     * 4つの頂点座標から、四角形(2つのFace)を作成し、indices、positions、uvsに各値を登録する
     */
    public static function createSquare(vertices:Vector.<Vector3D>, uvs:Vector.<Point>,
                                        indices:Vector.<uint>, positions:Vector.<Number>, 
                                        texcoords:Vector.<Number>, reverse:Boolean = false):void {
        
        if (reverse == false) {
            positions.push(    vertices[0].x, vertices[0].y, vertices[0].z,
                            vertices[3].x, vertices[3].y, vertices[3].z,
                            vertices[1].x, vertices[1].y, vertices[1].z);
            positions.push(    vertices[1].x, vertices[1].y, vertices[1].z,
                            vertices[3].x, vertices[3].y, vertices[3].z,
                            vertices[2].x, vertices[2].y, vertices[2].z);    
            //三角形用のUVを登録
            texcoords.push(uvs[0].x, uvs[0].y,uvs[3].x, uvs[3].y,uvs[1].x, uvs[1].y);
            texcoords.push(uvs[1].x, uvs[1].y,uvs[3].x, uvs[3].y,uvs[2].x, uvs[2].y);
                            
        } else {
            positions.push(    vertices[0].x, vertices[0].y, vertices[0].z,
                            vertices[1].x, vertices[1].y, vertices[1].z,
                            vertices[3].x, vertices[3].y, vertices[3].z);
            positions.push(    vertices[1].x, vertices[1].y, vertices[1].z,
                            vertices[2].x, vertices[2].y, vertices[2].z,
                            vertices[3].x, vertices[3].y, vertices[3].z);    
            //三角形用のUVを登録
            texcoords.push(uvs[0].x, uvs[0].y,uvs[1].x, uvs[1].y,uvs[3].x, uvs[3].y);
            texcoords.push(uvs[1].x, uvs[1].y, uvs[2].x, uvs[2].y, uvs[3].x, uvs[3].y);
        }
        
        //Face用indexを登録
        var startIndex:uint = indices.length
        indices.push(startIndex + 0, startIndex + 1, startIndex + 2);
        indices.push(startIndex + 3, startIndex + 4, startIndex + 5);
    
    }
    
    /**
     * 指定Meshの法線を作成します
     * @param    mesh
     */
    public static function createNormal(mesh:Mesh):void {
        //法線の有無のチェック
        if (mesh.geometry.hasAttribute(VertexAttributes.NORMAL) == false) {
            var nml:int = VertexAttributes.NORMAL;    
            mesh.geometry.addVertexStream([nml,nml,nml]);
        }
        
        var indices:Vector.<uint> = mesh.geometry.indices;
        var positions:Vector.<Number> = mesh.geometry.getAttributeValues(VertexAttributes.POSITION);
        var vartices:Vector.<Vector3D> = new Vector.<Vector3D>(positions.length / 3);
        var vNormals:Vector.<Vector3D> = new Vector.<Vector3D>(positions.length / 3);
        
        var i:int;
        var count:uint = positions.length / 3;
        for (i = 0; i < count; i++) {
            vartices[i] = new Vector3D(positions[i * 3], positions[i * 3 + 1], positions[i * 3 + 2]);
        }
        
        //面法線を求め、頂点法線に代入する
        count = indices.length;
        for (i = 0; i < count; i += 3) {
            var normal:Vector3D = calcNormal(vartices[indices[i]], vartices[indices[i + 1]], vartices[indices[i + 2]]);
            vNormals[indices[i]] = normal;
            vNormals[indices[i + 1]] = normal;
            vNormals[indices[i + 2]] = normal;
        }
        
        var normals:Vector.<Number> = new Vector.<Number>();
        
        count = vNormals.length;
        for (i = 0; i < count; i++) {
            if (vNormals[i]) {
                normals.push(vNormals[i].x, vNormals[i].y, vNormals[i].z);
            }
        }
        
        mesh.geometry.setAttributeValues(VertexAttributes.NORMAL, normals);
    }
    
    /**
     * 面法線の計算
     * 三つの頂点座標からなる三角ポリゴンの法線を計算し返します
     */
    public static function calcNormal(a:Vector3D, b:Vector3D, c:Vector3D):Vector3D {
        var v1:Vector3D = b.subtract(a);
        var v2:Vector3D = c.subtract(a);
        var v3:Vector3D = v1.crossProduct(v2);
        //var v3:Vector3D = cross(v1,v2);
        v3.normalize();
        return (v3);
    }
    

    
    /**
     * 指定Meshの法線をSmoothShadingにします
     * @param    mesh
     */
    public static function smoothShading(mesh:Mesh,separateSurface:Boolean=false,threshold:Number=0.000001):void {
        
        var indices:Vector.<uint> = mesh.geometry.indices;
        var positions:Vector.<Number> = mesh.geometry.getAttributeValues(VertexAttributes.POSITION);
        var normals:Vector.<Number> = mesh.geometry.getAttributeValues(VertexAttributes.NORMAL);
        
        var vartices:Vector.<Vector3D> = new Vector.<Vector3D>(positions.length / 3);
        var vNormals:Vector.<Vector3D> = new Vector.<Vector3D>(normals.length / 3);
        
        var vertexDictionary:Dictionary = new Dictionary()
        var exVertex:ExtraVertex;
        
        //サーフェースごとに判断する

        for (var s:uint = 0; s < mesh.numSurfaces; s++ ) {
            var side:String = (separateSurface) ? s.toString() : '';
            for (var n:uint = 0; n < mesh.getSurface(s).numTriangles * 3; n++) {
                var i:uint = indices[n+mesh.getSurface(s).indexBegin];
                vartices[i] = new Vector3D(positions[i * 3], positions[i * 3 + 1], positions[i * 3 + 2]);
                //誤差を丸める
                vartices[i].x = int(vartices[i].x / threshold) * threshold;
                vartices[i].y = int(vartices[i].y / threshold) * threshold;
                vartices[i].z = int(vartices[i].z / threshold) * threshold;                
                vNormals[i] = new Vector3D(normals[i * 3], normals[i * 3 + 1], normals[i * 3 + 2]);
                //誤差を丸める
                vNormals[i].x = int(vNormals[i].x / threshold) * threshold;
                vNormals[i].y = int(vNormals[i].y / threshold) * threshold;
                vNormals[i].z = int(vNormals[i].z / threshold) * threshold;
                
                //同じ頂点を集める
                //ただし、表裏がある場合があるので法線の方向もチェックする
                if (vertexDictionary[vartices[i].toString()+'_'+side]) {
                    exVertex = vertexDictionary[vartices[i].toString()+'_'+side]
                    if (exVertex.normals[vNormals[i].toString()+'_'+side] == null) {
                        exVertex.normals[vNormals[i].toString()+'_'+side] = vNormals[i];
                    }
                    exVertex.indices.push(i);

                } else {
                    exVertex = new ExtraVertex(vNormals[i].x, vNormals[i].y, vNormals[i].z);
                    exVertex.normals[vNormals[i].toString()+'_'+side] = vNormals[i];
                    exVertex.indices.push(i)
                    vertexDictionary[vartices[i].toString()+'_'+side] = exVertex
                }
            }                
            
        }

        //Normalの平均化
        var count:uint = 0;
        for each (exVertex in vertexDictionary) {
            var normalX:Number = 0;
            var normalY:Number = 0;
            var normalZ:Number = 0;
            count = 0
            for each (var normal:Vector3D in exVertex.normals) {
                normalX += normal.x;
                normalY += normal.y;
                normalZ += normal.z;
                count++
            }
            normal = new Vector3D(normalX / count, normalY / count, normalZ / count);
            normal.normalize();
            count = exVertex.indices.length;
            for (i = 0; i < count; i++) {
                vNormals[exVertex.indices[i]] = normal;
            }
        }
        count = vNormals.length;
        normals = new Vector.<Number>();
        for (i = 0; i < count; i++) {
            normals.push(vNormals[i].x, vNormals[i].y, vNormals[i].z);
        }
        
        mesh.geometry.setAttributeValues(VertexAttributes.NORMAL, normals);
    }
    
    
    /**
     * 指定MeshのUVをVertexのxyから仮に作成する
     * @param    mesh
     */
    public static function createUv(mesh:Mesh):void {
        if (mesh.geometry.hasAttribute(VertexAttributes.TEXCOORDS[0]) == false) {
            var tex:int = VertexAttributes.TEXCOORDS[0];    
            mesh.geometry.addVertexStream([tex,tex]);
        }
        mesh.calculateBoundBox()
        var width:Number = mesh.boundBox.maxX - mesh.boundBox.minX
        var length:Number = mesh.boundBox.maxZ - mesh.boundBox.minZ
        
        var positions:Vector.<Number> = mesh.geometry.getAttributeValues(VertexAttributes.POSITION);
        var texcoords:Vector.<Number> = new Vector.<Number>;
        var i:int;
        for (i = 0; i < positions.length; i += 3) {
            texcoords.push((positions[i] - mesh.boundBox.minX) / width, (positions[i + 2] - mesh.boundBox.minZ) / length);
        }

        mesh.geometry.setAttributeValues(VertexAttributes.TEXCOORDS[0], texcoords);
    }
    
    
    
    /**
     * 指定MeshのTangentを作成する
     * @param    mesh
     */
    static public function createTangent(mesh:Mesh):void {
        //接線有無のチェック


        
        
        var positions:Vector.<Number> = mesh.geometry.getAttributeValues(VertexAttributes.POSITION);
        var texcoords:Vector.<Number> = mesh.geometry.getAttributeValues(VertexAttributes.TEXCOORDS[0]);
        var normals:Vector.<Number> = mesh.geometry.getAttributeValues(VertexAttributes.NORMAL);
        
        var indices:Vector.<uint> = mesh.geometry.indices;
        var vertices:Vector.<Vector3D> = new Vector.<Vector3D>;
        var uvs:Vector.<Point> = new Vector.<Point>;
        var vNormals:Vector.<Vector3D> = new Vector.<Vector3D>;
        
        var i:int;
        for (i = 0; i < positions.length; i += 3) {
            vertices.push(new Vector3D(positions[i], positions[i + 1], positions[i + 2]));
        }
        for (i = 0; i < texcoords.length; i += 2) {
            uvs.push(new Point(texcoords[i], texcoords[i + 1]));
        }
        for (i = 0; i < normals.length; i += 3) {
            vNormals.push(new Vector3D(normals[i], normals[i + 1], normals[i + 2]));
        }
        
        var tangents:Vector.<Number> = calcTangent(mesh.geometry.indices, vertices, uvs, vNormals);
        
        var geometry:Geometry = new Geometry();
        //if (mesh.geometry.hasAttribute(VertexAttributes.TANGENT4) == false) {
        var tan:int = VertexAttributes.TANGENT4;
        var pos:int = VertexAttributes.POSITION;
        var nor:int = VertexAttributes.NORMAL;
        var tex:int = VertexAttributes.TEXCOORDS[0];
        var attribute:Array = [
            pos, pos, pos,
            nor, nor, nor,
            tan, tan, tan, tan,
            tex, tex
        ]
        geometry.addVertexStream(attribute)
        //}                
        geometry.numVertices = mesh.geometry.numVertices
        geometry.indices = mesh.geometry.indices;
        geometry.setAttributeValues(VertexAttributes.POSITION, positions);
        geometry.setAttributeValues(VertexAttributes.NORMAL, normals);
        geometry.setAttributeValues(VertexAttributes.TANGENT4, tangents);
        geometry.setAttributeValues(VertexAttributes.TEXCOORDS[0], texcoords);

        mesh.geometry = geometry;
    }
    
    
    /**
     * 複数のMeshを結合し、1つのMeshにします
     * @param    meshs
     * @return
     */
    public static function bindMeshs(meshs:Vector.<Mesh>):Mesh {
        var count:uint = meshs.length;
        
        var indices:Vector.<uint> = new Vector.<uint>();
        var positions:Vector.<Number> = new Vector.<Number>();
        var texcoords:Vector.<Number> = new Vector.<Number>();
        
        var nextIndex:uint = 0;
        var nextPosition:uint = 0;
        var mesh:Mesh = meshs[i];
        var i:int
        var j:int
        for (i = 0; i < count; i++) {
            mesh = meshs[i];
            var tempPositions:Vector.<Number> = mesh.geometry.getAttributeValues(VertexAttributes.POSITION);
            mesh.matrix.transformVectors(tempPositions, tempPositions);
            positions = positions.concat(tempPositions);
            texcoords = texcoords.concat(mesh.geometry.getAttributeValues(VertexAttributes.TEXCOORDS[0]));
            
            var tempIndices:Vector.<uint> = mesh.geometry.indices;            
            var indexCount:uint = tempIndices.length
            for (j = 0; j < indexCount; j++) {
                tempIndices[j] += nextIndex;
            }
            indices = indices.concat(tempIndices);
            nextIndex += tempPositions.length/3
        }

        var geometry:Geometry = new Geometry();
        
        var attributes:Array = [];
        attributes[0] = VertexAttributes.POSITION;
        attributes[1] = VertexAttributes.POSITION;
        attributes[2] = VertexAttributes.POSITION;
        attributes[3] = VertexAttributes.TEXCOORDS[0];
        attributes[4] = VertexAttributes.TEXCOORDS[0];
        
        geometry.addVertexStream(attributes);
        
        geometry.numVertices = positions.length/3;
        
        geometry.setAttributeValues(VertexAttributes.POSITION, positions);
        geometry.setAttributeValues(VertexAttributes.TEXCOORDS[0], texcoords);
        
        geometry.indices = indices;            
        
        var result:Mesh = new Mesh()
        result.geometry = geometry;
        
        //サーフェースのコピー
        var indexBegin:uint = 0
        for (i = 0; i < count; i++) {
            mesh = meshs[i];
            for (j = 0; j < mesh.numSurfaces; j++) {
                var surface:Surface = mesh.getSurface(j);
                result.addSurface(surface.material, surface.indexBegin+indexBegin, surface.numTriangles)
            }
            indexBegin = surface.indexBegin+indexBegin + surface.numTriangles * 3;
        }
        
        //normal再計算
        createNormal(result);
        return result;
    }
    
    
    /**
     * Cylinder、Cone、Dome、RoundMesh等を合成した、MeshのSurfaceを合成します
     * UVのV値のみ更新されます
     * 
     * 頂点情報の高さ(Z座標)で判断します
     * 
     */
    public static function repairRoundSurface(mesh:Mesh):Mesh {
        var positions:Vector.<Number> = mesh.geometry.getAttributeValues(VertexAttributes.POSITION);
        var texcoords:Vector.<Number> = mesh.geometry.getAttributeValues(VertexAttributes.TEXCOORDS[0]);
        var count:int = positions.length / 3
        //全体の高さを割り出す
        var minY:Number=0
        var maxY:Number=0
        for (var i:int = 0; i < count; i++) {
            if (minY > positions[i * 3 + 2])
                minY = positions[i * 3 + 2];
            if (maxY < positions[i * 3 + 2])
                maxY = positions[i * 3 + 2];
        }
        
        var height:Number = maxY - minY;
        for (i = 0; i < count; i++) {
            texcoords[i * 2 + 1] = (positions[i * 3 + 2] - minY) / height;
        }
        mesh.geometry.setAttributeValues(VertexAttributes.TEXCOORDS[0], texcoords);
        var result:Mesh = new Mesh();
        result.geometry = mesh.geometry
        result.addSurface(null, 0, positions.length / 9);
        return result;
    }
    
    
    /**
     * サーフェースのコピー
     * @param    origin
     * @param    mesh
     */
    public static function copySurface(origin:Mesh, mesh:Mesh):void {
        for (var i:uint = 0; i < origin.numSurfaces; i++) {
            var surface:Surface = origin.getSurface(i);
            mesh.addSurface(surface.material, surface.indexBegin, surface.numTriangles)
        }
    }

    
    /**
     * TANGENT4を再計算
     * @param    indices
     * @param    vertex
     * @param    uvs
     * @param    normals
     * @return
     */
    static public function calcTangent(indices:Vector.<uint>, vertices:Vector.<Vector3D>, uvs:Vector.<Point>, normals:Vector.<Vector3D>):Vector.<Number> {
        var tangent:Vector.<Number> = new Vector.<Number>;
        var numTriangle:int = indices.length / 3;
        var numVertex:int = vertices.length;
        
        var tan1:Vector.<Vector3D> = new Vector.<Vector3D>;
        var tan2:Vector.<Vector3D> = new Vector.<Vector3D>;
        
        var i:int;
        for (i = 0; i < vertices.length; i++) {
            tan1.push(new Vector3D());
            tan2.push(new Vector3D());
        }
        
        var max:int = indices.length;
        for (i = 0; i < max; i += 3) {
            var i1:Number = indices[i];
            var i2:Number = indices[i + 1];
            var i3:Number = indices[i + 2];
            
            var v1:Vector3D = vertices[i1];
            var v2:Vector3D = vertices[i2];
            var v3:Vector3D = vertices[i3];
            
            var w1:Point = uvs[i1];
            var w2:Point = uvs[i2];
            var w3:Point = uvs[i3];
            
            var x1:Number = v2.x - v1.x;
            var x2:Number = v3.x - v1.x;
            var y1:Number = v2.y - v1.y;
            var y2:Number = v3.y - v1.y;
            var z1:Number = v2.z - v1.z;
            var z2:Number = v3.z - v1.z;
            
            var s1:Number = w2.x - w1.x;
            var s2:Number = w3.x - w1.x;
            var t1:Number = w2.y - w1.y;
            var t2:Number = w3.y - w1.y;
            
            var r:Number = 1 / (s1 * t2 - s2 * t1);
            var sdir:Vector3D = new Vector3D((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r);
            var tdir:Vector3D = new Vector3D((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r);
            
            tan1[i1].incrementBy(sdir);
            tan1[i2].incrementBy(sdir);
            tan1[i3].incrementBy(sdir);
            
            tan2[i1].incrementBy(tdir);
            tan2[i2].incrementBy(tdir);
            tan2[i3].incrementBy(tdir);
        }
        
        for (i = 0; i < numVertex; i++) {
            var n:Vector3D = normals[i];
            var t:Vector3D = tan1[i];
            var tgt:Vector3D = t.subtract(getScaled(n, dot(n, t)));
            tgt.normalize();
            var w:Number = dot(cross(n, t), tan2[i]) < 0 ? -1 : 1;
            tangent.push(tgt.x, tgt.y, tgt.z, w);
        }
        return tangent;
    }
    
    /**
     * 2つのベクトルの内積を返します。
     * (内積:2つのベクトルがどれだけ平行に近いかを示す数値)
     * ・ 1 に近いほど同じ向きで平行
     * ・ 0 に近いほど直角
     * ・-1 に近いほど逆向きで平行
     */
    static public function dot(a:Vector3D, b:Vector3D):Number {
        return (a.x * b.x) + (a.y * b.y) + (a.z * b.z);
    }
    
    /**
     * 2つのベクトルの外積を返します。
     * (外積:2つのベクトルで作られる面に垂直なベクトル(=法線)。)
     */
    static public function cross(a:Vector3D, b:Vector3D):Vector3D {
        return new Vector3D((a.y * b.z) - (a.z * b.y), (a.z * b.x) - (a.x * b.z), (a.x * b.y) - (a.y * b.x));
    }
    
    /**
     * スケーリングした新しいベクトルを取得
     * @param    v
     * @param    scale
     * @return
     */
    static public function getScaled(v:Vector3D, scale:Number):Vector3D {
        var sv:Vector3D = v.clone();
        sv.scaleBy(scale);
        return sv;
    }
    
    /**
     * Jointの位置を初期化
     * @param    joints
     */
    public static function JointBindPose(joints:Vector.<Joint>):void {
        var count:uint = joints.length;
        for (var i:uint = 0; i < count; i++) 
        {
            var joint:Joint = joints[i]
            var jointMatrix:Matrix3D = joint.concatenatedMatrix.clone();

            jointMatrix.transpose();
            var jointBindingTransform:Transform3D = new Transform3D();
            jointBindingTransform.initFromVector(jointMatrix.rawData);
            jointBindingTransform.invert();
            var matrixVector:Vector.<Number> = new Vector.<Number>();
            matrixVector.push(jointBindingTransform.a);
            matrixVector.push(jointBindingTransform.b);
            matrixVector.push(jointBindingTransform.c);
            matrixVector.push(jointBindingTransform.d);
            matrixVector.push(jointBindingTransform.e);
            matrixVector.push(jointBindingTransform.f);
            matrixVector.push(jointBindingTransform.g);
            matrixVector.push(jointBindingTransform.h);
            matrixVector.push(jointBindingTransform.i);
            matrixVector.push(jointBindingTransform.j);
            matrixVector.push(jointBindingTransform.k);
            matrixVector.push(jointBindingTransform.l);
            
            joint.setBindPoseMatrix(matrixVector);

        }
    }
    

}