2008.04 Monthly archives

[Away3D] View3D

2008.04.30

オレ向けメモの続き。表示系の土台たるView3Dを押さえてみる。
クラス名の雰囲気的にPV3DのViewport3Dと同じものかと思っちゃうけど、どちらかというとBasicViewクラスに近い。

View3Dを生成すると、主なものとして

  • Scene3D
  • カメラ
  • レンダラ
  • AbstractRenderSession
  • クリッピング領域
  • hud、interactiveLayer、backgroundという3つのSprite

が定義される。
このうちhud、interactiveLayer、backgroundについては下記のようになってる。

View3dのコンテナ構造

3D空間のレンダリング結果が描画される_session.container(AbstractRenderSession.container)を挟む形で、画面手前からhud、interactiveLayer、_session.container、backgroundの順。hudは、3D空間より手前に表示したいもの(ゲームとかだと体力ゲージとか、エースコンバットみたいなものならまさにHUDを)をここにaddChildしとけってことか。

逆にbackgroundは、3D空間よりも奥にあって、遠景の画像とかを配置するみたいな使い方かな。interactiveLayerは、MovieMaterialに割り当てたMovieClipがインタラクティブ要素を含む場合に使われてるみたいなんだけどイマイチわかんね。パッと見た感じ_session.containerには外部からアクセスできないみたいなので、PV3DのマルチViewportみたいなことは、とりあえずムリかもしれない。

View3D自体もSpriteを継承していて、3D空間ごとStageに配置するようなイメージ。View3Dの原点は(矩形領域と考えて)左上ではなく(矩形領域の)中心になるのでView3Dのx、y座標を定義する場合に注意。例えばステージのセンターにView3Dを配置する場合は

var view:View3D = new View3D();
view.x:stage.stageWidth/2;
view.y:stage.stageHeight/2;

もしくは

var view:View3D = new View3D({x:stage.stageWidth/2, y:stage.stageHeight/2});

てな感じに。
hudやbackgroundも、つい左上が原点と考えがちだけど、これらも中心が原点になる。

コンストラクタへの引数はオブジェクトで渡す。表記は冗長になるけどコードを見た時にパラメータの意味が把握しやすい。

View3D({scene:hoge, camera:fuga, stats:false, x:500, y:300});

引数は下記のような感じ。

