forked from: Shadow

by dizgid forked from Shadow (diff: 334)
影がどうやってついているか、学習のためfork  なるほどこうなってるのか。すごい。
シャドウボリューム法を参考に無理やり影生成をしてみました。
* 色々と間違ってる気がします。もの凄く重い。
♥0 | Line 336 | Modified 2010-07-05 12:57:26 | MIT License
play

ActionScript3 source code

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

// 影がどうやってついているか、学習のためfork  なるほどこうなってるのか。すごい。
// forked from tencho's Shadow
/**
 * シャドウボリューム法を参考に無理やり影生成をしてみました。
 * 色々と間違ってる気がします。もの凄く重い。
 */
package  {
    import com.bit101.components.*;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.BlendMode;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.filters.BlurFilter;
    import flash.geom.Point;
    import flash.utils.Dictionary;
    import org.papervision3d.cameras.Camera3D;
    import org.papervision3d.core.clipping.FrustumClipping;
    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.Number3D;
    import org.papervision3d.materials.ColorMaterial;
    import org.papervision3d.materials.utils.MaterialsList;
    import org.papervision3d.objects.DisplayObject3D;
    import org.papervision3d.objects.primitives.Cube;
    import org.papervision3d.objects.primitives.Plane;
    import org.papervision3d.objects.primitives.Sphere;
    import org.papervision3d.Papervision3D;
    import org.papervision3d.render.BasicRenderEngine;
    import org.papervision3d.render.QuadrantRenderEngine;
    import org.papervision3d.scenes.Scene3D;
    import org.papervision3d.view.BitmapViewport3D;
    import org.papervision3d.view.Viewport3D;
    
    import org.papervision3d.materials.special.CompositeMaterial;
    import org.papervision3d.materials.WireframeMaterial;
    
    public class Shadow extends Sprite {
        private var scene:Scene3D;
        private var render:BasicRenderEngine;
        private var camera:Camera3D;
        private var viewport:Viewport3D;
        private var cameraTarget:DisplayObject3D;
        private var light:Sphere;
        private var plane:Plane;
        private var kaidan:Plane;
        private var wall:Plane;
        private var floor:Plane;
        private var points:Array;
        private var indexes:Array;
        private var bg:Sprite;
        private var shadowScene:Scene3D;
        private var shadowViewport:BitmapViewport3D
        private var shadowRender:QuadrantRenderEngine;
        //private var shadowRender:BasicRenderEngine;
        private var shadowModel:Cube;
        private var shadowMap:BitmapData;
        private var fillMaterial:CompositeMaterial;
        //private var maskMaterial:ColorMaterial;
        private var maskMaterial:CompositeMaterial;
        private var topColor:ColorMaterial;
        private var sideColor:CompositeMaterial;
        private var shadowClones:Dictionary;
        private var totalShadow:Sprite;
        private var view1:Bitmap;
        private var view2:Bitmap;
        private var sceneDragger:SceneDragger;
        private var lightDragger:LightDragger;
        private var shadowBitmap:Bitmap;
        //コンストラクタ
        public function Shadow() {
            Style.LABEL_TEXT = 0xFFFFFF;
            var size:Array = [100, 450];
            bg = new Sprite();
            bg.graphics.beginFill(0x333333);
            bg.graphics.drawRect(0, 0, 465, 465);
            points = [new Number3D(size[0]/2, -size[1]/2, -600), new Number3D(-size[0]/2, -size[1]/2, -600), new Number3D(size[0]/2, size[1]/4, -600), new Number3D(-size[0]/2, size[1]/2, -600)];
            indexes = [0,1,2,3,6,7,4,5];
            scene = new Scene3D();
            render = new BasicRenderEngine();
            render.clipping = new FrustumClipping(FrustumClipping.NEAR);
            viewport = new Viewport3D(465, 465, false);
            camera = new Camera3D();
            cameraTarget = scene.addChild(new DisplayObject3D());
            cameraTarget.position = new Number3D(0, 0, 0);
            //color
            fillMaterial = new CompositeMaterial();
            fillMaterial.addMaterial(new WireframeMaterial(0xff0000));
            fillMaterial.addMaterial(new ColorMaterial(0x00ff00, 0.5));
            
            maskMaterial = new CompositeMaterial();
            maskMaterial.addMaterial(new WireframeMaterial(0xff0000));
            maskMaterial.addMaterial(new ColorMaterial(0xffffff));
            topColor = new ColorMaterial(0x969696);
            sideColor = new CompositeMaterial();
            sideColor.addMaterial(new WireframeMaterial(0xff0000));
            sideColor.addMaterial(new ColorMaterial(0xD5D5D5));
            //model
            createModels();
            //shadow
            shadowScene = new Scene3D();
            shadowRender = new QuadrantRenderEngine(QuadrantRenderEngine.ALL_FILTERS);
            //shadowRender = new BasicRenderEngine();
            shadowViewport = new BitmapViewport3D(465, 465, false, false, 0x000000);
            shadowModel = new Cube(new MaterialsList( { all:maskMaterial } ), 100, 100, 100, 1, 1, 1);
            scene.addChild(plane);
            scene.addChild(wall);
            scene.addChild(floor);
            scene.addChild(kaidan);
            scene.addChild(light);
            shadowScene.addChild(shadowModel);
            var clones:Array = [kaidan, plane, wall, floor];
            shadowClones = new Dictionary();
            for each(var model:DisplayObject3D in clones) {
                var tm:TriangleMesh3D = shadowScene.addChild(model.clone()) as TriangleMesh3D;
                for each(var tri:Triangle3D in tm.geometry.faces) tri.material = fillMaterial;
                shadowClones[model] = tm;
            }
            shadowMap = new BitmapData(465, 465, false, 0x00000000);
            totalShadow = new Sprite();
            view1 = totalShadow.addChild(new Bitmap(new BitmapData(465, 465, true))) as Bitmap;
            view2 = totalShadow.addChild(new Bitmap(new BitmapData(465, 465, false))) as Bitmap;
            view2.blendMode = BlendMode.SUBTRACT;
            shadowBitmap = new Bitmap(shadowMap);
            shadowBitmap.alpha = 0.2;
            shadowBitmap.filters = [new BlurFilter(5, 5, 1)];
            shadowBitmap.blendMode = BlendMode.SUBTRACT;
            sceneDragger = new SceneDragger(bg, -120, 15);
            sceneDragger.addEventListener(Event.CHANGE, onMoveCamera);
            lightDragger = new LightDragger();
            lightDragger.addEventListener(Event.CHANGE, onChangeLight);
            viewport.mouseChildren = false;
            viewport.mouseEnabled = false;
            addChild(bg);
            addChild(viewport);
            addChild(shadowBitmap);
            addChild(lightDragger);
            createButtons();
            updateLight();
            refresh();
            
            shadowViewport.mouseChildren = false;
            shadowViewport.mouseEnabled = false;
            addChild(shadowViewport);
        }
        private function createButtons():void {
            var vox:VBox = new VBox(this, 7, 115);
            new RadioButton(vox, 0, 0, "NORMAL", true, onSelectMode).tag = 0;
            new RadioButton(vox, 0, 0, "VIEW1", false, onSelectMode).tag = 1;
            new RadioButton(vox, 0, 0, "VIEW2", false, onSelectMode).tag = 2;
            new Label(this, 3, 465-20, "DRAG TO ROTATE CAMERA");
        }
        private function onSelectMode(e:MouseEvent):void{
            switch(RadioButton(e.currentTarget).tag) {
                case 0:
                    view1.visible = true;
                    view2.visible = true;
                    view2.blendMode = BlendMode.SUBTRACT;
                    shadowBitmap.filters = [new BlurFilter(5, 5, 1)];
                    shadowBitmap.alpha = 0.2;
                    break;
                case 1:
                    view1.visible = true;
                    view2.visible = false;
                    shadowBitmap.alpha = 0.4;
                    shadowBitmap.filters = [];
                    break;
                case 2:
                    view1.visible = false;
                    view2.visible = true;
                    view2.blendMode = BlendMode.NORMAL;
                    shadowBitmap.filters = [];
                    shadowBitmap.alpha = 0.4;
                    break;
            }
            refresh();
        }
        private function createModels():void {
            var i:int;
            var slide:Number = 500;
            var stepDepth:Number = 150;
            var stepHeight:Number = 60;
            var stepNum:Number = 5;
            var stepWidth:Number = 1000;
            var planeColor:ColorMaterial = new ColorMaterial(0xEEEEEE);
            planeColor.doubleSided = true;
            plane = new Plane(planeColor, 100, 500, 1, 1);
            for (i = 0; i < plane.geometry.vertices.length; i++) {
                plane.geometry.vertices[i].x = points[i].x;
                plane.geometry.vertices[i].y = points[i].y;
                plane.geometry.vertices[i].z = points[i].z - 5;
            }
            kaidan = new Plane(null, 100, stepWidth, stepNum*2, 1);
            kaidan.rotationY = kaidan.rotationX = 90;
            kaidan.z = slide;
            for (i = 0; i < kaidan.geometry.faces.length; i++)
                kaidan.geometry.faces[i].material = (int(i/2)%2)? sideColor : topColor;
            for (i = 0; i < kaidan.geometry.vertices.length; i++) {
                var v:Vertex3D = kaidan.geometry.vertices[i];
                v.z = int(i / 4) * stepHeight;
                v.x = int((i+2) / 4) * stepDepth;
            }
            floor = new Plane(topColor, stepWidth, 500, 1, 1);
            floor.z = -stepDepth * stepNum + slide - 500/2;
            floor.y = -stepHeight * stepNum;
            floor.rotationX = 90;
            wall = new Plane(sideColor, stepWidth, 1000, 1, 1);
            wall.y = 1000/2;
            wall.z = slide;
            light = new Sphere(new ColorMaterial(0xFFAA00, 0.5), 50, 8, 4);
            light.position = new Number3D(0, 300, -1000);
        }
        private function onChangeLight(e:Event):void {
            updateLight();
            refresh();
        }
        private function onMoveCamera(e:Event):void {
            refresh();
        }
        private function updateLight():void {
            var pos:Point = lightDragger.getPosition();
            light.x = pos.x * 500;
            light.y = pos.y * 500 - 200;
            for (var i:int = 0; i < 4; i++) {
                shadowModel.geometry.vertices[indexes[i]].x = points[i].x;
                shadowModel.geometry.vertices[indexes[i]].y = points[i].y;
                shadowModel.geometry.vertices[indexes[i]].z = points[i].z;
                var stretch:Number3D = Number3D.sub(points[i], light.position);
                stretch.multiplyEq(5);
                stretch.plusEq(light.position);
                shadowModel.geometry.vertices[indexes[i + 4]].x = stretch.x;
                shadowModel.geometry.vertices[indexes[i + 4]].y = stretch.y;
                shadowModel.geometry.vertices[indexes[i + 4]].z = stretch.z;
            }
        }
        private function refresh():void {
            camera.position = sceneDragger.getPosition(2000);
            camera.lookAt(cameraTarget);
            for(var m:* in shadowClones) m.transform = shadowClones[m].transform;
            render.renderScene(scene, camera, viewport);
            drawShadow();
        }
        private function drawShadow():void {
            shadowRender.renderScene(shadowScene, camera, shadowViewport);
            view1.bitmapData.copyPixels(shadowViewport.bitmapData, shadowViewport.bitmapData.rect, new Point());
            shadowModel.geometry.flipFaces();
            shadowRender.renderScene(shadowScene, camera, shadowViewport);
            view2.bitmapData.copyPixels(shadowViewport.bitmapData, shadowViewport.bitmapData.rect, new Point());
            shadowMap.draw(totalShadow);
            shadowModel.geometry.flipFaces();
        }
    }
}
import com.bit101.components.Label;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.geom.Rectangle;
import org.papervision3d.core.math.Number3D;
/**
 * ライト位置をマウスで動かす
 */
