Zoetrope Generator
ローカルPCのFLV、MP4からゾエトロープ的なものを生成します。
ファイルサイズは最大20MB、ファイル名マルチバイト文字NGです。
サンプルFLV http://310design.org/zoetrope/zoetrope_sample.zip
マウスのX座標によって回転速度が変わります。
DebugCamera3Dを使ってるので、マウス、キーボード操作で視点を動かせます。
@author Yukio Sato (310design.)
/**
* Copyright 310design ( http://wonderfl.net/user/310design )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/zdXg
*/
package {
import flash.media.SoundTransform;
import flash.events.DataEvent;
import flash.utils.Timer;
import flash.net.FileFilter;
import caurina.transitions.Tweener;
import org.papervision3d.materials.BitmapMaterial;
import org.papervision3d.objects.DisplayObject3D;
import org.papervision3d.objects.primitives.Cylinder;
import org.papervision3d.render.BasicRenderEngine;
import org.papervision3d.scenes.Scene3D;
import org.papervision3d.view.Viewport3D;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.MouseEvent;
import flash.events.NetStatusEvent;
import flash.events.SecurityErrorEvent;
import flash.events.TimerEvent;
import flash.geom.Matrix;
import flash.media.Video;
import flash.net.FileReference;
import flash.net.NetConnection;
import flash.net.NetStream;
import flash.net.URLRequest;
import flash.net.URLRequestMethod;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;
/**
* ローカルPCのFLV、MP4からゾエトロープ的なものを生成します。
* ファイルサイズは最大20MB、ファイル名マルチバイト文字NGです。
* サンプルFLV http://310design.org/zoetrope/zoetrope_sample.zip
* マウスのX座標によって回転速度が変わります。
* DebugCamera3Dを使ってるので、マウス、キーボード操作で視点を動かせます。
* @author Yukio Sato (310design.)
*/
[SWF(backgroundColor = 0x000000, frameRate = 30)]
public class ZoetropeGenerator extends Sprite {
private var camera:DebugCamera3DCustomized;
private var scene:Scene3D = new Scene3D();
private var viewport:Viewport3D = new Viewport3D(640, 480, true);
private var renderer:BasicRenderEngine = new BasicRenderEngine();
private var file:FileReference;
private var netstream:NetStream;
private var connection:NetConnection;
private var video:Video;
private var bmdCollection:Vector.<BitmapData> = new Vector.<BitmapData>;
private var cylinder:DisplayObject3D;
private var textField:TextField;
public var vx:Number = 0;
private static const RADIUS:Number = 800;
private static const PIC_MAX:uint = 60;
//private static const UPLOAD_URL:String = "http://localhost/data/";
private static const UPLOAD_URL:String = "http://310design.org/zoetrope/";
public function ZoetropeGenerator() {
//stage.scaleMode = StageScaleMode.NO_SCALE;
//stage.align = StageAlign.TOP_LEFT;
var format:TextFormat = new TextFormat();
format.size = 14;
format.align = TextFormatAlign.CENTER;
textField = new TextField();
textField.autoSize = TextFieldAutoSize.CENTER;
textField.selectable = false;
textField.textColor = 0xffffff;
textField.width = stage.stageWidth;
textField.defaultTextFormat = format;
textField.text = "Click and select FLV or MP4 on your computer.\r(Max 20MB and the file name must not include multibyte character.)";
textField.x = (stage.stageWidth - textField.width) * 0.5;
textField.y = (stage.stageHeight - textField.height) * 0.5;
addChild(textField);
file = new FileReference();
file.addEventListener(Event.SELECT, onSelectFile);
file.addEventListener(Event.COMPLETE, onUploadFile);
file.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
file.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
file.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, uploadCompleteDataHandler);
stage.addEventListener(MouseEvent.CLICK, browseFile);
}
private function browseFile(event:MouseEvent):void {
try {
var fileFilter:FileFilter = new FileFilter("*.flv,*.f4v,*.mp4,*.m4v", "*.flv;*.f4v;*.mp4;*.m4v");
file.browse([fileFilter]);
}catch(e:Error) {
trace(e.message);
}
}
private function onSelectFile(event:Event):void {
stage.removeEventListener(MouseEvent.CLICK, browseFile);
var request:URLRequest = new URLRequest(UPLOAD_URL + "upload.php");
request.method = URLRequestMethod.POST;
file.upload(request);
textField.text = "Uploading file...";
}
private function onError():void {
textField.text = "Fail to upload data. Try again.";
stage.addEventListener(MouseEvent.CLICK, browseFile);
}
private function ioErrorHandler(event:IOErrorEvent):void {
trace(event.text);
onError();
}
private function securityErrorHandler(event:SecurityErrorEvent):void {
trace(event.text);
onError();
}
private function netStatusHandler(e:NetStatusEvent):void {
trace(e.info.code);
if(e.info.code == "NetStream.Play.StreamNotFound") {
onError();
}
}
private function uploadCompleteDataHandler(event:DataEvent):void {
trace(event.data);
}
private function onUploadFile(event:Event):void {
connection = new NetConnection();
connection.connect(null);
netstream = new NetStream(connection);
netstream.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
netstream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
var transform:SoundTransform = netstream.soundTransform;
transform.volume = 0;
netstream.soundTransform = transform;
var customClient:Object = new Object();
netstream.client = customClient;
customClient.onMetaData = onMetaData;
video = new Video();
video.attachNetStream(netstream);
//addChild(video);
netstream.play(UPLOAD_URL + "upfiles/" + file.name);
textField.text = "Generating your zoetrope...";
}
private function onMetaData(param:Object):void {
video.width = param.width;
video.height = param.height;
var framerate:Number = param.framerate ? param.framerate : param.videoframerate;
if(isNaN(framerate)) framerate = stage.frameRate;
var count:uint = Math.floor(Math.min(param.duration * framerate, PIC_MAX));
var timer:Timer = new Timer(1000 / framerate, count);
timer.addEventListener(TimerEvent.TIMER, onTimer);
timer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete);
timer.start();
}
private function onTimer(event:TimerEvent):void {
var bmd:BitmapData = new BitmapData(video.width, video.height, false, 0);
var matrix:Matrix = new Matrix();
matrix.scale(video.width / 320, video.height / 240);
bmd.draw(video, matrix, null, null, null, true);
bmdCollection.push(bmd);
}
private function onTimerComplete(event:TimerEvent):void {
netstream.close();
var bmd360:BitmapData = new BitmapData(unitWidth * bmdCollection.length, unitHeight);
for (var i:int = 0;i < bmdCollection.length;i++) {
var source:BitmapData = bmdCollection[i];
var matrix:Matrix = new Matrix();
var scale:Number;
if(source.width / source.height > 3 / 4) {
scale = unitHeight / source.height;
matrix.scale(scale, scale);
matrix.translate((unitWidth - source.width * scale) * 0.5, 0);
} else {
scale = unitWidth / source.width;
matrix.scale(scale, scale);
matrix.translate(0, (unitHeight - source.height * scale) * 0.5);
}
var bmd:BitmapData = new BitmapData(unitWidth + 2, unitHeight);
bmd.draw(source, matrix, null, null, null, true);
var matrix2:Matrix = new Matrix();
matrix2.translate(unitWidth * i, 0);
bmd360.draw(bmd, matrix2, null, null, null, true);
}
createZoetrope(bmd360);
removeChild(textField);
}
private function createZoetrope(bmpData:BitmapData):void {
camera = new DebugCamera3DCustomized(viewport);
camera.z = -RADIUS - unitHeight * 1.8 - 50;
camera.y = unitHeight * 0.7;
camera.forceRotationX(-camera.z * 1.2 / camera.y);
camera.focus = 20;
addChild(viewport);
var bmpMaterial:BitmapMaterial = new BitmapMaterial(bmpData);
bmpMaterial.smooth = true;
bmpMaterial.doubleSided = true;
cylinder = new Cylinder(bmpMaterial, RADIUS, unitHeight, bmdCollection.length, 8, RADIUS, false, false);
scene.addChild(cylinder);
startRendering();
stage.addEventListener(MouseEvent.MOUSE_MOVE, startMove);
}
private function startMove(event:MouseEvent):void {
stage.removeEventListener(MouseEvent.MOUSE_MOVE, startMove);
addEventListener(Event.ENTER_FRAME, enterFrame);
}
private function enterFrame(event:Event):void {
var maxVX:Number = 360 / bmdCollection.length * 1.5;
var targetVX:Number;
if(Math.abs(mouseX - stage.stageWidth * 0.5) < 50) {
targetVX = 0;
} else if(mouseX > stage.stageWidth / 2) {
targetVX = (mouseX - stage.stageWidth * 0.5 - 50) / (stage.stageWidth * 0.5 - 50) * maxVX;
} else {
targetVX = (mouseX - stage.stageWidth * 0.5 + 50) / (stage.stageWidth * 0.5 - 50) * maxVX;
}
Tweener.addTween(this, {vx:targetVX, time:1, transition:"easeOutCubic", onUpdate:rotateCylinder});
}
public function rotateCylinder():void {
cylinder.rotationY += vx;
}
public function startRendering():void {
addEventListener(Event.ENTER_FRAME, onRenderTick);
viewport.containerSprite.cacheAsBitmap = false;
}
protected function onRenderTick(event:Event = null):void {
renderer.renderScene(scene, camera, viewport);
}
public function get unitWidth():Number {
//return RADIUS * 2 * Math.sin(Math.PI / bmdCollection.length);
return RADIUS * 2 * Math.PI / bmdCollection.length;
}
public function get unitHeight():Number {
return Math.floor(unitWidth * 4 / 3);
}
}
}
import org.papervision3d.cameras.DebugCamera3D;
import org.papervision3d.view.Viewport3D;
class DebugCamera3DCustomized extends DebugCamera3D {
public function DebugCamera3DCustomized(viewport3D:Viewport3D, fovY:Number = 90, near:Number = 10, far:Number = 5000) {
super(viewport3D, fovY, near, far);
}
public function forceRotationX(rot:Number):void {
startRotationX = targetRotationX = super.rotationX = rot;
}
}