3D Particle Text Effect
/**
* Copyright ThatsAsif ( http://wonderfl.net/user/ThatsAsif )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/4XIT
*/
// forked from clockmaker's Xmas Message with Twitter
/**
* ネットで投稿されたメッセージが
* クリスマスの夜空を飾ります
*
* クリスマスメッセージは
* 「Twitter」と「はてなブックマーク」で送ることができます
* ※追記:11/5(木)、Twitter仕様変更によりTwitter投稿はできなくなりました
*
* ※投稿はFlash内のボタンを押して、
* Twitterの場合は[]内にコメントを
* はてなブックマークの場合は、ブックマークコメントを記入下さい
*
* このFlashはwonderfl上の様々なテクニックを元に作られました
*
* Twitter連動
* http://wonderfl.net/code/04d6d1ae7e943b7faab88623ed83404e7a40c5f7
*
* キラキラ3D
* http://wonderfl.net/code/c0f513f31389ba07e375331256c2bfa3a8b9006c
*
* 文字のパーティクル化
* http://wonderfl.net/code/08ec1c2c8da8e9e487867a8e2ab8cddeb75f8986
*
* 木を作る再帰関数
* http://wonderfl.net/code/00cbe3ecbf0db2e865bc410a8ea54677c19ba4c6
*
* 配列のシャッフル
* http://wonderfl.net/code/8cfa39c9e91a268791142fe605c010dc30338588
*
* BetweenAS3の時間調整
* http://wonderfl.net/code/59af9d8a3cc45e4c2e6fcf12a9bc9ea55b1576e7
*
*/
package {
import flash.filters.BlurFilter;
import flash.filters.GlowFilter;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.BlendMode;
import flash.display.Sprite;
import flash.display.StageQuality;
import flash.display.StageScaleMode;
import flash.display.PixelSnapping;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.TimerEvent;
import flash.geom.Matrix;
import flash.geom.ColorTransform;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.utils.Timer;
import flash.utils.setTimeout;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.TextFieldAutoSize;
import org.libspark.betweenas3.BetweenAS3;
import org.libspark.betweenas3.easing.*;
import org.libspark.betweenas3.tweens.ITween;
import org.papervision3d.core.effects.*;
import org.papervision3d.core.math.Number3D;
import org.papervision3d.core.geom.Pixels;
import org.papervision3d.core.geom.renderables.Pixel3D;
import org.papervision3d.core.utils.Mouse3D;
import org.papervision3d.render.BasicRenderEngine;
import org.papervision3d.view.BasicView;
import org.papervision3d.view.Viewport3D;
import org.papervision3d.view.layer.BitmapEffectLayer;
import org.papervision3d.view.layer.ViewportLayer;
import org.papervision3d.view.layer.util.ViewportLayerSortMode;
import org.papervision3d.cameras.Camera3D;
import org.papervision3d.scenes.Scene3D;
import org.papervision3d.materials.ColorMaterial;
import org.papervision3d.objects.DisplayObject3D;
import org.papervision3d.objects.primitives.*;
import org.papervision3d.objects.parsers.DAE;
import org.papervision3d.events.InteractiveScene3DEvent;
import org.papervision3d.events.FileLoadEvent;
[SWF(width=590,height=300,frameRate=30,backgroundColor=0x000000)]
public class Main extends Sprite {
static private const MAX_PARTICLES:Number = 2500;
// color
public static const WHITE:uint = 0xFFF0F0F0;
public static const GREEN:uint = 0xFF008800;
public static const RED:uint = 0xFFCC0000;
public static const GOLD:uint = 0xFFFFCC66;
public static const SILVER:uint = 0xFFCCCCCC;
public static const COLORS:Array = [WHITE, GREEN, RED, GOLD, SILVER];
// 3d
private var scene:Scene3D;
private var renderer:BasicRenderEngine;
private var viewport:Viewport3D;
private var camera:Camera3D;
// 3d objects
private const MAX_RADIUS:int = 10000;
private const PIXCEL_MARGIN:Number = 1.25;
private var textPixels:Pixels;
private var bfx:BitmapEffectLayer;
private var dmyObjs:Array = [];
private var tweens:Array = [];
private var preRenderedTests:Array = [];
// 3d camera
public var cameraDistance:Number = 10;
public var cameraYaw:Number = 0;
public var cameraPitch:Number = 0;
// 2d
private var canvas:BitmapData;
private var mtx:Matrix;
// Message array
private var messages:Array = [];
// data
private var index:Number = 0;
private const RESET_TIMER:Timer = new Timer(11 * 1000);
private const PLANE_Y:int = 500;
private const FLOOR_LENGTH:int = 2000;
private var sceneWidth:int;
private var sceneHeight:int;
private var tween:ITween;
private var glowTimer:Timer;
private var loadText:TextField;
private var loadSprite:Sprite;
private var loadTimer:Timer;
public function Main(scw:int = 590, sch:int = 300):void {
stage.quality = StageQuality.MEDIUM;
stage.scaleMode = StageScaleMode.NO_SCALE;
graphics.beginFill(0x000000, 1);
graphics.drawRect(0, 0, scw, sch);
graphics.endFill();
sceneWidth = scw;
sceneHeight = sch;
setLoaderDisplay();
messages.push( { comment: "Particle Text" } );
messages.push( { comment: "betweenas3" } );
messages.push( { comment: "pv3d" } );
messages.push( { comment: "wonderfl" } );
setScene();
}
private function setLoaderDisplay():void {
loadSprite = new Sprite();
loadSprite.x = loadSprite.y = 0;
loadSprite.graphics.beginFill(0x000000, .8);
loadSprite.graphics.drawRect(0, 0, sceneWidth, sceneHeight);
loadSprite.graphics.endFill();
addChild(loadSprite);
var tf:TextFormat = new TextFormat();
tf.size = 20;
tf.font = "Arial";
tf.bold = true;
loadText = new TextField();
loadText.multiline = true;
loadText.autoSize = "left";
loadText.defaultTextFormat = tf;
loadSprite.addChild(loadText);
loadText.filters = [new GlowFilter(COLORS[1], 1, 3, 3, 5, 1, false, true)];
loadTimer = new Timer(1000);
loadTimer.addEventListener(TimerEvent.TIMER, loadTimerHandler);
setLoaderText("Loading...");
}
private function loadTimerHandler(t:TimerEvent):void {
var col:uint = COLORS[COLORS.length * Math.random() | 0];
var ct:ColorTransform = new ColorTransform();
ct.color = col;
BetweenAS3.to(loadText, {transform: {colorTransform: {redOffset: ct.redOffset, greenOffset: ct.greenOffset, blueOffset: ct.blueOffset}}}, 1, Linear.easeIn).play();
}
private function setLoaderText(str:String):void {
loadText.text = str;
loadText.x = (loadSprite.width / 2) - (loadText.width / 2);
loadText.y = (loadSprite.height / 2) - (loadText.height / 2);
setChildIndex(loadSprite, numChildren - 1);
loadSprite.visible = true;
if (!loadTimer.running){
loadTimer.start();
}
}
private function hideLoaderText():void {
loadTimer.stop();
loadSprite.visible = false;
}
private function setScene():void {
//Particles Layers and bitmap
scene = new Scene3D();
viewport = new Viewport3D(sceneWidth, sceneHeight);
camera = new Camera3D();
addChild(viewport);
bfx = new BitmapEffectLayer(viewport, sceneWidth, sceneHeight);
bfx.addEffect(new BitmapColorEffect(1, 1, 1, 0.75));
viewport.containerSprite.addLayer(bfx);
textPixels = new Pixels(bfx);
scene.addChild(textPixels);
var glowScale:Number = 4;
canvas = new BitmapData(sceneWidth / glowScale, sceneHeight / glowScale, false, 0x0000000);
var bitmap:Bitmap = new Bitmap(canvas, PixelSnapping.NEVER, true);
bitmap.scaleX = bitmap.scaleY = glowScale;
bitmap.blendMode = BlendMode.ADD;
addChild(bitmap);
mtx = new Matrix();
mtx.scale(1 / glowScale, 1 / glowScale);
camera.target = DisplayObject3D.ZERO;
renderer = new BasicRenderEngine();
var i:Number;
var arr:Array = [];
for (i = 0; i < messages.length; i++){
dmyObjs[i] = new DisplayObject3D();
while (true){
var angle:Number = 360 * Math.random();
dmyObjs[i].x = 2000 * Math.sin(angle * Number3D.toRADIANS);
dmyObjs[i].y = 2000 * (Math.random() - 0.5);
dmyObjs[i].z = 2000 * Math.cos(angle * Number3D.toRADIANS);
if (i == 0)
break;
else {
if (DisplayObject3D(dmyObjs[i]).distanceTo(dmyObjs[i - 1]) >= 500)
break;
}
}
dmyObjs[i].lookAt(DisplayObject3D.ZERO);
}
for (i = 0; i < MAX_PARTICLES; i++){
var p:Pixel3D = new Pixel3D(COLORS[COLORS.length * Math.random() | 0]);
textPixels.addPixel3D(p);
}
for (i = 0; i < messages.length; i++){
preRenderText(i);
createText(i);
}
runAnimation();
}
private function timerHandler(e:TimerEvent = null):void {
var i:Number;
var arr:Array = [];
for (i = 0; i < textPixels.pixels.length; i++){
arr[i] = BetweenAS3.serial(BetweenAS3.delay(BetweenAS3.to(textPixels.pixels[i], getRandomPos(), 3, Expo.easeInOut), Math.random()));
}
BetweenAS3.serial(BetweenAS3.parallelTweens(arr), BetweenAS3.func(function():void {
setTimeout(motionText, 2 * 1000);
})).play();
var cameraTarget:DisplayObject3D = new DisplayObject3D();
cameraTarget.copyTransform(dmyObjs[index]);
cameraTarget.moveBackward(250);
var dis:Number = cameraTarget.distanceTo(DisplayObject3D.ZERO);
var rot:Number = Math.atan2(cameraTarget.x, cameraTarget.z);
BetweenAS3.parallel(BetweenAS3.delay(BetweenAS3.bezier(this, {cameraYaw: rot, cameraDistance: dis, cameraPitch: cameraTarget.y}, null, getRandomPos(), 10.5, Expo.easeInOut), 1.0), BetweenAS3.serial(BetweenAS3.to(camera, {fov: 60}, 4, Sine.easeInOut), BetweenAS3.to(camera, {fov: 50}, 8, Sine.easeInOut))).play();
}
private function motionText():void {
var tw:ITween = BetweenAS3.parallelTweens(tweens[index]);
tw = BetweenAS3.scale(tw, 3.5 / tw.duration);
tw.play();
index++;
if (index == messages.length){
index = 0;
}
}
private function preRenderText(itemIndex:Number):void {
preRenderedTests[itemIndex] = [];
var text:TextField = new TextField();
text.multiline = true;
text.autoSize = TextFieldAutoSize.LEFT;
var size:Number = 24;
var tf:TextFormat = new TextFormat();
tf.size = size;
tf.font = "Arial";
tf.bold = true;
text.defaultTextFormat = tf;
text.text = messages[itemIndex].comment;
var twh:Number = text.textWidth * text.textHeight;
while (twh > MAX_PARTICLES){
size--;
tf.size = size;
text.defaultTextFormat = tf;
text.text = messages[itemIndex].comment;
twh = text.textWidth * text.textHeight;
}
var cap:BitmapData = new BitmapData(text.textWidth + 10, text.textHeight, true, 0xFFFFFFFF);
cap.lock();
cap.draw(text);
for (var i:Number = 0; i < text.textWidth + 10; i++){
for (var j:Number = 0; j < text.textHeight; j++){
if (cap.getPixel(i, j) == 0xFFFFFF)
continue;
if (preRenderedTests[itemIndex].length >= MAX_PARTICLES)
break;
preRenderedTests[itemIndex].push({x: i, y: j, textWidth: text.textWidth, textHeight: text.textHeight});
}
}
cap.unlock();
cap.dispose();
}
private function createText(itemIndex:Number):void {
var t:DisplayObject3D = dmyObjs[itemIndex];
var arr:Array = [];
var col:uint = COLORS[Math.floor(Math.random() * (COLORS.length))];
for (var i:Number = 0; i < preRenderedTests[itemIndex].length; i++){
var pix:Object = preRenderedTests[itemIndex][i];
var p:Pixel3D = textPixels.pixels[i];
var vec:DisplayObject3D = new DisplayObject3D();
vec.x = (pix.x - pix.textWidth / 2) * PIXCEL_MARGIN;
vec.y = 100 + (pix.textHeight / 2 - pix.y) * PIXCEL_MARGIN;
vec.z = 50;
vec.transform.calculateMultiply(t.transform, vec.transform);
arr[i] = BetweenAS3.bezier(p, {x: vec.x, y: vec.y, z: vec.z, color: col}, null, getRandomPos(), 1.2 + Math.random() * 0.25 + i * 0.002, Expo.easeOut);
}
tweens.push(arr);
}
private function loop(e:Event = null):void {
camera.x = cameraDistance * Math.sin(cameraYaw);
camera.y = cameraPitch;
camera.z = cameraDistance * Math.cos(cameraYaw);
renderer.renderScene(scene, camera, viewport);
canvas.fillRect(canvas.rect, 0x000000);
canvas.draw(viewport, mtx);
}
private function getRandomPos():Object {
return {x: MAX_RADIUS * (Math.random() - 0.5), y: MAX_RADIUS * (Math.random() - 0.5), z: MAX_RADIUS * (Math.random() - 0.5)};
}
private function runAnimation():void {
hideLoaderText();
RESET_TIMER.addEventListener(TimerEvent.TIMER, timerHandler);
RESET_TIMER.start();
timerHandler();
addEventListener(Event.ENTER_FRAME, loop);
}
}
}