class LightDragger extends Sprite {
    private var position:Point;
    private var circle:Sprite;
    private var grid:BitmapData;
    public function LightDragger() {
        position = new Point(50, 70);
        grid = new BitmapData(10, 10, false, 0xFFA0A0A0);
        grid.fillRect(new Rectangle(0, 0, 10, 1), 0xFF888888);
        grid.fillRect(new Rectangle(0, 0, 1, 10), 0xFF888888);
        graphics.clear();
        graphics.beginBitmapFill(grid);
        graphics.drawRect(0, 0, 100, 100);
        circle = new Sprite();
        circle.graphics.beginFill(0x000000);
        circle.graphics.drawCircle(0, 0, 5);
        circle.graphics.beginFill(0xFFFFFF);
        circle.graphics.drawCircle(0, 0, 4);
        circle.mouseChildren = false;
        circle.mouseEnabled = false;
        addChild(circle);
        draw(position.x, position.y);
        buttonMode = true;
        addEventListener(MouseEvent.MOUSE_DOWN, onMsDown);
        new Label(this, 3, 3, "LIGHT POSITION");
    }
    public function getPosition():Point {
        return new Point((position.x - 50) / 50, 1 - (position.y - 50) / 50);
    }
    private function onMsUp(e:MouseEvent):void {
        stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMsMove);
        stage.removeEventListener(MouseEvent.MOUSE_UP, onMsUp);
        draw();
    }
    private function onMsDown(e:MouseEvent):void {
        stage.addEventListener(MouseEvent.MOUSE_MOVE, onMsMove);
        stage.addEventListener(MouseEvent.MOUSE_UP, onMsUp);
        draw();
    }
    private function onMsMove(e:MouseEvent):void {
        draw();
    }
    private function draw(px:Number = NaN, py:Number = NaN):void {
        if (isNaN(px)) px = Math.max(0, Math.min(100, mouseX));
        if (isNaN(py)) py = Math.max(0, Math.min(100, mouseY));
        circle.x = px;
        circle.y = py;
        position.x = px;
        position.y = py;
        dispatchEvent(new Event(Event.CHANGE));
    }
}
/**
 * シーンをマウスでぐるぐる
 */