stats:Boolean[true]… ステータスパネルのon/off
scene:Scene3D[new Scene3D()]… Scene3D設定
camera:Camera3D[new Camera3D({x:0, y:0, z:1000, lookat:"center"}]… カメラ設定
renderer:IRenderer[new BasicRenderer()]… レンダラ設定
session:AbstractRenderSession[new SpriteRenderSession()]… RenderSession設定
clip:Clipping[defaultclip / new Clipping()]… クリッピングする矩形領域の設定
x:Number[0]
y:Number[0]

※[]内はデフォルト値

【stats】swf再生時に3Dオブジェクトを[ctrl] + クリック(Windows環境なら右クリック)で表示されるコンテキストメニューからステータスパネルを呼び出す機能を活かすかどうか。

var view:View3D = new View3D({stats:false});

で、ステータスパネルを無効化。

【scene】通常デフォルトのままでいいと思うけど、Scene3Dのコンストラクタでphysicsと、tickTraverserが設定できるので、ここを指定したい場合なんかに

var scene3d:Scene3D = new Scene3D({physics});
var view:View3D = new View3D({scene:scene3d});

とかいう感じで設定するんだろうけど、肝心のphysicsと、tickTraverserについてが全くわかんね。とりあえず保留。

【camera】Camera3D以外のカメラ使う場合は必ず指定。

var targetCamera:TargetCamera3D = new TargetCamera3D();
var view:View3D = new View3D({camera:targetCamera});

【renderer】デフォルトのBasicRenderer以外に、QuadrantRendererってのがあって、空間分割最適化されたレンダラか?とか思ったけどよくわからん。追記ねぎミクで遊んだ時に調べた事を完全に忘れて何をすっとぼけてるのかオレは…。レンダラについては別途まとめる。
あと、以前デモで使ったFogFilterのようなフィルタ類を適応したい場合も任意に指定する必要がある。

var fog:FogFilter = new FogFilter({material:new ColorMaterial(0xFFFFFF,{alpha:0.1}),minZ:5000,maxZ:20000});
var rend:BasicRenderer = new BasicRenderer(fog);
var view:View3D = new View3D({renderer:rend});

【session】PV3DのRenderSessionと同じようなもの(むしろPV3Dが取り入れた)と考えてたんだけど、Away3DはRenderSessionで描画処理を一局集中で行ってるみたい(PV3Dだと描画系の処理は各マテリアルで行う)なので全然違うシロモノかもしれない。よーわからんッス。

【clip】通常はデフォルト値で問題無いと思うけど、より狭い範囲で頂点クリップしたい場合は任意に指定する。

var rc:RectangleClipping = new RectangleClipping(-500,-500,500,500);
var view:View3D = new View3D({clip:rc});

※デフォルトは、RectangleClipping(-1000000,-1000000,1000000,1000000)

View3Dはこんな感じか。
それにしても、Away3DはASDocがほとんど役に立ちませんな

[Away3D] HelloAway3D

2008.04.22

とりあえず何か表示させるための基本を押さえる。
svnリポジトリの trunk > examples > HelloAway3D 超基本サンプルがあるんだけど、かなり以前のリビジョンベースのままずっと放置されてて、Away3D2.0だとコンパイルエラーになる。なんという一見さんお断りっぷり。
動くようにしたのが下記。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package
{
    import flash.display.*;
    import flash.events.*;
    import flash.utils.*;
 
    import away3d.containers.*;
    import away3d.primitives.*;
    import away3d.materials.*;
 
    [SWF(backgroundColor="#222266", frameRate="60", width="600", height="400")]
    public class HelloAway3D extends Sprite
    {
        public var view:View3D;
        public var sphere:Sphere;
 
        public function HelloAway3D()
        {
            view = new View3D();
            view.x = 300;
            view.y = 200;
            addChild(view);
 
            var wcMat:WireColorMaterial = new WireColorMaterial(0xFF7700, {wirecolor:0xCC4400})
            sphere = new Sphere({material:wcMat, radius:250, segmentsW:12, segmentsH:9, y:50});
 
            view.scene.addChild(sphere);
            view.camera.lookAt(sphere.position);
 
            addEventListener(Event.ENTER_FRAME, onEnterFrame);
        }
 
        private function onEnterFrame(event:Event):void
        {
            view.render();
            sphere.rotationY = getTimer() / 100;
        }
 
    }
}
  • とくにかくView3Dを生成する。
  • View3Dオブジェクトに内包される形で、scene と camera が生成される。そこだけ承知しとけばPV3Dと流れは変わらない。
  • View3D.render()でレンダリング。
  • View3Dと、PV3DのViewport3Dは似て非なるものなので注意。

view.scene とか、view.camera とかいう記述がイマイチしっくりこないんで、もうちょっとPV3Dライクに。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package
{
    import flash.display.*;
    import flash.events.*;
 
    import away3d.containers.View3D;
    import away3d.cameras.TargetCamera3D;
    import away3d.primitives.Sphere;
    import away3d.materials.WireColorMaterial;
 
    [SWF(backgroundColor="#000000")]
 
    public class Away3DBasic extends View3D
    {
        private var sphere:Sphere;
 
        public function Away3DBasic()
        {
            stage.frameRate = 60;
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.quality = StageQuality.MEDIUM;
 
            stage.addEventListener(Event.RESIZE, onStageResize);
            addEventListener(Event.ENTER_FRAME, onEnterFrame);
 
            //View3D init
            var targetCamera:TargetCamera3D = new TargetCamera3D();
            super({camera:targetCamera, x:stage.stageWidth/2, y:stage.stageHeight/2});
 
            //Camera Setting
            camera.z = -2500;
            camera.focus = 500;
            camera.zoom = 12;
 
            //Material Setting
            var wcMat:WireColorMaterial = new WireColorMaterial(0x0099CC, {wirecolor:0x00CCFF});
 
            //3DObject Setting
            sphere = new Sphere({material:wcMat, radius:100, segmentsW:12, segmentsH:9});
            scene.addChild(sphere);
        }
 
        //Render Loop
        private function onEnterFrame(event:Event):void
        {
            //rotation Sphere
            sphere.rotationY += 0.5;
 
            //Rendering
            render();
        }
 
        private function onStageResize(event:Event):void
        {
            this.x = stage.stageWidth >> 1;
            this.y = stage.stageHeight >> 1;
        }
 
    }
}

View3Dの定義時に、わざわざTargetCamera3Dを設定してるのは、デフォルトのCamera3DがPV3DでいうところのFreeCamera3Dだから。動作確認のためのデモをゴリゴリ作る場合は、カメラがワールド原点注視しっぱなしのほうが個人的に楽なので。

[Away3D] Matrix3D / Number3D / Quaternion

2008.04.21

Away3Dに関するオレのオレによるオレのための覚え書き開始。
地味だけど座標変換のキモとなる Matrix3D と Number3Dから。それと、理解が追いつかない Quaternion もついでに。

Matrix3D

まずはMatrix3D。行列を扱うクラス。

コンストラクタ

import away3d.core.math.Matrix3D;

var matrix:Matrix3D = new Matrix3D();

PV3Dの

var matrix:Matrix3D = Matrix3D.IDENTITY;

と等価。

各成分へのアクセス

sxx sxy sxz tx
syx syy syz ty
szx szy szz tz
0 0 0 1
var matrix:Matrix3D = new Matrix3D();
matrix.sxx; //1行1列目
matrix.sxy; //1行2列目
matrix.sxz; //1行3列目
matrix.tx;  //1行4列目
・
・
・
matrix.tz;  //3行4列目

行列から位置を取得

var matrix:Matrix3D = new Matrix3D();
var pos:Number3D = matrix.position();

やってることは以下と同じ

var pos:Number3D = new Number3D();
pos.x = matrix.tx;
pos.y = matrix.ty;
pos.z = matrix.tz;

行列式の取得

var matrix:Matrix3D = new Matrix3D();
var determinant:Number = matrix.det();

※逆行列が存在するかどうかの判別以外に行列式の使い道知らないんだけど、何かあるのかな?

行列のかけ算

var multiplymatrix:Matrix3D();
multiplymatrix.multiply(matrix1, matrix2);

回転成分だけをかけ算

var multiply3x3matrix:Matrix3D();
multiply3x3matrix.multiply3x3(matrix1, matrix2);

行列をスカラー倍

matrix.scale(2, 1, 1);

行列の複製

var newMatrix:Matrix3D = matrix.clone();

回転成分だけをコピー

var newMatrix:Matrix3D = Matrix3D.copy3x3(matrix);

回転行列の取得

Matrix3D.rotationMatrix(u:Number, v:Number, w:Number, angle:Number)

//X軸で40度回転する行列の取得
var rotationmatrix:Matrix3D = new Matrix3D();
rotationmatrix.rotationMatrix(1, 0, 0, 30*Math.PI/180);

//Y軸で20度回転する行列の取得
var rotationmatrix:Matrix3D = new Matrix3D();
rotationmatrix.rotationMatrix(0, 1, 0, 20*Math.PI/180);

//Z軸で15度回転する行列の取得
var rotationmatrix:Matrix3D = new Matrix3D();
rotationmatrix.rotationMatrix(0, 0, 1, 15*Math.PI/180);

移動行列の取得

//X軸方向に20、Y軸方向に30、Z軸方向に40移動する
var translationmatrix:Matrix3D = new Matrix3D();
translationmatrix.translationMatrix(20, 30, 40);

拡大縮小行列の取得

//X軸方向に2倍、Y軸方向に3倍、Z軸方向に4倍
var scalematrix:Matrix3D = new Matrix3D();
scalematrix.translationMatrix(2, 3, 4);

逆行列の取得

var inverseMatrix:Matrix3D = new Matrix3D();
inverseMatrix.inverse(matrix);

配列から行列を生成

var matArray:Array =
[
1,0,0,0,
0,1,0,0,
0,0,1,0
];
//4行目は0,0,0,1固定なので、3行4列分を定義

matrix.array2matrix(matArray);

行列からオイラー角に変換

var angle:Number = matrix.matrix2euler();

//角度はDegreeに変換される
obj.rotationX = angle.x;
obj.rotationY = angle.y;
obj.rotationZ = angle.z;

クォータニオンから行列に変換

var q:Quaternion = new Quaternion();
q.axis2quaternion(1,0,0,45*Math.PI/180);
var mat:Matrix3D = new Matrix3D();
mat.quaternion2matrix(q);

よくある使いどころ

//Cubeを生成
var wcMat:WireColorMaterial = new WireColorMaterial(0x0099CC, {wirecolor:0x00CCFF});
vae cube:Cube = new Cube({width:200, height:200, depth:200, faces:{left:wcMat, right:wcMat, bottom:wcMat,top:wcMat,front:wcMat,back:wcMat} });
scene.addChild(sphere);

//Z軸で45度回転する行列を取得
var rotationmatrix:Matrix3D = new Matrix3D();
rotationmatrix.rotationMatrix(0, 0, 1, 45*Math.PI/180);

//行列をcube.transform(Cubeの同次座標)にかける
cube.transform.multiply(cube.transform, rotationmatrix);

※これだけだったらrotationZ使えって話;

Number3D

次はNumber3D。ベクトルを扱うクラス。

コンストラクタ

import away3d.core.math.Number3D;
var vec:Number3D = new Number3D(x:Number, y:Number, z:Number, n:Boolean);

//引数を定義しなければ方向性を持たないゼロベクトルになる
var vec:Number3D = new Number3D();

各成分へのアクセス

var vec:Number3D = new Number3D(1,0,1);

trace(vec.x, vec.y, vec.z);

static変数

Number3D.FORWARD … Number3D(0, 0, 1)と等価
Number3D.BACKWARD … Number3D(0, 0, -1)と等価
Number3D.LEFT … Number3D(-1, 0, 0)と等価
Number3D.RIGHT … Number3D(1, 0, 0)と等価
Number3D.UP … Number3D(0, 1, 0)と等価
Number3D.DOWN … Number3D(0, -1, 0)と等価

ベクトルの正規化

var vec:Number3D = new Number3D(5, 1, 3);
vec.normalize();

//コンストラクタの第4引数をtrueにすることで代用可
var vec:Number3D = new Number3D(5, 1, 3, ture);

ノルム(ベクトルの大きさ)を取得

var vec:Number3D = new Number3D(2, 1, 8);
var mod:Number = vec.modulo;

ノルムの大きさを大まかに判別(ざっくりだけど高速?)

var vec:Number3D = new Number3D(20, 20, 0);
var mod:Number = vec.modulo2;
var magnitude:Number = 5;

//(mod > magnitude*magnitude) が true ならvecのノルムはmagnitudeより大きい
//(mod < magnitude*magnitude) が true ならvecのノルムはmagnitudeより小さい
//(mod == magnitude*magnitude) が true ならvecのノルムはmagnitudeと等しい

※PV3DのNumber3DにあるmoduloSquaredと同じものだと思うけど自信ない。

ベクトルを複製

var vec:Number3D = new Number3D(20, 20, 0);
var newVec:Number3D = vec.clone();

加算と減算

//加算
var result:Number3D = new Number3D();
var vec1:Number3D = new Number3D();
var vec2:Number3D = new Number3D();
result.add(vec1, vec2);

//減算
var result:Number3D = new Number3D();
var vec1:Number3D = new Number3D();
var vec2:Number3D = new Number3D();
result.sub(vec1, vec2);

内積と外積

//内積
var vec1:Number3D = new Number3D();
var vec2:Number3D = new Number3D();
vec1.dot(vec2);

//外積(法線ベクトル取得)
var crossResult:Number3D = new Number3D();
var vec1:Number3D = new Number3D();
var vec2:Number3D = new Number3D();
crossResult.cross(vec1, vec2);

ベクトルをスカラー倍

var vec:Number3D = new Number3D();
var vec1:Number3D = new Number3D(1,0,0);
vec.scale(vec1,2);

2つのベクトルのなす角度(ラジアン)を取得

var vec:Number3D = new Number3D(1, 0, 0);
var vec2:Number3D = new Number3D(0, 1, 0);
var angle:Number = vec.getAngle(vec2);

2つのベクトルの距離を取得

var vec:Number3D = new Number3D(1, 0, 0);
var vec2:Number3D = new Number3D(0, 1, 0);
var dist:Number = vec.distance(vec2);

Matrix3Dでベクトルを変換

//Number3D.rotate(v:Number3D, m:Matrix3D)
//回転のみ
var vecResult:Number3D = new Number3D();
var vec:Number3D = new Number3D(1, 0, 0);
var rotationmatrix:Matrix3D = new Matrix3D();
rotationmatrix.rotationMatrix(0, 0, 1, 45*Math.PI/180);
vecResult.rotate(vec,rotationmatrix);

//Number3D.transform(v:Number3D, m:Matrix3D)
//4x4
var vecResult:Number3D = new Number3D();
var vec:Number3D = new Number3D(1, 0, 0);
var translationmatrix:Matrix3D = new Matrix3D();
translationmatrix.translationMatrix(0, 30, 0);
vecResult.transform(vec,rotationmatrix);

ベクトルが無限平面と交わる位置を取得?

var p:Number3D = new Number3D(5,5,2); //任意のベクトル
var k:Number3D = new Number3D(10, 10, 10); //平面上の一点
var n:Number3D = new Number3D(0, 0, 1); //平面の法線ベクトル

var vecResult:Number3D;
vecResult = p.closestPointOnPlane(p,k,n); //vecResultにベクトルpが平面と交わる座標が返るのか?

※これについてはウソ書いてる可能性多いにアリ。PV3Dにも core.math.Plane3D に同様のメソッドがある。

Quaternion

最後はQuaternion。文字通りクォータニオン(四元数)を扱うクラス。永遠に理解できそうもない。
PV3Dと比べると限定的な機能しかなさげ。ライブラリ内部でもほとんど使われてない。SLERP(球面線形補完)のメソッドも無いので、どーしても使いたきゃ自力で実装しろってことか。

import away3d.core.math.Quaternion;
var q:Quaternion = new Quaternion();

※コンストラクタが無いので、クォータニオンを生成するには後述のaxis2quaternionか、euler2quaternionを使うと。

回転クォータニオンを得る

//axis2quaternion(x:Number, y:Number, z:Number, angle:Number)

var q:Quaternion = new Quaternion();
q.axis2quaternion(1,0,0,45*Math.PI/180);

オイラー角から回転クォータニオンを得る?

//euler2quaternion(ax:Number, ay:Number, az:Number)

var q:Quaternion = new Quaternion();
q.euler2quaternion(0,0,45*Math.PI/180);

※これも自信ないなぁ思惑と違う結果になる。引き続き意識の片隅に。

クォータニオンの大きさを得る

var q:Quaternion = new Quaternion();
q.euler2quaternion(rotationZ,rotationY,rotationX);

var mag:Number = q.magnitude;

クォータニオンを正規化

var q:Quaternion = new Quaternion();
q.euler2quaternion(rotationZ,rotationY,rotationX);
q.normalize();

クォータニオンのかけ算

var q:Quaternion = new Quaternion();
var q2:Quaternion = new Quaternion();
var ans:Quaternion = new Quaternion();

q.euler2quaternion(rotationZ,rotationY,rotationX);
q2.axis2quaternion(1,0,0,45*Math.PI/180);
ans.multiply(q,q2);

あと、Number2D っていうNumber3Dの2D版っぽいクラスもあって、MovieMaterialとかで使われてるみたいだけど、とりあえず割愛。

core.mathパッケージのクラスは、コードの書き方をまとめたところで何の役にも立ちませんな;
「ベクトルの内積が0なら、2つのベクトルは直角」とか「3Dベクトルの外積=法線ベクトル」みたいな超基本的な事はもちろん、Number3DやMatrix3Dの各メソッドがどういう概念に基づいて実装されてるのかがわかってないと使いようがない。逆にそういった知識は3Dプログラミング全般で役に立つと思うので、もっと必死こいて勉強すれ!>オレ

[Away3D] Away3D2.0 Released

2008.04.16

コツコツと地味にバージョンアップを重ねてきたAway3Dが、とうとう2.0リリース。
悶絶なデモまで併せて公開。脱帽でございます。

主要な追加機能は以下のような感じ。

  • Simulated phong shading (with blinn-phong specular highlights)
    フォンシェーディングもどき?
  • Environment-mapped materials
    環境マップ
  • Normal-mapped materials (an open source first for Flash)
    法線マップ(!)
  • Z-depth filter for large outdoor scenes
    Farclipみたいなものかと
  • Fog filter
    フォグフィルタ
  • Straight-to-bitmap rendering for fast scaled views
    よくわかんね。これのことかな
  • Surface caching for fast rendered lights
    ライトとの関係で変化するサーフェスのレンダリング結果をキャッシュ

バージョンアップ内容詳細

Z-depth filter と Fog filter 待ってた!いつか実装してくれると信じてた。これでようやくササヤカな野望の実現に向けて一歩前進。とりあえず、PV3DのFrustumCameraを使ったデモを移植しつつ、Z-depth filterとFog filterを使ってみた。

e_fogtest.jpg

fogTest.swf(要:FlashPlayer9)
※地形データの読み込みに少々時間がかかります。
※画面クリック後、カーソルキーでカメラが移動

どんだけ濃霧なんだよって感じだけどイイっ!あとはニアクリップを上手いことやる方法がわかれば文句ないわ。盛り上がってまいりました。オレだけが。

[PV3D2.0] materials.MovieMaterial

2008.04.12

e_moviematerial.jpg

moviematerial_test.swf(要:FlashPlayer9)

tera@trick7さんが、メディアテクノロジーラボブログに投稿されたエントリー「Papervision3D の平面に外部 swf をロード」に、大きなお世話だけど補足を入れてみる。

MovieMaterial 第2引数の transparent は、第1引数で指定したDisplayObjectのAlpha値をサポートするかどうかを決める要素。これに関しては、Aquiouxさんが書かれた詳細な記事で解説されている通り。(PV3Dのマテリアルに関するAquiouxさんの解説は、1.5〜1.7ベースの記事ですが、PV3D2.0でも当てはまる部分がほとんどなので、かなり参考になると思います。)

teraさんのコードの

//読み込むムービーの背景色と同じ塗りの色にする
exStageSpr.graphics.beginFill(0xFF3300);
・・・
//exStageSprをテクスチャに設定する。
var movieMat:MovieMaterial = new MovieMaterial(exStageSpr, false, true, false);

の部分を

//読み込むムービーの背景色と同じ塗りの色にする
exStageSpr.graphics.beginFill(0xFF3300, 0.5);
・・・
//exStageSprをテクスチャに設定する。
var movieMat:MovieMaterial = new MovieMaterial(exStageSpr, true, true, false);

に変えることで、背面の赤が半透明になる。逆に、外部から読み込んだSWF内に半透明塗りされた部分があるなら、transparentをtrueにしないと狙った結果にならない。

次に第4引数の precise は、PV3D1.7で Precise〜Material という個別のクラスとして実装されてたものが、BitmapMaterialに組み込まれて、パラメータ指定できるようになったもの。いわゆるパースペクティブコレクトの on/off 設定。重めの処理だけど、予めメッシュを細かく分割しておかなくてもテクスチャのゆがみが目立たなくなる。

preciseについての詳細は別エントリにて

というあたりを使ってみたのが冒頭のデモ。

var movieMat_tf:MovieMaterial = new MovieMaterial(loadSWF,false,true,true);
var movieMat_tt:MovieMaterial = new MovieMaterial(loadSWF,true,true,true);

と、transparentの設定だけを変えた2つのマテリアルを、背中合わせに配置した2枚のPlaneそれぞれに割り当てた。

以下、簡略版ソース

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package
{
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.display.StageQuality;
    import flash.events.Event;
    import flash.display.Loader;
    import flash.net.URLRequest;
 
    import org.papervision3d.view.BasicView;
    import org.papervision3d.objects.primitives.Plane;
    import org.papervision3d.materials.MovieMaterial;
    import org.papervision3d.materials.WireframeMaterial;
 
    [SWF(backgroundColor=0x000000)]
 
    public class moviematerial_sample extends BasicView
    {
        private var loadSWF:Sprite;
        private var objPlaneA:Plane;
        private var objPlaneB:Plane;
        private var rotateAngle : Number = 0;
 
        public function moviematerial_sample()
        {
            stage.frameRate = 60;
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.quality = StageQuality.MEDIUM;
 
            super (0,0,true,true,"Target");
            init();
        }
 
        private function init():void
        {
            loadSWF = new Sprite();
            loadSWF.graphics.beginFill(0xFFFFFF,0.6);
            loadSWF.graphics.drawRect(0, 0, 480, 360);
            loadSWF.graphics.endFill();
 
            var loader:Loader = new Loader();
            loadSWF.addChild(loader);
            loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
            loader.load(new URLRequest("external.swf"));
 
            function loadComplete(e:Event):void
            {
                init3D();
            }
        }
 
        private function init3D():void
        {
            camera.z = -2400;
            camera.fov = 30;
 
            var movieMat_tf:MovieMaterial = new MovieMaterial(loadSWF,false,true,true);
            objPlaneA = new Plane( movieMat_tf, 480, 360, 1, 1 );
            scene.addChild(objPlaneA);
            objPlaneA.yaw(180);
 
            var movieMat_tt:MovieMaterial = new MovieMaterial(loadSWF,true,true,true);
            objPlaneB = new Plane( movieMat_tt, 480, 360, 1, 1 );
            scene.addChild(objPlaneB);
 
            var wireMat:WireframeMaterial = new WireframeMaterial(0x666666);
            var floor:Plane = new Plane( wireMat, 700, 700, 3, 3 );
            scene.addChild(floor);
            floor.z = 350;
 
            startRendering();
        }
 
        override protected function onRenderTick(event:Event=null):void
        {
            objPlaneA.yaw(-2);
            objPlaneB.yaw(-2);
 
            super.onRenderTick(event);
        }
    }
}

08/09/17追記:

タロタローグさんの『Papervision3DのMovieMaterialの挙動がよく分からん。スクリプトでのアニメーションじゃ駄目なのかな。』からトラックバックをいただいたので、スクリプト制御でDisplayObjectをアニメーションさせている外部SWFを読み込んでMovieMaterialに割り当てるってのをやってみた。

■読み込まれる(MovieMaterialに割り当てる)側のソース

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package
{
    import flash.display.Stage;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.display.StageQuality;
    import flash.events.Event;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.text.TextFormat;
    import flash.text.AntiAliasType;
 
    //import flash.filters.BlurFilter;
 
    [SWF(backgroundColor=0x000000)]
 
    public class matSwf extends Sprite
    {
        private var bg:Sprite;
 
        public function matSwf()
        {
            if ( this.stage != null ) init( this.stage );
        }
 
        public function init( s:Stage ):void
        {
            s.frameRate = 50;
            s.align = StageAlign.TOP_LEFT;
            s.scaleMode = StageScaleMode.NO_SCALE;
            s.quality = StageQuality.BEST;
 
            bg = new Sprite();
            bg.graphics.beginFill(0x006699,0.7);
            bg.graphics.drawRect(0, 0, 480, 360);
            bg.graphics.endFill();
            addChild(bg);
 
            for (var i:int = 0; i < 15; i++)
            {
                var tf:TextField = new TextField();
                tf.autoSize = TextFieldAutoSize.LEFT;
                tf.selectable = false;
                tf.antiAliasType = AntiAliasType.ADVANCED;
 
                var tfm:TextFormat = new TextFormat();
                tfm.font = "Arial";
                tfm.color = 0x0099CC;
                tfm.size = 24 + (Math.random()*40 - 10);
                tfm.letterSpacing = -3;
                tf.defaultTextFormat = tfm;
                tf.text = "THE WORDS OF THE PROPHETS ARE WRITTEN ON THE SUBWAYWALLS AND TENEMENT HALLS.";
                addChild(tf);
 
                //tf.filters = [new BlurFilter(Math.random()*10,Math.random()*10,1)];
                tf.x = i*100 + Math.random()*150;
                tf.y = (bg.height - tf.height)/2 + (Math.random()*300 - 150);
                tf.addEventListener(Event.ENTER_FRAME, loop);
            }
        }
 
        private function loop(e:Event):void
        {
            e.target.x -= 2;
            if (e.target.x < -e.target.width) e.target.x = 480;
        }
    }
}

■PV3Dシーン側のソース

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package
{
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.display.StageQuality;
    import flash.events.Event;
    import flash.display.Loader;
    import flash.net.URLRequest;
    import flash.geom.*;
    import flash.text.*;
 
    import org.papervision3d.view.BasicView;
    import org.papervision3d.objects.primitives.Plane;
    import org.papervision3d.materials.MovieMaterial;
    import org.papervision3d.materials.WireframeMaterial;
 
    [SWF(backgroundColor=0x000000)]
 
    public class moviematerial_sample extends BasicView
    {
        private var loadSWF:Sprite;
        private var objPlane:Plane;
 
        public function moviematerial_sample()
        {
            stage.frameRate = 50;
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.quality = StageQuality.MEDIUM;
 
            super (0,0,true,true,"Target");
            init();
        }
 
        private function init():void
        {
            loadSWF = new Sprite();
 
            var loader:Loader = new Loader();
            loadSWF.addChild(loader);
            loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
            loader.load(new URLRequest("matSwf.swf"));
        }
 
        private function loadComplete(e:Event):void
        {
            e.target.loader.content["init"]( stage );
            init3D();
        }
 
        private function init3D():void
        {
            camera.x = 500;
            camera.z = -1500;
            camera.fov = 30;
 
            var movieMat_tt:MovieMaterial = new MovieMaterial(loadSWF,true,true,true, new Rectangle(0,0,480,360));
            movieMat_tt.doubleSided = true;
            objPlane = new Plane( movieMat_tt, 480, 360, 1, 1 );
            scene.addChild(objPlane);
 
            var wireMat:WireframeMaterial = new WireframeMaterial(0x666666);
            var floor:Plane = new Plane( wireMat, 700, 700, 3, 3 );
            scene.addChild(floor);
            floor.z = 350;
 
            startRendering();
        }
 
        override protected function onRenderTick(event:Event=null):void
        {
            objPlane.yaw(-1);
            super.onRenderTick(event);
        }
    }
}

上記の実行結果(要:FlashPlayer9)

て感じで、スクリプト制御でDisplayObjectをアニメーションさせている場合でもMovieMaterialに割り当てることはできるみたい。ただ、やっぱりこのマテリアルは重い。上の読み込まれる側のソースで、ブラーフィルタ部分をコメントアウトしてるのは、やたら重かったから。

タロタローグさんが挙げている3つの残念ポイント

  • 滑らかだったアニメーションは見る影も無くガタガタで、時々思い出したように表示が更新され、記事タイトルが大移動している
  • newした方(背景透明)は、何故か幅の認識がおかしいようで、ラベルが2つ(消え行く用と出て来る用)見えてしまっている。
  • swf読み込みの方は、大きさが元々の640×480になっているらしく、3D化したものは多分左上しか出てきていない

のうち2つ目と3つ目は、いつ仕様変更で追加されたか忘れたけど、MovieMaterialの第五引数として渡すクリッピング用Rectangleオブジェクトの指定で解決できそうな気がする。クリッピング範囲を指定しないとステージ上に配置されているオブジェクトがテクチャの範囲に収まるように辻褄合わせしてしまうので、表示したくない部分まで表示されたり縦横比が変わったりする。
1つめの残念ポイントについては、よくわかんないけどブラーフィルタとかが重いせいかも。

いずれにしても MovieMaterial に割り当てるムービーを、かなりシンプルにしないとキツそうな感じですな。