RadioheadのプロモーションがGoogle Codeで行われている理由
RadioheadがGoogle Codeで"House of Cards"のPVを公開しています。
http://code.google.com/creative/radiohead/
このPVはビデオカメラをいっさい使わず、レーザースキャニング技術を使った3Dデータをもとに構成されており、
Flash版のViewerでは、リアルタイムに視点を変えながら再生することもできます。
http://code.google.com/creative/radiohead/viewer.html
さらに、この3DデータはCSV形式で公開されており、誰でもこのデータを使って作品を作ることができるようになっています。
http://code.google.com/p/radiohead/downloads/list
Processingのサンプルはダウンロードできますが、Flash版はなかったので、簡単なサンプルを作ってみました。
サンプルとソースは以下の通り。
Radiohead "House of Cards" Sample
なるべく単純なサンプルにするために、3Dの描画はPapervision3Dなどは使わず、もっとも単純な透視投影の計算結果をBitmap.setPixelを使って描画するだけにしました。マウス移動も視点が平行移動するだけ、マウスクリックによるモードの切り替えも単に毎フレーム描画をリフレッシュするかどうかという違いだけです。
公開されているCSVデータは毎フレーム毎に1ファイルになっていて、1ファイルの各行が描画点の
x,y,z,濃度
というデータになっています。1ファイルで12,000行くらいあるので、このサンプルでは255フレーム目から12フレーム分だけを描画しています。
なお、複数ファイルの読み込みには以前紹介したQueueクラスを利用しています。
import flash.display.*;
import flash.events.*;
import flash.geom.Rectangle;
import flash.net.*;
import org.as3s.*;
[SWF(width='600',height='400',backgroundColor='0x000000',frameRate='30')]
public class Radiohead extends Document {
private var queue:Queue;
private var loaderList:Array;
private var loaderCount:int = 0;
private var image:Bitmap;
private var bitmap:BitmapData;
private var r:Number = 500;
private var start:int = 255;
private var total:int = 12;
private var count:int = 0;
private var scale:Number = 1.5;
private var afterglow:Boolean = false;
public function Radiohead() {
Document.scaleMode = StageScaleMode.NO_SCALE;
bitmap = new BitmapData(600, 400, false, 0x000000);
image = new Bitmap(bitmap);
addChild(image);
queue = new Queue();
queue.addEventListener(Queue.PROGRESS, onProgress);
queue.addEventListener(Queue.COMPLETE, onComplete);
loaderList = new Array();
for (var i:int=0; i
var loader:URLLoader = new URLLoader();
loaderList.push(loader);
queue.put(onLoadComplete);
}
loaderCount = 0;
Document.addEventListener(Event.ENTER_FRAME, load);
Document.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
}
private function load(event:Event):void {
var loader:URLLoader = loaderList[loaderCount];
try {
loader.addEventListener(Event.COMPLETE, onLoadComplete);
loader.addEventListener(IOErrorEvent.IO_ERROR, onLoadIOError);
loader.load(new URLRequest("csv/"+(start+loaderCount)+".csv"));
} catch (error:Error) {
queue.get(onLoadComplete);
}
if (++loaderCount>=total) {
Document.removeEventListener(Event.ENTER_FRAME, load);
}
}
private function onMouseDown(event:MouseEvent):void {
afterglow = !afterglow;
}
private function onLoadComplete(event:Event):void {
var data:String = event.target.data;
var lines:Array = data.split("\n");
var dataList:Array = new Array();
for each(var line:String in lines) {
var words:Array = line.split(",");
if (words.length==4) {
dataList.push({x:words[0]*scale, y:words[1]*scale, z:words[2]*scale, i:words[3]*1.3});
event.target.data = dataList;
}
}
queue.get(onLoadComplete);
}
private function onLoadIOError(event:Event):void {
queue.get(onLoadComplete);
}
private function onProgress(event:Event):void {
bitmap.fillRect(new Rectangle(0, 200, (queue.total-queue.length)/queue.total*600, 1), 0xffffff);
}
private function onComplete(event:Event):void {
bitmap.fillRect(new Rectangle(0, 0, 600, 400), 0x000000);
queue.removeEventListener(Queue.COMPLETE, onComplete);
Document.addEventListener(Event.ENTER_FRAME, update);
}
private function draw(dataList:Array):void {
for each (var p:Object in dataList) {
bitmap.setPixel((p.x-this.mouseX+200)*r/(r-p.z)+300, (p.y-this.mouseY)*r/(r-p.z)+200, p.i<<16 | p.i<<8 | p.i);
}
}
private function update(event:Event):void {
if (!afterglow) bitmap.fillRect(new Rectangle(0, 0, 600, 400), 0x000000);
draw(loaderList[count].data);
count = (count+1)%loaderList.length;
}
}
}