class SceneDragger extends EventDispatcher {
    private var _rotation:Number;
    private var _angle:Number;
    private var _sprite:Sprite;
    private var _saveRotation:Number;
    private var _saveAngle:Number;
    private var _saveMousePos:Point;
    public function SceneDragger(sprite:Sprite, rotation:Number = 0, angle:Number = 30) {
        _angle = angle;
        _rotation = rotation;
        _sprite = sprite;
        _sprite.addEventListener(MouseEvent.MOUSE_DOWN, onMsDown);
        _sprite.addEventListener(MouseEvent.MOUSE_UP, onMsUp);
    }
    private function onMsDown(e:MouseEvent):void {
        _sprite.addEventListener(MouseEvent.MOUSE_MOVE, onMsMove);
        _saveRotation = _rotation;
        _saveAngle = _angle;
        _saveMousePos = new Point(_sprite.mouseX, _sprite.mouseY);
    }
    private function onMsMove(e:MouseEvent):void {
        var dragOffset:Point = new Point(_sprite.mouseX, _sprite.mouseY).subtract(_saveMousePos);
        _rotation = _saveRotation - dragOffset.x * 0.5;
        _angle = Math.max(-89, Math.min(89, _saveAngle + dragOffset.y * 0.5));
        dispatchEvent(new Event(Event.CHANGE));
    }
    private function onMsUp(...rest):void {
        _sprite.removeEventListener(MouseEvent.MOUSE_MOVE, onMsMove);
        dispatchEvent(new Event(Event.CHANGE));
    }
    public function getPosition(distance:Number):Number3D {
        var per:Number = Math.cos(Math.PI / 180 * _angle);
        var px:Number = Math.cos(Math.PI / 180 * _rotation) * distance * per;
        var py:Number = Math.sin(Math.PI / 180 * _angle) * distance;
        var pz:Number = Math.sin(Math.PI / 180 * _rotation) * distance * per;
        return new Number3D(px, py, pz);
    }
}