Sea of Clouds(フルスクリーン)
雲海のテスト
* クリックでカメラを切り替えられます(操作はできません)
* BitmapData.draw()を毎フレーム何十回もやってるので結構重いです
* 雲の数を200~300にするといい感じに雲海になるんですが重すぎて断念
*
* (追加修正)
* ・カメラを少し調整しました
* ・初期化時の雲画像生成が重いのでプログレスバーをつけました
* ・ごく稀に穴だらけの雲が出来るバグを修正(なぜかperlinNoiseのシード値に7185を渡すと1つのチャンネルが穴だらけに・・・)
* ・低画質で遠景画像を傾けた時のぶれを修正
*
* (いじってみた)
* ・フルスクリーンにしてみた!
* ・雲も増やしたので重いよ!
/**
* Copyright fumix ( http://wonderfl.net/user/fumix )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/sRn5
*/
// forked from tencho's Sea of Clouds
/**
* 雲海のテスト
* クリックでカメラを切り替えられます(操作はできません)
* BitmapData.draw()を毎フレーム何十回もやってるので結構重いです
* 雲の数を200~300にするといい感じに雲海になるんですが重すぎて断念
*
* (追加修正)
* ・カメラを少し調整しました
* ・初期化時の雲画像生成が重いのでプログレスバーをつけました
* ・ごく稀に穴だらけの雲が出来るバグを修正(なぜかperlinNoiseのシード値に7185を渡すと1つのチャンネルが穴だらけに・・・)
* ・低画質で遠景画像を傾けた時のぶれを修正
*
* (いじってみた)
* ・フルスクリーンにしてみた!
* ・雲も増やしたので重いよ!
*/
package {
import flash.display.StageScaleMode;
import flash.display.StageAlign;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.filters.GlowFilter;
import flash.geom.ColorTransform;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.utils.getTimer;
import net.hires.debug.Stats;
import org.papervision3d.core.geom.renderables.Vertex3D;
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.Papervision3D;
import org.papervision3d.view.BasicView;
[SWF(width = "465", height = "465", frameRate = "30", backgroundColor = "#000000")]
public class Cloud extends Sprite {
private var PI:Number;
private var time:int = 0;
//雲の数
private var cloudNum:int = 200;
private var loading:LoadingScene;
private var loadCount:int = 0;
private var bv:BasicView;
private var cameraTarget:DisplayObject3D;
private var horizonTarget:DisplayObject3D;
private var plane:DisplayObject3D;
private var smokes:Vector.<Smoke>;
private var cameraRotation:Number = 0;
private var cameraAngle:Number = 100;
private var cameraRoll:Number = 0;
private var cameraDist:Number = 500;
private var campus1:BitmapData;
private var campus2:BitmapData;
private var farCloud:Sprite;
private var bg:BackGround;
private var sea:Sprite;
private var cameraMode:int = 0;
private var planeMaterial:ColorMaterial;
private var wingMaterial:ColorMaterial;
private var startTime:int;
//コンストラクタ
public function Cloud() {
Wonderfl.capture_delay(7);
//stage関連の設定
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
//フルスクリーンに!
var _w : int = stage.stageWidth;
var _h : int = stage.stageHeight;
Param.init(_w, _h);
PI = Math.PI / 180;
stage.frameRate = 60;
stage.quality = "low";
Papervision3D.PAPERLOGGER.unregisterLogger(Papervision3D.PAPERLOGGER.traceLogger);
bv = new BasicView(Param.display.width, Param.display.height, false, false, "Free");
planeMaterial = new ColorMaterial(Param.planeColor, 1);
wingMaterial = new ColorMaterial(Param.planeColor, 1);
wingMaterial.doubleSided = true;
cameraTarget = bv.scene.addChild(new DisplayObject3D());
horizonTarget = bv.scene.addChild(new DisplayObject3D());
horizonTarget.autoCalcScreenCoords = true;
addChild(Painter.createGradientRect(Param.display.width, Param.display.height, [Param.skyColor1], [1]));
sea = addChild(Painter.createGradientRect(Param.display.width, Param.display.height, [Param.seaColor], [1])) as Sprite;
bg = addChild(new BackGround()) as BackGround;
bg.x = Param.display.width / 2;
bg.mask = addChild(Painter.createGradientRect(Param.display.width, Param.display.height, [0x000000], [1]));
campus1 = new BitmapData(Param.display.width, Param.display.height, true, 0x00FFFFFF);
campus2 = new BitmapData(Param.display.width, Param.display.height, true, 0x00FFFFFF);
addChild(new Bitmap(campus1));
addChild(bv.viewport);
addChild(new Bitmap(campus2));
addChild(new Stats());
//雲生成
smokes = new Vector.<Smoke>();
for (var i:int = 0; i < cloudNum; i++) {
var smoke:Smoke = new Smoke(bv.scene);
smokes.push(smoke);
smoke.setPosition(Math.random() * 3500 - 1750, Math.random() * -300 + 10000, Math.random() * 4000 - 2000);
}
plane = bv.scene.addChild(createAirPlain());
plane.rotationY = -90;
plane.y = 10000;
plane.scale = 0.5;
plane.autoCalcScreenCoords = true;
bv.viewport.filters = [new GlowFilter(Param.outlineColor, 1, 3, 3, 6, 1)];
//
loading = addChild(new LoadingScene()) as LoadingScene;
addEventListener(Event.ENTER_FRAME, onLoading);
}
private function onClick(e:MouseEvent):void {
cameraMode = (cameraMode + 1) % 5;
}
//画像生成中
private function onLoading(...arg):void {
for (var i:int = 0; i < 3; i++) {
if(loadCount >= smokes.length){
removeEventListener(Event.ENTER_FRAME, onLoading);
start()
break;
}
var smoke:Smoke = smokes[loadCount];
smoke.changeImage();
loadCount++;
loading.setPercentage(loadCount / smokes.length);
}
}
//レンダリング開始
private function start():void {
stage.addEventListener(MouseEvent.CLICK, onClick);
startTime = getTimer();
removeChild(loading);
addEventListener(Event.ENTER_FRAME, onEnter);
onEnter();
}
//毎フレーム処理
private function onEnter(...arg):void {
time = getTimer() - startTime + 7000;
plane.rotationZ = Math.sin(PI * time / 150) * 30;
var area:int = 2000;
//雲を動かす
for each(var sm:Smoke in smokes) {
sm.do3d.z = ((sm.startPosition.z + time * 0.42) + area) % (area * 2) - area;
var loop:int = int(((sm.startPosition.z + time * 0.42) + area) / (area * 2));
if (sm.loop != loop) {
sm.loop = loop;
sm.do3d.x = Math.random() * 3500 - 1750;
sm.do3d.y = Math.random() * -300 + 10000;
}
}
//カメラ移動
switch(cameraMode) {
case 0:
cameraDist = Math.sin(PI * time / 80) * 200 + 600;
cameraRotation = time / 1000 * 60 * 0.2;
cameraRoll = Math.sin(PI * time / 50) * 20;
cameraAngle = Math.sin(PI * time / 100) * 100 + 50;
cameraTarget.position = new Number3D(0, 10000, 0);
break;
case 1:
cameraDist = 280;
cameraRotation = Math.sin(PI * time / 110) * 20 + 90;
cameraRoll = -plane.rotationZ;
cameraAngle = Math.sin(PI * time / 90) * 50 + 30;
cameraTarget.position = new Number3D(0, 10000, -10000);
break;
case 2:
cameraDist = 500;
cameraRotation = Math.sin(PI * time / 110) * 10;
cameraRoll = 0;
cameraAngle = Math.sin(PI * -time / 70) * 200;
cameraTarget.position = new Number3D(0, 10000, 0);
break;
case 3:
cameraDist = 800;
cameraRotation = -time / 200;
cameraRoll = 0;
cameraAngle = -400;
cameraTarget.position = new Number3D(0, 10000, 0);
break;
case 4:
cameraDist = 50;
cameraRotation = 90;
cameraRoll = -plane.rotationZ/2;
cameraAngle = 30 + plane.rotationZ;
cameraTarget.position = new Number3D(1000, 10000, -700);
break;
}
bv.camera.position = new Number3D(Math.cos(PI*cameraRotation)*cameraDist, cameraAngle+10000, Math.sin(PI*cameraRotation)*cameraDist);
bv.camera.lookAt(cameraTarget);
bv.camera.roll(cameraRoll);
var crot:Number = Math.atan2(cameraTarget.z - bv.camera.z, cameraTarget.x - bv.camera.x);
//地平線用ダミーモデルをカメラの前方に配置
horizonTarget.position = new Number3D(Math.cos(crot) * 500000, 0, Math.sin(crot) * 500000);
//PV3Dレンダリング
bv.renderer.renderScene(bv.scene, bv.camera, bv.viewport);
//背景画像を地平線の高さに移動
bg.y = int(horizonTarget.screen.y + Param.center.y);
bg.rotation = cameraRoll;
bg.setPositionPer(crot / PI / 360);
sea.visible = horizonTarget.screen.y + Param.center.y < Param.display.height;
//雲画像処理
campus1.fillRect(Param.display, 0x00FFFFFF);
campus2.fillRect(Param.display, 0x00FFFFFF);
smokes.sort(function(a:Smoke, b:Smoke):Number { return int(a.scale > b.scale) - int(a.scale < b.scale); } );
for each(var smoke:Smoke in smokes) {
smoke.refresh();
if (smoke.do3d.screen.z > 10 && smoke.alpha) {
var target:BitmapData = (plane.screen.z < smoke.do3d.screen.z)? campus1 : campus2;
var ct:ColorTransform = new ColorTransform(1, 1, 1, smoke.alpha, 0, 0, 0, 0);
var mat:Matrix = new Matrix();
mat.scale(smoke.scale, smoke.scale);
mat.rotate(PI * bv.camera.localRotationZ);
var offsetX:Number = smoke.scale * smoke.imageSize.width / 2;
var offsetY:Number = smoke.scale * smoke.imageSize.height / 2;
var xp:Number = smoke.do3d.screen.x + Param.center.x - (Math.cos(PI * cameraRoll) * offsetX - Math.sin(PI * cameraRoll) * offsetY);
var yp:Number = smoke.do3d.screen.y + Param.center.y - (Math.sin(PI * cameraRoll) * offsetX + Math.cos(PI * cameraRoll) * offsetY);
mat.translate(xp, yp);
target.draw(smoke.image, mat, ct);
}
}
}
//航空機モデルを生成
private function createAirPlain():DisplayObject3D {
var i:int;
var plane:DisplayObject3D = new DisplayObject3D();
var d:DisplayObject3D = plane.addChild(new DisplayObject3D());
var body:Plane = new Plane(planeMaterial, 446, 46, 3, 2);//23
for (i = 0; i <= 2; i++) body.geometry.vertices[i].y = [-12, -6, 0][i];
for (i = 3; i <= 5; i++) body.geometry.vertices[i].x = -178;
for (i = 6; i <= 8; i++) body.geometry.vertices[i].x = 77;
for (i = 9; i <= 11; i++) body.geometry.vertices[i].y = [7, 15, 23][i-9];
for (i = 4; i <= 7; i += 3) body.geometry.vertices[i].z = -22;
var wing:Plane = new Plane(wingMaterial, 190, 96, 2, 1);
for (i = 0; i <= 5; i++) wing.geometry.vertices[i].x = [0, 64, 190][int(i / 2)];
for (i = 0; i <= 5; i++) wing.geometry.vertices[i].y = [0, 96, 53, 117, 165, 190][i];
for (i = 2; i <= 5; i++) wing.geometry.vertices[i].z = [ -5, -12][int((i - 2) / 2)];
wing.position = new Number3D( -86, -10, -10);
wing.rotationZ = -90;
wing.rotationY = 90;
var wing2:Plane = new Plane(wingMaterial, 66, 72, 1, 1);
for (i = 0; i <= 3; i++) {
wing2.geometry.vertices[i].x = [0, 0, 72, 72][i];
wing2.geometry.vertices[i].y = [0, 66, 72, 100][i];
}
wing2.rotationZ = -90;
wing2.rotationY = 90;
wing2.position = new Number3D(148,10,0);
var wing3:Plane = new Plane(wingMaterial, 82, 64, 1, 1);
for (i = 0; i <= 3; i++) {
wing3.geometry.vertices[i].x = [0, 82, 82, 110][i];
wing3.geometry.vertices[i].y = [0, 0, 64, 64][i];
}
wing3.position = new Number3D(130, 21, 0);
d.addChild(body);
d.addChild(wing);
d.addChild(wing2);
var engine1:Cube = d.addChild(new Cube(new MaterialsList( { all:planeMaterial } ), 48, 16, 16, 1, 1, 1)) as Cube;
var engine2:Cube = d.addChild(new Cube(new MaterialsList( { all:planeMaterial } ), 48, 16, 16, 1, 1, 1)) as Cube;
engine1.position = new Number3D(-20, -15, 64);
engine2.position = new Number3D(40, -15, 137);
engine1.rotationX = engine2.rotationX = 45;
plane.addChild(wing3);
var frip:DisplayObject3D = plane.addChild(d.clone());
frip.scaleZ *= -1;
for each(var model:DisplayObject3D in frip.children) model.geometry.flipFaces();
return plane;
}
}
}
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.display.Stage;
import flash.events.Event;
import flash.filters.BevelFilter;
import flash.filters.BlurFilter;
import flash.filters.ColorMatrixFilter;
import flash.filters.GlowFilter;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import org.papervision3d.core.math.Number3D;
import org.papervision3d.objects.DisplayObject3D;
import org.papervision3d.scenes.Scene3D;
//各種パラメータ
class Param {
static public var center:Point;
static public var display:Rectangle;
//航空機の色
static public var planeColor:uint = 0xFFFFFF;
//航空機のアウトライン
static public var outlineColor:uint = 0x40699E;
//雲の色
static public var cloudColor:uint = 0xFFD7DDE5;
//雲のハイライト色
static public var lightColor:uint = 0xFFFFFF;
//雲の影の色
static public var shadowColor:uint = 0x294A59;
//空の色(上)
static public var skyColor1:uint = 0x1F437F;
//空の色(下)
static public var skyColor2:uint = 0x6290C1;
//地平線付近の色
static public var horizonColor:uint = 0xA0BCD1;
//地上の色
static public var seaColor:uint = 0x7D8C98;
public function Param() { }
static public function init(w:int,h:int):void {
display = new Rectangle(0, 0, w, h);
center = new Point(display.width / 2, display.height / 2);
}
}
class Painter {
public function Painter() { }
//雲画像生成
static public function createCloud(width:int, height:int, type:Boolean = true):BitmapData {
var gradiation:Sprite = new Sprite();
var drawMatrix:Matrix = new Matrix();
drawMatrix.createGradientBox(width, height);
gradiation.graphics.beginGradientFill("radial", [0x000000, 0x000000], [0, 1], [0, 255], drawMatrix);
gradiation.graphics.drawRect(0, 0, width, height);
gradiation.graphics.endFill();
var alphaBmp:BitmapData = new BitmapData(width, height);
var seed:int = int(Math.random() * 10000);
//seed=7185の時にperlinNoiseの1つのチャンネルに
//ブロック状の穴が発生するためグレースケールは選ばないように変更
alphaBmp.perlinNoise(width / 3, height / 2.5, 7, seed, false, true, 7, false);
var zoom:Number = (type)? 1.8 : 1.1;
var ct:Number = (type)? 1.6 : 0.2;
var ctMatrix:Array = [ct + 1, 0, 0, 0, -(128 * ct), 0, ct + 1, 0, 0, -(128 * ct), 0, 0, ct + 1, 0, -(128 * ct), 0, 0, 0, 1, 0];
alphaBmp.draw(gradiation, new Matrix(zoom, 0, 0, zoom, -(zoom - 1) / 2 * width, -(zoom - 1) / 2 * height));
alphaBmp.applyFilter(alphaBmp, alphaBmp.rect, new Point(), new ColorMatrixFilter(ctMatrix));
var image:BitmapData = new BitmapData(width, height, true, Param.cloudColor);
//赤チャンネルは穴だらけになる可能性があるので別のチャンネルを使うように変更
image.copyChannel(alphaBmp, alphaBmp.rect, new Point(), 2, 8);
image.applyFilter(image, image.rect, new Point(), new GlowFilter(Param.lightColor, 1, 4, 4, 1, 3, true));
var bsize:Number = Math.min(width, height) / 30;
image.applyFilter(image, image.rect, new Point(), new BevelFilter(bsize, 45, Param.lightColor, 1, Param.shadowColor, 1, bsize/5, bsize/5, 1, 3));
alphaBmp.dispose();
return image;
}
//グラデーションスプライト生成
static public function createGradientRect(w:Number, h:Number, colors:Array, alphas:Array, ratios:Array = null, r:Number = 0):Sprite {
var rts:Array = new Array();
if(ratios == null){
for (var n:int = 0; n < colors.length; n++) rts.push(int(255 * n / (colors.length - 1)));
} else {
for (var m:int = 0; m < ratios.length; m++) rts[m] = Math.round(ratios[m] * 255);
}
var sp:Sprite = new Sprite();
var mat:Matrix = new Matrix();
mat.createGradientBox(w, h, Math.PI / 180 * r, 0, 0);
if (colors.length == 1 && alphas.length == 1) sp.graphics.beginFill(colors[0], alphas[0]);
else sp.graphics.beginGradientFill("linear", colors, alphas, rts, mat);
sp.graphics.drawRect(0, 0, w, h);
sp.graphics.endFill();
return sp;
}
}
//遠景画像
class BackGround extends Sprite {
private var farWidth:int = 2000;
private var farHeight:int = 20;
private var viewWidth:int = 600;
private var viewImage:BitmapData;
private var farImage:BitmapData;
public function BackGround() {
var sky:Sprite = addChild(Painter.createGradientRect(Param.display.width*2, Param.display.height, [Param.skyColor1, Param.skyColor2, Param.horizonColor], [1, 1, 1], [0, 0.9, 1], 90)) as Sprite;
sky.x = -Param.display.width;
sky.y = -Param.display.height;
var ground:Sprite = addChild(Painter.createGradientRect(Param.display.width*2, Param.display.height, [Param.horizonColor, Param.seaColor], [1, 1], null, 90)) as Sprite;
ground.y = -1;
ground.x = -Param.display.width;
//遠景画像作成
farImage = new BitmapData(farWidth + viewWidth, farHeight, true, 0x00FFFFFF);
for (var n:int = 0; n < 40; n++) {
var cloud:BitmapData = Painter.createCloud(80, 20, true);
farImage.copyPixels(cloud, cloud.rect, new Point(Math.random() * 1960, farHeight - 20), null, null, true);
}
farImage.applyFilter(farImage, farImage.rect, new Point(), new BlurFilter(2, 2, 1));
farImage.copyPixels(farImage, new Rectangle(0, 0, viewWidth, farHeight), new Point(farWidth, 0));
viewImage = new BitmapData(viewWidth, farHeight, true, 0x88FFFFFF);
var img:Bitmap = addChild(new Bitmap(viewImage)) as Bitmap;
img.x = -viewWidth / 2;
img.y = -farHeight;
}
public function setPositionPer(per:Number):void {
per = ((per % 1) +1) % 1;
var px:int = int(farWidth * (1 - per));
viewImage.copyPixels(farImage, new Rectangle(px, 0, viewWidth, farHeight), new Point(0, 0));
}
}
//画像生成中画面
class LoadingScene extends Sprite {
private var lineWidth:Number = 200;
private var loadedLine:Sprite;
public function LoadingScene() {
var bg:Sprite = addChild(Painter.createGradientRect(Param.display.width, Param.display.height, [0x000000], [1])) as Sprite;
var baseLine:Sprite = addChild(Painter.createGradientRect(lineWidth, 2, [0x444444], [1])) as Sprite;
loadedLine = addChild(Painter.createGradientRect(lineWidth, 2, [0x7DA3C8], [1])) as Sprite;
baseLine.x = loadedLine.x = int((Param.display.width - lineWidth) / 2);
baseLine.y = loadedLine.y = int((Param.display.height - baseLine.height) / 2);
setPercentage(0);
}
public function setPercentage(per:Number):void {
loadedLine.width = lineWidth * per;
}
}
//雲
class Smoke {
public var do3d:DisplayObject3D;
public var image:BitmapData;
public var imageSize:Rectangle;
public var alpha:Number;
public var scale:Number;
public var loop:int;
public var startPosition:Number3D;
public function Smoke(scene:Scene3D) {
do3d = scene.addChild(new DisplayObject3D());
do3d.autoCalcScreenCoords = true;
do3d.position = new Number3D();
imageSize = new Rectangle(0, 0, 400, 200);
}
public function changeImage():void {
image = Painter.createCloud(imageSize.width, imageSize.height, Math.random() > 0.3);
}
//雲の初期位置を設定
public function setPosition(x:Number, y:Number, z:Number):void {
startPosition = new Number3D(x, y, z);
do3d.position = startPosition.clone();
}
//スケールと透明度を更新
public function refresh():void {
scale = 700 / do3d.screen.z;
var per1:Number = Math.max(0, Math.min(1, do3d.screen.z / 100));
var per2:Number = Math.max(0, Math.min(1, 1 - (do3d.screen.z - 1500) / 800));
alpha = per1 * per2 * 2;
}
}