ラベル ActionScript の投稿を表示しています。 すべての投稿を表示
ラベル ActionScript の投稿を表示しています。 すべての投稿を表示

2012年1月8日日曜日

Flashで3Dなお絵描き

大学の時にブラウザ上で3Dモデルに絵を描くアプリケーションを作りました。

長い間ブログを放置していたので、それの紹介でもしたいと思います。

まずブラウザで3Dを扱うというのはあまりない話です。重いですし。
下のリンクでは描いたテクスチャを3Dモデルに貼り付けるようなことをやっていました。
http://labs.logosware.com/archives/21

なんやかんやあってFlashで作るのがいいだろうって話になって、そこからは自分がいろいろ試して作っていくことになりました。
Flashもそんな詳しいわけではなかったし、3Dは全く分からないというぐらいだったので開発には苦労しました。
ブログの過去記事などを見ると試行錯誤の跡が少し見られると思います。

まず3Dモデルをどうするかという話でしたが、開発メンバーが有料のモデリングソフトもってない、Flashの3Dライブラリでサポートされてるといった点などを考えた結果、Collada(.xml)形式にしました。

で、3Dライブラリは当時(今でも?)一番メジャーなPapervision3Dにしようと考えていました。

http://blog.r3c7.net/?p=333
上の記事を参考にサンプルを作っていきました。3Dモデルの作成や、マウスイベントなど苦労しましたがなんとかそれっぽいものはできました。
じゃあ次はシェーダーを試そうと思った時に問題が起きました。
http://aaharu.blogspot.com/2009/10/pv3duv.html
UVマッピングを使用したモデルにシェーダーを適用すると黒い線が出てきました。
これの解決方法は結局わからず、Papervision3D以外のライブラリではどうなのか試してみることにしました。

で、試したのがAway3Dです。
まあFlashの3Dライブラリといってもメジャーなのは、Papervision3DとAway3DとAlternativa3Dぐらいでしたし、Papervision3Dから派生したものだと聞いて今までのが移植しやすいかなーと。

それで試した結果、Away3Dは問題なく動作することがわかり、3DライブラリはAway3Dを使用することに決めました。
Away3Dにして良かった点はいくつかありました。
・上の黒い線の問題が解決したこと
・3Dマウスイベントが豊富なこと
・シェーダーなども豊富で、きれいであったこと
絵を描くということを考えると3Dのオブジェクトのマウスイベントがあるのはとてもよかったです。
悪いと思った点もいくらかあります。
・重い
・そんなに移植しやすいわけではなかった

とそんな感じでぐだぐだと作りました。
せっかくなので、FlashDevelopで書き直した、一部機能削除版をソースコードごと公開します。(開発時はFlashBuilderを使いました。学生だったので無料ライセンスで)
今見るといろいろと直したくなりますがそこらへんは許してやってください。


Flexアプリ http://www.aaharu.com/data/UruLabCreater.swf
ソースコード(Flex) http://www.aaharu.com/data/srcview_urulabcreater/index.html
ソースコード(ライブラリ)http://www.aaharu.com/data/srcview_urulab/index.html

ソースコード書き出し後に修正行ってるのでソースコード最新じゃないですけどだいたいあってます。

2011年4月23日土曜日

FlexのBase64Decoder

DC startup generatorのソースコードを少し修正した。
んで、mx.utils.Base64Decoderを使うときにちょっと躓いたのでメモ。

修正個所は、zlib圧縮をやめたのと、
修正前はライブラリを使ってBase64を扱っていたのですが、どうやらFlex4には最初から
mx.utils.Base64Decoder
なるものがあるらしいので使ってみることにした。

*自分はFlash Builder 4で開発してます。

まず、普通にAS3プロジェクトを作ってもBase64Decoderをインポートできない。
「framework.swc」というswcファイルが必要になります。
Base64Encoder/Base64Decoderを使う
これはSDKがあるディレクトリの、[frameworks/libs]の中にあります。

しかしまだエラーが残るので厄介。
Base64Decoder 使おうとしたら「リソースバンドルutilsを解決できません」とか言い出したのでやっつけた
なぜかダミーのフォルダを作らないといけないようです・・・

あとは問題なく使えます。
設定さえ終われば便利です。

以下ソースコード。
package
{
 import flash.display.Sprite;
 import flash.display.StageScaleMode;
 import flash.events.Event;
 import flash.filters.BlurFilter;
 import flash.net.URLRequest;
 import flash.net.navigateToURL;
 import flash.text.TextField;
 import flash.text.TextFormat;
 import flash.utils.ByteArray;
 
 import mx.utils.Base64Decoder;
 
 import org.libspark.betweenas3.BetweenAS3;
 import org.libspark.betweenas3.easing.Cubic;
 import org.libspark.betweenas3.easing.Quad;
 import org.libspark.betweenas3.tweens.ITween;
 
 public class DC extends Sprite
 {
  private function Base64toByteArray(data:String):ByteArray
  {
   var byteArray:ByteArray;
   var base64Decoder:Base64Decoder = new Base64Decoder();
   base64Decoder.decode(data);
   try {
    byteArray = base64Decoder.toByteArray();
    byteArray.position = 0;
   } catch (e:Error) {
    return null;
   }
   return byteArray;
  }
  
  private const R:uint = 60;
  private const W:Number = stage.stageWidth / 2;
  private const H:Number = stage.stageHeight / 2;
  private var _letter:Array = [];
  private var _ball:Ball;
  private var _temp:TextField = new TextField();
  private var _bounceTiem:Number = 0.36;
  
  public function DC()
  {
   stage.scaleMode = StageScaleMode.SHOW_ALL;
   stage.frameRate = 60;
   
   var param:Object = loaderInfo.parameters;
   var word:String = param["w"];
   if(word == null)
    word = "Dreamcast";
   else {
    var ba:ByteArray = new ByteArray();
    ba = Base64toByteArray(word);
    if(!ba) {
     navigateToURL(new URLRequest("/dc/"));
    } else {
     word = ba.readUTFBytes(ba.length);
    }
   }
   _temp.text = word;
   var format:TextFormat = new TextFormat(null, R);
   _temp.setTextFormat(format);
   
   for(var i:int = 0; i < _temp.text.length; i++)
   {
    var tf:TextField = new TextField();
    tf.text = _temp.text.charAt(i);
    tf.selectable = false;
    tf.setTextFormat(format);
    
    if(i)
     tf.x = _letter[i-1].x + _letter[i-1].textWidth;
    else
     tf.x = W - _temp.textWidth / 2;
    tf.y = H + R/2;
    _letter.push(tf);
   }
   
   _ball = new Ball();
   _ball.filters = [new BlurFilter()];
   _ball.x = _letter[0].x - R*2.2;
   _ball.y = _letter[0].y - R*4;
   addChild(_ball);
   
   var tween:ITween = BetweenAS3.bezierTo(_ball, {x: _letter[0].x + _letter[0].textWidth, y: _letter[0].y + 35}, {x: _letter[0].x + R, y: _letter[0].y - R}, 2, Cubic.easeIn);
   tween.onComplete = bounce;
   tween.onCompleteParams = [1];
   tween.play();
  }
  
  private function bounce(n:uint):void
  {
   character(n-1);
   var tween:ITween = BetweenAS3.bezierTo(_ball, {x: _letter[n].x + _letter[n].textWidth, y: _ball.y}, {x: _letter[n].x + _letter[n].textWidth / 4, y: _letter[n].y - 10}, _bounceTiem);
   if(_bounceTiem >= 0.22) {
    _bounceTiem -= 0.02;
   }
   if(n < _temp.text.length - 1)
    tween.onComplete = function():void
    {
     bounce(n+1)
    };
   else
    tween.onComplete = ぴょーん;
   tween.play();
  }
  
  private function character(index:uint):void
  {
   addChild(_letter[index]);
   BetweenAS3.tween(_letter[index], {y: _letter[index].y, alpha: 1}, {y: _letter[index].y + 20, alpha: 0}).play();
  }
  
  private function ぴょーん():void
  {
   character(_temp.text.length - 1);
   
   var tween:ITween = BetweenAS3.bezierTo(_ball, {x: W, y: _letter[0].y - R*0.8}, {x: (W + _ball.x) / 2, y: _letter[0].y - R*5}, 1.2, Quad.easeOut);
   tween.play();
   tween.onComplete = ぐるぐる;
  }
  
  private function ぐるぐる():void
  {
   var sp:Sprite = new Sprite();
   sp.filters = [new BlurFilter()];
   addChild(sp);
   sp.graphics.lineStyle(6, _ball.color);
   sp.graphics.moveTo(_ball.x, _ball.y);
   
   addEventListener(Event.ENTER_FRAME, onEnterFrame(sp));
   
   var tweenArray:Array = [];
   for(var i:int = 0; i < 900; i++)
   {
    var toX:Number = _ball.x - R * i / 720 * Math.cos(i * Math.PI / 180);
    var toY:Number = _ball.y - R * i / 720 * Math.sin(i * Math.PI / 180);
    tweenArray.push(BetweenAS3.to(_ball, {x: toX, y: toY}, 0.002));
   }
   var tween:ITween = BetweenAS3.serialTweens(tweenArray);
   tween.onComplete = function():void
   {
    removeEventListener(Event.ENTER_FRAME, onEnterFrame);
    sp.graphics.lineTo(_ball.x, _ball.y);
    removeChild(_ball);
   };
   tween.play();
  }
  
  private function onEnterFrame(sp:Sprite):Function
  {
   return function(e:Event):void
   {
    sp.graphics.lineTo(_ball.x, _ball.y);
   };
  }
 }
}

import flash.display.Sprite;

class Ball extends Sprite
{
 private var _color:uint;
 
 public function get color():uint
 {
  return _color;
 }
 
 public function Ball(j:Boolean = true) {
  if(j)
  {
   graphics.beginFill(0xea5504);
   _color = 0xea5504;
  } else {
   graphics.beginFill(0x0000FF);
   _color = 0x0000FF;
  }
  graphics.drawCircle(0, 0, 4);
  graphics.endFill();
 }
}

2010年11月20日土曜日

ドリームキャスト起動画面のジェネレータ作ってみた

ドリキャスの起動画面をFlashで再現してみた。

DC startup generator

生成されたURL(http://www.aaharu.com/dcdata/DC.swf?w=eJzzSM3JyddRCM8vyklRBAAfngRqなど)に飛べば、その文字列をいつでも再現できます。

Flashのソースコードも公開してあります。
Python(GAE)のコードの一部がこれ。
class GenerateHandler(webapp.RequestHandler):
    def post(self):
        form = self.request.get('words')
        if len(form) < 2:
            path = os.path.join(os.path.dirname(__file__), 'index.html')
            self.response.out.write(template.render(path, {'alert': "2文字未満は未対応です・・・"}))
        else:
            form = zlib.compress(form.encode('utf_8'))
            form = b64encode(form)
            self.redirect("../dcdata/DC.swf?w="+form)

GETでswfに文字列を渡しているのですが、流れとしては
HTMLのformで文字列をPOSTで送信→Pythonでzlib(gzip)圧縮→PythonでBase64エンコード→GETで渡す→swfでGET受信→ActionScriptでBase64をByteArrayにデコード→ByteArrayを解凍→文字列に挿入
て感じです。ちょっと違う?

2010年7月26日月曜日

Stringのlengthで全角を2文字として扱ってみる[AS3]

※注 - よくわかってないで記事書いてるので、いろいろ勘違いしているかも。突っ込み歓迎。

どうやらActionScript3のStringのlengthってマルチバイト文字も1として数えてるみたい。
今まで気にしてなかったけど、例えばPHPのstrlen関数はマルチバイト文字は2としているので、差異がある。

そこで、マルチバイト文字を2として数える方法ないかなーと思ったけど、見つからない。
というわけで、自分で作ってみた。
var str:String = "1@a $%&'=a ぁa←あア亜1@";
var reg:RegExp = new RegExp("[^ -~。-゚]", "g");
trace(str);
trace(str.length);
trace(str.match(reg));
trace(str.match(reg).length);
trace(str.length + str.match(reg).length);
実行結果
1@a $%&'=a ぁa←あア亜1@
19
 ,ぁ,a,←,あ,ア,亜,1,@
9
28
一応合ってる。

正規表現を使ってます。参考:ASCIIコード表
半角の文字以外にマッチする正規表現を作って、マッチした文字数を足してます。

2010年7月10日土曜日

AS3でイベントリスナーに引数を渡す

ActionScript3.0でイベントリスナーにイベント以外の引数を渡す方法。
自分は2つの方法を使ってます。てかそれ以外のやり方は知らない。
グローバル変数を増やしたくないときとかに使ってます。

クリック数をカウントするプログラム。
2つ目と3つ目は基本的にやってることは変わらないと思う。
// グローバル変数を使う
public class event extends Sprite
{
    private var a:int = 0;
    public function event()//constructor
    {
        stage.addEventListener(MouseEvent.CLICK, onClick);
    }

    private function onClick(e:MouseEvent):void
    {
         trace(++a);
    }
}
// Functionを返す関数を作る
public class event extends Sprite
{
    public function event()//constructor
    {
        var a:int = 0;
        stage.addEventListener(MouseEvent.CLICK, onClick(a));
    }

    private function onClick(a:int):Function
    {
        return function(e:MouseEvent):void
        {
            trace(++a);
        };
    }
}
// 無名関数使う
public class event extends Sprite
{
    public function event()//constructor
    {
        var a:int = 0;
        stage.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void
        {
            trace(++a);
        });
    }
}

2010年6月1日火曜日

[AS3.0]2点間の距離の計算比較

先日Point.distance()で2点間の距離を計算できることを初めて知りました。
今まで2点間の距離は、三平方の定理を使って計算していたので、Pointを使えば楽に求められると思ったのですが、どちらの計算方法が早いのか疑問に思ったので比べてみた。
package {
 import flash.display.Sprite;
 import flash.geom.Point;
 import flash.utils.getTimer;
 
 public class distance extends Sprite {
  public function distance() {
   // 点1
   var x1:Number = 100;
   var y1:Number = 0;
   // 点2
   var x2:Number = 391;
   var y2:Number = 36.8;
   
   var distance:Number;
   
   var start:Number;
   var end:Number;
   var t:Number;
   
   var i:uint;
   
   start = getTimer();
   for(i = 0; i < 1000000; i++)
    distance = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
   end = getTimer();
   t = end - start;
   trace("Math1: " + t);
   trace(distance);
   
   start = getTimer();
   for(i = 0; i < 1000000; i++)
    distance = Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
   end = getTimer();
   t = end - start;
   trace("Math2: " + t);
   trace(distance);
   
   start = getTimer();
   var dx:Number = x2 - x1;
   var dy:Number = y2 - y1;
   for(i = 0; i < 1000000; i++)
    distance = Math.sqrt(dx * dx + dy * dy);
   end = getTimer();
   t = end - start;
   trace("Math2.5: " + t);
   trace(distance);
   
   start = getTimer();
   var pt1:Point = new Point(x1, y1);
   var pt2:Point = new Point(x2, y2);
   for(i = 0; i < 1000000; i++)
    distance = Point.distance(pt1, pt2);
   end = getTimer();
   t = end - start;
   trace("Point: " + t);
   trace(distance);
  }
 }
}
差が出やすいように100万回計算させてみた。 上のプログラムを実行した結果は、
Math1: 401
293.3176435197856
Math2: 205
293.3176435197856
Math2.5: 197
293.3176435197856
Point: 1214
293.3176435197856
計算結果は当然ながら全部一致。 計算時間は、Pointが目に見えて遅い。 あと二乗ぐらいなら、Math.pow()を使わない方が早い。 ちなみに、Math.sqrt()を使わないで自分で実装したらどうなのかと思って、ニュートン法を使って実行してみたのですが、圧倒的に遅かったです。Math.sqrt()すげー。

というわけで2点間の距離は、今後も三平方の定理を使って求めていきたいと思います。

2010年4月16日金曜日

[Pv3D] Meteor Attack(@wonderfl)の解説

一時的にだけど、[Pv3D] Meteor AttackがWonderflでデイリーのPVで一位になったのを記念に解説をこちらのほうでやってみます。


と言ってもこれはそんなに難しいことをしているわけではないんですよね。
やっていることをまとめると、
「球(スフィア)の配置」「カメラ、ライト移動のためのイベント」「画像のロード」「シェーダー、マテリアルの設定」「球(スフィア)のクリックイベント」「アニメーション」「バンプマップの更新」
ぐらいです。

全部説明するのは面倒疲れるので、「球(スフィア)のクリックイベント」「バンプマップの更新」だけ解説します。
該当部はここらへん。
//private function onMapComplete(e:Event):void {
private function onMapComplete():void {
 //var bmpData:BitmapData = new BitmapData(e.target.loader.width, e.target.loader.height);
 var loader1:Loader = imageArray.pop();
 var bmpData:BitmapData = new BitmapData(loader1.width, loader1.height);
 bmpData.draw(loader1);
 moonBmpData = bmpData;
 var loader2:Loader = imageArray.pop();
 bmpData = new BitmapData(loader2.width, loader2.height);
 bmpData.draw(loader2);
 bumpBmpData = bmpData;
 scene.addChild(light);
 
 shader = new PhongShader(light, 0xFFFFFF, 0x666666, 0, bumpBmpData);
 var material:ShadedMaterial = new ShadedMaterial(new BitmapMaterial(moonBmpData), shader);
 material.interactive = true;
 material.smooth = true;
 var sphere:Sphere = new Sphere(material, 100, 15, 15);
 sphere.addEventListener(InteractiveScene3DEvent.OBJECT_CLICK, onObjectClick);
 scene.addChild(sphere);
 
 var particles:ParticleField = new ParticleField(new ParticleMaterial(0xffffff, 1, 1), 200, 2, 500, 500, 500);
 scene.addChild(particles);
}

private function onObjectClick(e:InteractiveScene3DEvent):void {
 var ball:Sphere = new Sphere(new ColorMaterial(0xEEEEEE), 10);
 scene.addChild(ball);
 var tween:ITween = BetweenAS3.tween(ball, {x: e.renderHitData.x * 0.9, y: e.renderHitData.y * 0.9, z: e.renderHitData.z * 0.9}, {x: e.renderHitData.x * 3.5, y: e.renderHitData.y * 3.5, z: e.renderHitData.z * 3.5});
 tween.play();
 tween.onComplete = function():void {
  var sp:Sprite = new Sprite();
  sp.graphics.beginFill(0xFFFFFF);
  sp.graphics.drawCircle(25, 25, 20);
  sp.graphics.endFill();
  sp.graphics.beginFill(0x000000);
  sp.graphics.drawCircle(25, 25, 17);
  sp.graphics.endFill();
  var bmpData:BitmapData = new BitmapData(50, 50, true, 0x00000000);
  bmpData.draw(sp);
  bumpBmpData.draw(bmpData, new Matrix(1, 0, 0, 1, e.x * 1000 - 25, e.y * 500 - 25));
  shader.bumpmap = bumpBmpData;
  scene.removeChild(ball);
 }
}
上のほうはシェーダーの設定です。
sphere.addEventListener(InteractiveScene3DEvent.OBJECT_CLICK, onObjectClick);
でSphereのクリックイベントを設定しています。

で、イベントハンドラで、
(InteractiveScene3DEvent).x及び、(InteractiveScene3DEvent).yは、SphereのマテリアルのUV情報、
(InteractiveScene3DEvent).renderHitData.x(.y, .z)は、クリックしたXYZ座標の値を取ってくることができます。
UVについてはこちらが自分はわかりやすいと思いました。英語だけど。
要するに、UVの値からテクスチャにおける座標を取得したいと思ったら、UVそれぞれにテクスチャの幅と高さを掛けてやれば良いわけです。
(InteractiveScene3DEvent).x * (image).widthという感じで。

上記のプログラムの32行目~は、外側が白で、内側が黒の円を描いて、円の中心がクリックした点になるように移動し、shader.bumpmap = bumpBmpData;でバンプマップを更新しているわけです。

バンプマップを更新したところで、地味で目立たないですね。
というかこれ、以前の記事のプログラムの流用です。

・・・解説下手だなー。

2010年2月9日火曜日

あーくたん

前回のSVG表示するやつを少し変えたものをwonderflに投稿してみました。

回転させる角度を求めるのに、最初Math.atan2メソッドを知らなくて、Math.atanで事象ごとに場合分けをして求めようとしてたら、無駄に苦労して涙目。

というわけでatanの復習。
atanつまりarctan(アークタンジェント)はtan(タンジェント)の逆関数。
直角三角形の2辺から、角度(ラジアン)を求めることができます。

今回のプログラムをatanを使おうとすると、単純にMath.atan(dy / dx)では角度が求められない場合がでてくる。一番わかり易いのだとdxが0の場合とか。
Math.atan2だとそんな問題が解決できる。ActionScriptのatan2がどう実装されているか知りませんが、一般的に、atan2はこのようになっているようです。

atan2(y, x) =
{
atan(y/x) (x>0),

π-atan(|y/x|) (x<0 かつ y>0),

-(π-atan(|y/x|)) (x<0 かつ y<0),

0 (x<=0 かつ y=0),

π/2 (x=0 かつ y>0),

-π/2 (x=0 かつ y<0)
}

ちゃんと0のときも定義されているので使いやすい。

2010年1月30日土曜日

[AS3.0]SVG表示してみた

ベクター形式の画像を表示したいと思ったので、折角だから最近流行ってる?SVG形式を試しに使ってみました。
こんな感じです。
何もしないと真っ白なままなので、クリックやドラッグをしてみてください。SVG画像が表示されます。
SVG形式の画像はInkscapeで作りました。
ソースコードはこちら。
package {
 import flash.display.Sprite;
 import flash.events.Event;
 import flash.events.MouseEvent;
 import flash.net.URLLoader;
 import flash.net.URLRequest;
 
 import frocessing.shape.FShapeSVG;

 [SWF(backgroundColor = "0xFFFFFF", frameRate = "30")]
 public class TestSVG extends Sprite {
  private var sp:Sprite;
  private var mouseDown:Boolean;
  private var mouseDownX:Number;
  private var mouseDownY:Number;
  
  public function TestSVG() {
   var loader:URLLoader = new URLLoader();
   loader.addEventListener(Event.COMPLETE, onComplete);
   loader.load(new URLRequest("test.svg"));
   
   mouseDown = false;
   stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
   stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
   stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
  }
  
  private function onComplete(e:Event):void {
   var svg:XML = XML(e.target.data);
   var shapeData:FShapeSVG = new FShapeSVG(svg);
   sp = shapeData.toSprite();
  }
  
  private function onMouseDown(e:MouseEvent):void {
   mouseDownX = e.stageX;
   mouseDownY = e.stageY;
   sp.x = e.stageX - sp.width / 2;
   sp.y = e.stageY - sp.height / 2;
   sp.alpha = 0.2;
   mouseDown = true;
   addChild(sp);
  }
  
  private function onMouseMove(e:MouseEvent):void {
   if(mouseDown) {
    sp.width = Math.abs(mouseDownX - e.stageX);
    sp.height = Math.abs(mouseDownY - e.stageY);
    sp.x = mouseDownX - sp.width / 2;
    sp.y = mouseDownY - sp.height / 2;
   }
  }
  
  private function onMouseUp(e:MouseEvent):void {
   sp.alpha = 1;
   mouseDown = false;
  }
 }
}
Frocessingというライブラリを使わせてもらっています。
思った以上に簡単に出来ました。

今回の参考サイト
FlashでSVG形式のベクターグラフィックを利用する | デベロッパーセンター
nutsu/Frocessing - Spark project

2009年11月23日月曜日

[Away3D]UV座標とれた。

最近投稿のタイトルが適当です。

前にわからないって言ってたUV座標の取り方がわかりました。
わからないって言ってたのが馬鹿みたいに簡単にできました。

こんなん。
そういえばちょっと前から作ったものをGoogle App Engineのほうに上げるようにしたんですけど問題なく見られますかね?あまり動作確認とかしてないので・・・

ソースコードはこんな感じ。
package {
 import away3d.cameras.HoverCamera3D;
 import away3d.containers.ObjectContainer3D;
 import away3d.containers.Scene3D;
 import away3d.containers.View3D;
 import away3d.core.utils.Cast;
 import away3d.events.MouseEvent3D;
 import away3d.lights.DirectionalLight3D;
 import away3d.loaders.Collada;
 import away3d.materials.MovieMaterial;

 import flash.display.Bitmap;
 import flash.display.MovieClip;
 import flash.display.Sprite;
 import flash.display.StageQuality;
 import flash.events.Event;
 import flash.events.MouseEvent;

 public class away3 extends Sprite {
  private var scene:Scene3D;
  private var camera:HoverCamera3D;
  private var view:View3D;

  [Embed(source="mate.png")]
  public var materialImage:Class;
  [Embed(source="test.dae", mimeType="application/octet-stream")]
  public var colladaData:Class;

  private var material:MovieMaterial;
  private var collada:Collada;
  private var model:ObjectContainer3D;
  private var light:DirectionalLight3D;
  private var mc:MovieClip;

  private var isMouseDown:Boolean;
  //navigation variables
  private var lastPanAngle:Number;
  private var lastTiltAngle:Number;
  private var lastMouseX:Number;
  private var lastMouseY:Number;


  public function away3() {
   mc = new MovieClip();
   mc.base = mc.addChild(new Bitmap(Cast.bitmap(materialImage)));
   mc.surface = mc.addChild(new Sprite());
   initEngine();
   initLights();
   initMaterials();
   initObjects();
   initListeners();

   isMouseDown = false;
  }

  /**
   * Initialise the engine
   */
  private function initEngine():void {
   scene = new Scene3D();
   camera = new HoverCamera3D();
   camera.zoom = 1;
   camera.focus = 100;
   camera.distance = 50;
   camera.steps = 10;
   camera.targetpanangle = camera.panangle = 0;
   camera.targettiltangle = camera.tiltangle = 10;

   view = new View3D({camera: camera, scene: scene});
   addChild(view);

   stage.quality = StageQuality.HIGH;
  }

  /**
   * Initialise the lights
   */
  private function initLights():void {
   //light = new DirectionalLight3D({color:0xFFFFFF, ambient:0.25, diffuse:0.75, specular:0.9, x:40000, y:40000, z:40000});
   light = new DirectionalLight3D();
   light.color = 0xFFFFFF;
   light.ambient = 0.25;
   light.diffuse = 0.75;
   light.specular = 0.9;
   light.x = 0;
   light.y = 1000;
   light.z = 1000;

   scene.addChild(light);
  }

  /**
   * Initialise the materials
   */
  private function initMaterials():void {
   material = new MovieMaterial(mc);
   material.interactive = true;
  }

  /**
   * Initialise the scene objects
   */
  private function initObjects():void {
   //model1 = Collada.parse(Charmesh, {scaling:10, material:material, mouseEnabled:false});
   collada = new Collada();
   //collada.scaling = 0.1;
   model = collada.parse(colladaData) as ObjectContainer3D;
   model.materialLibrary.getMaterial("mat1").material = material;
   model.mouseEnabled = true;
   model.addOnMouseDown(onModelMouseDown);
   scene.addChild(model);
  }

  /**
   * Initialise the listeners
   */
  private function initListeners():void {
   addEventListener(Event.ENTER_FRAME, onEnterFrame);
   stage.addEventListener(Event.RESIZE, onResize);
   stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
   stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
   stage.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);
   onResize();
  }

  /**
   * Navigation and render loop
   */
  private function onEnterFrame(event:Event):void {
   //update the camera position
   if(isMouseDown) {
    camera.targetpanangle = 0.35 * (stage.mouseX - lastMouseX) + lastPanAngle;
    camera.targettiltangle = 0.35 * (stage.mouseY - lastMouseY) + lastTiltAngle;
   }
   camera.hover();

   //render scene
   view.render();
  }

  private function onModelMouseDown(e:MouseEvent3D):void {
   mc.surface.graphics.beginFill(0xFFFFFF);
   mc.surface.graphics.drawCircle(e.uv.u * mc.width, mc.height - (e.uv.v * mc.height), 100);
   mc.surface.graphics.endFill();
  }

  private function onMouseDown(event:MouseEvent):void {
   lastPanAngle = camera.targetpanangle;
   lastTiltAngle = camera.targettiltangle;
   lastMouseX = stage.mouseX;
   lastMouseY = stage.mouseY;
   isMouseDown = true;
   stage.addEventListener(Event.MOUSE_LEAVE, onStageMouseLeave);
  }

  private function onMouseUp(event:MouseEvent):void {
   isMouseDown = false;
   stage.removeEventListener(Event.MOUSE_LEAVE, onStageMouseLeave);
  }

  private function onMouseWheel(e:MouseEvent):void {
   camera.distance -= e.delta * 1.5;
   if(camera.distance < 0) {
    camera.distance = 0;
   } else if(camera.distance > 70) {
    camera.distance = 70;
   }
  }

  private function onStageMouseLeave(event:Event):void {
   isMouseDown = false;
   stage.removeEventListener(Event.MOUSE_LEAVE, onStageMouseLeave);
  }

  /**
   * stage listener for resize events
   */
  private function onResize(event:Event = null):void {
   view.x = stage.stageWidth / 2;
   view.y = stage.stageHeight / 2;
  }
 }
}

UV座標は、
material = new MovieMaterial(mc);
material.interactive = true;
MovieMaterialを使ってinteractiveをtrueにして、
private function onModelMouseDown(e:MouseEvent3D):void {
 mc.surface.graphics.beginFill(0xFFFFFF);
 mc.surface.graphics.drawCircle(e.uv.u * mc.width, mc.height - (e.uv.v * mc.height), 100);
 mc.surface.graphics.endFill();
}
MouseEvent3Dのuvで取得できます。
Y座標は「e.uv.v * mc.height」かと思ったんですが、「mc.height - (e.uv.v * mc.height)」でした。
わかりづらいね。

参考サイト
blog.tartiflop » Blog Archive » First steps in Away3D : Part 3 - Texture mapping
Tutorials (category) > Tutorials > Flash Magazine

カメラの動きは相変わらずよくわからない。

2009年11月21日土曜日

[Away3D]ライトとか。

Away3DでCollada(DAE)オブジェクトにお絵かき、みたいなことをやろうとしたけどやり方がわからなかったので違うものを作ってみました。
Away3Dでクリックした座標をどうやってとってくるのかがよくわからないんだよなー。Pv3DだとVirtualMouseとか使ってたけどどうやるんだろ?

今回はライトの3つの値(ambient, diffuse, specular)をスライダーで変更してみました。
こちら。
いつものようにソースコードは右クリック→「ソースの表示」で見られます。
自分も大学のCG論のOpenGLで少しやったぐらいなのであまり詳しくないですが、ambientは環境光、diffuseは散乱光、specularは反射です。

次はまだよく理解していないカメラの動きを勉強する。

2009年11月10日火曜日

[Away3D]Away3DでShader試してみた[AS3.0]

前回言ってた続きです。

Papervision3DでUVマッピングを使用したColladaがうまく読み込めなかったので、Away3Dで試してみました。
Away3Dは検索しても解説してくれているところがあまりないので、サンプルファイルを参考にしながら(というかコピーして)、UVマッピングを使用したColladaを表示してみました。
それでできたものがこれ。

Pv3Dで表示されてた黒い線が消えてるじゃないですかー。
ぅわーい。

あ、Away3DはこちらにあるようにShaderの種類がPv3Dより少ないので、PhongShaderを使ったんですが、非常に重かったのでモデルデータを作り直しました。

というわけで全然完成してないけどソースコード公開するよ。
package {
 import away3d.cameras.HoverCamera3D;
 import away3d.containers.ObjectContainer3D;
 import away3d.containers.Scene3D;
 import away3d.containers.View3D;
 import away3d.core.base.Mesh;
 import away3d.core.utils.Cast;
 import away3d.lights.DirectionalLight3D;
 import away3d.loaders.Collada;
 import away3d.materials.PhongBitmapMaterial;
 
 import flash.display.Sprite;
 import flash.display.StageQuality;
 import flash.events.Event;
 import flash.events.MouseEvent;

 public class away3 extends Sprite {

  private var scene:Scene3D;
  private var camera:HoverCamera3D;
  private var view:View3D;

  //[Embed(source="mat.png")]
  //public var materialImage:Class;
  //[Embed(source="sample.dae", mimeType="application/octet-stream")]
  //public var colladaData:Class;
  [Embed(source="mate.png")]
  public var materialImage:Class;
  [Embed(source="test.dae", mimeType="application/octet-stream")]
  public var colladaData:Class;

  private var material:PhongBitmapMaterial;
  private var collada:Collada;
  private var model:ObjectContainer3D;
  private var mesh:Mesh;
  private var light:DirectionalLight3D;

  private var isMouseDown:Boolean;
  //navigation variables
  private var lastPanAngle:Number;
  private var lastTiltAngle:Number;
  private var lastMouseX:Number;
  private var lastMouseY:Number;

  public function away3() {
   initEngine();
   initLights();
   initMaterials();
   initObjects();
   initListeners();

   isMouseDown = false;
  }

  /**
   * Initialise the engine
   */
  private function initEngine():void {
   scene = new Scene3D();
   camera = new HoverCamera3D();
   camera.zoom = 1;
   camera.focus = 100;
   camera.distance = 80;
   camera.targetpanangle = camera.panangle = -10;
   camera.targettiltangle = camera.tiltangle = 20;

   view = new View3D({camera: camera, scene: scene});
   //view.session = new BitmapRenderSession(1);

   //view.mouseZeroMove = true;
   addChild(view);

   stage.quality = StageQuality.HIGH;
   //stage.quality = StageQuality.LOW;
  }

  /**
   * Initialise the lights
   */
  private function initLights():void {

   //light = new DirectionalLight3D({color:0xFFFFFF, ambient:0.25, diffuse:0.75, specular:0.9, x:40000, y:40000, z:40000});
   light = new DirectionalLight3D();
   light.color = 0xFFFFFF;
   light.ambient = 0.25;
   light.diffuse = 0.75;
   light.specular = 0.9;
   light.x = 0;
   light.y = 1000;
   light.z = 1000;

   scene.addChild(light);
  }

  /**
   * Initialise the materials
   */
  private function initMaterials():void {
   material = new PhongBitmapMaterial(Cast.bitmap(materialImage));
   material.specular = 0.5;
  }

  /**
   * Initialise the scene objects
   */
  private function initObjects():void {
   //model1 = Collada.parse(Charmesh, {scaling:10, material:material, mouseEnabled:false});
   collada = new Collada();
   //collada.scaling = 0.1;
   model = collada.parse(colladaData) as ObjectContainer3D;
   model.materialLibrary.getMaterial("mat1").material = material;
   model.mouseEnabled = false;

   scene.addChild(model);

   //mesh = model.getChildByName("obj1-node") as Mesh;
  }

  /**
   * Initialise the listeners
   */
  private function initListeners():void {
   addEventListener(Event.ENTER_FRAME, onEnterFrame);
   stage.addEventListener(Event.RESIZE, onResize);
   stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
   stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
   //stage.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);
   onResize();
  }

  /**
   * Navigation and render loop
   */
  private function onEnterFrame(event:Event):void {
   //update the camera position
   //camera.moveTo(0, 70, -10);
   if(isMouseDown) {
    camera.targetpanangle = 0.3 * (stage.mouseX - lastMouseX) + lastPanAngle;
    camera.targettiltangle = 0.3 * (stage.mouseY - lastMouseY) + lastTiltAngle;
   }
   camera.hover();

   //render scene
   view.render();
  }

  private function onMouseDown(event:MouseEvent):void {
   lastPanAngle = camera.targetpanangle;
   lastTiltAngle = camera.targettiltangle;
   lastMouseX = stage.mouseX;
   lastMouseY = stage.mouseY;
   isMouseDown = true;
   stage.addEventListener(Event.MOUSE_LEAVE, onStageMouseLeave);
  }

  private function onMouseUp(event:MouseEvent):void {
   isMouseDown = false;
   stage.removeEventListener(Event.MOUSE_LEAVE, onStageMouseLeave);
  }
  
  /*private function onMouseWheel(e:MouseEvent):void {
   if(camera.distance > 20) {
    camera.distance -= e.delta * 1.5;
   }
  }*/

  private function onStageMouseLeave(event:Event):void {
   isMouseDown = false;
   stage.removeEventListener(Event.MOUSE_LEAVE, onStageMouseLeave);
  }

  /**
   * stage listener for resize events
   */
  private function onResize(event:Event = null):void {
   view.x = stage.stageWidth / 2;
   view.y = stage.stageHeight / 2;
  }
 }
}

2009年10月24日土曜日

[Pv3D]UVマッピングの問題


UVマッピングを使用したテクスチャに、Shaderを適用すると出てくる例の黒い線を消すべくいろいろやってみた。

日本語のページを探してもこの問題の解決策を見つけられなかったので、英語で調べてみるとこんなものを見つけました。
まさに自分と同じ現象、しかも解決策書いてある!というわけで早速試してみました。

解決方法が書いてあるのはこの一文。
a fix is to change the shadercomposition mode to per_tri on the shader. This will be slower but remove the seams.
意訳すると、「シェーダーのshadercomposition modeをper_triにすれば、重くなるけど線消えるよ。」って感じでしょうか?

shadercomposition modeってのは、ShadedMaterialのプロパティにshaderCompositeModeというのがあるので多分これのことでしょう。
shaderCompositeModeのデフォルトの値は0。
また、ShaderCompositeModesに次のような記述があります。
public static var PER_LAYER:int = 0;
public static var PER_TRIANGLE_IN_BITMAP:int = 1;
per_triというのは、おそらくこのPER_TRIANGLE_IN_BITMAPのこと。
というわけで、
var material:ShadedMaterial = new ShadedMaterial(bitmapmaterial, shader, 1);
という風に記述しました。3つ目の引数がshaderCompositeModeです。

こちらが作ったswfファイルです。重いので反応遅れます。
ソースコードは右クリックで見られます。
他の目的で作ったものをブログ用に適当に改変してるので非効率的な書き方になってます。

上のファイルを見てもらえればわかりますが、この方法を試しても駄目でした。
例の線は消えてはいるようですが、それとはまた別のものが表示されるようになってしまいました。
原因はよくわかりません。

自分は解決しませんでしたが、これで解決する場合もあるかもしれません。

2009年10月18日日曜日

反省とメモ

最近改めて以前公開したソースコードを見直したりしてますが、今見直すと修正したいものが結構あります。
ブログは他人にも見られるものなので一応修正しておきます。

この記事についての訂正です。

var myMaterials:Object = {
    material_2_1_8_image: new BitmapMaterial(material0.bitmapData),
    material_3_2_8_image: new BitmapMaterial(material1.bitmapData),
    material_4_3_8_image: new BitmapMaterial(material2.bitmapData),
中略
    material_28_37_8_image: new BitmapMaterial(material35.bitmapData),
    material_22_38_8_image: new BitmapMaterial(material36.bitmapData),
    material_0_23_8_image: new BitmapMaterial(material37.bitmapData)
}
var daeObject:DAE = new DAE();
daeObject.load(XML(new daeFile()), new MaterialsList(myMaterials));
scene.addChild(daeObject);
Object型は文字『-』を使うとエラーが出たので、Colladaファイルの『-image』を『_image』に置換してました。
これを今書き直すならこうします。
var myMaterials:MaterialsList = new MaterialsList();
myMaterials.addMaterial(new BitmapMaterial(material0.bitmapData), "material_2_1_8-image");
myMaterials.addMaterial(new BitmapMaterial(material1.bitmapData), "material_3_2_8-image");
myMaterials.addMaterial(new BitmapMaterial(material2.bitmapData), "material_4_3_8-image");
中略
myMaterials.addMaterial(new BitmapMaterial(material35.bitmapData), "material_28_37_8-image");
myMaterials.addMaterial(new BitmapMaterial(material36.bitmapData), "material_22_38_8-image");
myMaterials.addMaterial(new BitmapMaterial(material37.bitmapData), "material_0_23_8-image");

var daeObject:DAE = new DAE(false);
daeObject.load(XML(new daeFile()), myMaterials);
scene.addChild(daeObject);
訂正版はコンパイルしたわけではないので、もしかしたらエラーが出るかも・・・
MaterialsList.addMaterialを使えば、文字『-』を使っても問題ない(はず)ですので、こっちのほうがいいです。
ソースコードも多少わかりやすいと思います。


あと、気になった記事のメモ。
Papervision 3D Programming Tutorial - Blend Modes
いろいろあるんですね。

単調なウェブサイトのデザインに手を加えてクオリティをアップするチュートリアル

必ずモノにしたい! ウェブデザイン向け Photoshop チュートリアル16個

cssのテクニックなどを学びたい時に見ておきたい12サイト
未チェック。そのうち見る。

[JS]画像に見出しとキャプションをスライド表示させるチュートリアル
jQuery。

お前等ノートパソコンにステッカー貼らないよな?
ちょっと作ってみたいかも。

2009年10月12日月曜日

[Pv3D]Phong shadingとBump mapping[AS3.0]


久しぶりの更新です。
自転車で東京へ行くっていう馬鹿なことしてたので時間がありませんでした。

さて本題。
Papervision3DのPhongShaderを使ってバンプマッピングをしてみました。
バンプマッピングってのは白黒の画像で表面の凹凸を表現する方法のことらしいです。
こんな感じ。結構CPUに負荷かかるので気をつけてください。

クリックした場所のバンプマップを書き換えてます。
某アニメの最終話みたいに月に落書きします。
note.x | [PV3D2.0] BumpMapの動的書き換え
上の記事を参考に書きました。
月の画像はこちらのを使わせてもらいました。

あまりいい書き方ではないと思いますが、以下ソースコードです。
変数名とか関数名とかかなり適当。
package {
 import flash.display.Bitmap;
 import flash.events.Event;
 import flash.geom.Point;
 import flash.geom.Rectangle;

 import org.papervision3d.core.utils.InteractiveSceneManager;
 import org.papervision3d.core.utils.Mouse3D;
 import org.papervision3d.core.utils.virtualmouse.VirtualMouse;
 import org.papervision3d.lights.PointLight3D;
 import org.papervision3d.materials.BitmapMaterial;
 import org.papervision3d.materials.shaders.PhongShader;
 import org.papervision3d.materials.shaders.ShadedMaterial;
 import org.papervision3d.objects.primitives.Plane;
 import org.papervision3d.objects.primitives.Sphere;
 import org.papervision3d.view.BasicView;

 [SWF(backgroundColor="#ffffff")]
 public class sample_bump extends BasicView {
  // ヴァーチャルマウス
  private var vMouse:VirtualMouse;
  // 球(月)
  private var sphere:Sphere;
  // 月のバンプマップ埋め込み
  [Embed(source="moonbump1k.jpg")] private var bumpImage:Class;
  private var bumpMap:Bitmap = new bumpImage() as Bitmap;
  // シェーダー
  private var shader:PhongShader;

  /* constructor */
  public function sample_bump() {
   super(640, 480, true, true,"Target");

   // カメラ設定
   camera.x = 0;
   camera.y = 0;
   camera.z = 400;
   camera.focus = 1000;
   camera.zoom = 1;

   vMouse = viewport.interactiveSceneManager.virtualMouse;
   Mouse3D.enabled = true;

   /* 背景画像は画像を貼り付けたPlane(平面)を球(月)の奥に設置している */
   // 背景画像埋め込み
   [Embed(source="000191_m.jpg")] var wallpaper:Class;
   var wp:Bitmap = new wallpaper() as Bitmap;
   var star:BitmapMaterial = new BitmapMaterial(wp.bitmapData);
   star.doubleSided = true;
   star.smooth = true;
   var wall:Plane = new Plane(star, 1024, 681);
   wall.z = -200;
   scene.addChild(wall);

   // 月の画像埋め込み
   [Embed(source="moonmap1k.jpg")] var texture:Class;
   var tex:Bitmap = new texture() as Bitmap;
   // ライト設定
   var light:PointLight3D = new PointLight3D();
   light.x = 0;
   light.y = 200;
   light.z = 500;
   scene.addChild(light);

   var bmpMat:BitmapMaterial = new BitmapMaterial(tex.bitmapData);
   shader = new PhongShader(light, 0xFFFFFF, 0x666666, 50, bumpMap.bitmapData, tex.bitmapData);
   var material:ShadedMaterial = new ShadedMaterial(bmpMat, shader);
   material.smooth = true;
   material.interactive = true;

   sphere = new Sphere(material, 100, 16, 12);
   scene.addChild(sphere);

   startRendering();
  }

  // バンプマップ埋め込み
  [Embed(source="bump.png")] private var craterImage:Class;
  private var crater:Bitmap = new craterImage() as Bitmap;
  private function meteor():void {
   // バンプマップ合成
   bumpMap.bitmapData.merge(crater.bitmapData, new Rectangle(0, 0, crater.width, crater.height), new Point(vMouse.x*1000 - crater.width/2, vMouse.y*500 - crater.height/2), 128, 128, 128, 128);

   // バンプマップ更新
   shader.bumpmap = bumpMap.bitmapData;
  }

  override protected function onRenderTick(event:Event=null):void {
   // 回転
   sphere.yaw(0.25);
   if(InteractiveSceneManager.MOUSE_IS_DOWN) {
    meteor();
   }

   super.onRenderTick(event);
  }
 }
}

2009年9月3日木曜日

[Pv3D]Collada(.dae)にShader試してみた[AS3.0]

Papervision3DでShader試してみました。
Shaderってのは、3Dで光、それによって発生する影を表現するもの、と思っておけばいいです。自分も詳しくは知りません。
やはり影がないと3Dって感じがしないですよね。ベタ塗りだと奥行きとかわからないですし。

このようになりました。
ソースは右クリックで見てください。

おー、一気に3Dっぽくなったー。
PhongShaderだと重いので、今回はGouraudShaderを使用しました。マシンによっては重いかも。

まぁ見ればわかりますが、問題があります。
表面に黒い線のツギハギができてしまっています。

原因はテクスチャマッピング関係だと思います。

このサンプルのモデル、テクスチャ画像は下のようになっています。

つまりこの一枚の画像を、テクスチャマッピング(割り当て)をして、モデルに対応させて表示しています。

おそらく、この問題の原因は、テクスチャの継ぎ目のところに影を生成してしまってるからだと思います。

この問題を解決して綺麗に表示したいのですが方法がわかりません。
さてこれはどうしたものか・・・

--------------------------------------
追記
解決しませんでしたが、こちらで黒い線の問題を取り上げました。

さらに追記
別のライブラリを使って問題を解決してみました。
こちら。

2009年8月14日金曜日

ちょっと遊び

Parpervision3Dの2.1が公開されてますね。
DAEまわりが改善されてるようで嬉しいです。
変えてみたら以前はエラーが出てたものが動くようになったりしました。

というわけでちょっと更新。
内容自体は前のとほとんど変わりません。

初音ミクのモデルデータに落書きをできるようにしてみた。→■
かなーり適当です。左右対称に描かれてしまうところが多いです。
ちなみに、このプログラムはPv3Dが2.0だとエラーが出て動きません。2.1だと動いたので公開してみました。

モデルのデータはこちらからいただきました。謝々。

2009年7月16日木曜日

[Pv3D][Flex]Collada(.dae)にお絵かき[AS3.0]

これまた前回の続きです。

こんな感じです。
現時点で不具合は多いですが、基本の動作としてはこんな感じかな~と・・・
右下の「編集する」を選択すると編集モードに入ります。

[PV3D2.0] virtualMouse axis on DAE
Flash Player 10でPV3D上の落書きを保存してみる
↑2つを参考にし、matsu4512さんに助けてもらいながら作りました。

ソースコード
demo.mxml World3D.as

現時点でお絵かきが上手くいってない原因はここらへんが原因かなー。
material = new MovieMaterial(movieClip, false, true, false, new Rectangle(0, 0, bmpData.width, bmpData.height));


---
お絵かきをするにはクリックしたマテリアルの情報を得る必要があるのですが次のように行ってます。

private function modelComplete(e:FileLoadEvent):void {
var dae_rootNode:DisplayObject3D = dae.getChildByName("COLLADA_Scene");
var targetMesh:DisplayObject3D = dae_rootNode.getChildByName("Model").getChildByName("mesh1");
targetMesh.addEventListener(InteractiveScene3DEvent.OBJECT_MOVE, drawModel);
}

private function drawModel(e:InteractiveScene3DEvent):void {
if(isModelOver) {
targetMaterial = int(e.renderHitData.material.name);
}
}

mesh1っていうDisplayObject3Dからマテリアルの名前をどうやって取得するかわからなかったので、Flex Builderの補完機能をつかってそれっぽいのを片っ端から試してたら、
『e.renderHitData.material.name』で取得することができることが判明。
この"renderHitData"って調べてもあまり情報がないんですよね~。
まあ問題なく(?)動いてるので大丈夫かな。

ちなみに、「getChildByName("Model").getChildByName("mesh1")」の"Model"と"mesh1"は

<library_visual_scenes>
<visual_scene id="SketchUpScene" name="SketchUpScene">
<node id="Model" name="Model">
<node id="mesh1" name="mesh1">
<instance_geometry url="#mesh1-geometry">
<bind_material>
<technique_common>
<instance_material symbol="material0" target="#material0ID">
<bind_vertex_input semantic="UVSET0" input_semantic="TEXCOORD" input_set="0"/>
</instance_material>
<instance_material symbol="material1" target="#material1ID">
<bind_vertex_input semantic="UVSET0" input_semantic="TEXCOORD" input_set="0"/>
</instance_material>
中略
<instance_material symbol="material6" target="#material6ID">
<bind_vertex_input semantic="UVSET0" input_semantic="TEXCOORD" input_set="0"/>
</instance_material>
</technique_common>
</bind_material>
</instance_geometry>
</node>
<node id="mesh2" name="mesh2">
<instance_geometry url="#mesh2-geometry">
<bind_material>
<technique_common>
<instance_material symbol="ForegroundColor" target="#ForegroundColorID"/>
</technique_common>
</bind_material>
</instance_geometry>
</node>
中略
</visual_scene>
</library_visual_scenes>

daeファイルのこの部分の『node』の名前からきてます。
mesh1のほかにもmesh2とかがありますが、マテリアルが含まれてるのはmesh1のほうなのでそっちです。
書き出したソフトによって多少違いがあるとは思いますが他も同じようなもののはず。

2009年7月3日金曜日

[Pv3D][Flex]Collada(.dae)のマテリアル張り替え

前回の続き?

前回3DギャラリーからとってきたCollada形式のデータはテクスチャの枚数が多かったので今回は自分でGoogle SketchUpを使ってモデルを作ってみました。形はかなり適当ですが・・・

動作サンプル
ソースコードは右クリックで表示できます。

背景が白いところでドラッグ&ドロップで視点移動、マウスホイールでズームイン&アウトします。
ズームは正確にはズームではなく、カメラごと対象に近づく(or遠ざける)ようになってます。
「ロード」ボタンを押してから、「はりかえ」ボタンを押すとテクスチャがランダムに変わります。
エラーが出ると思うので、「ロード」を押す前に「はりかえ」を押さないでください。

DAEに渡すマテリアルリストの作成は次のようにやってます。

var material0:BitmapMaterial = new BitmapFileMaterial("model3/images/texture0.jpg");
var material1:BitmapMaterial = new BitmapFileMaterial("model3/images/texture1.jpg");
var material2:BitmapMaterial = new BitmapFileMaterial("model3/images/texture2.jpg");
var material3:BitmapMaterial = new BitmapFileMaterial("model3/images/texture3.jpg");
var material4:BitmapMaterial = new BitmapFileMaterial("model3/images/texture4.jpg");
var material5:BitmapMaterial = new BitmapFileMaterial("model3/images/texture5.jpg");
var material6:BitmapMaterial = new BitmapFileMaterial("model3/images/texture6.jpg");
var material7:BitmapMaterial = new BitmapFileMaterial("model3/images/texture7.jpg");
var material8:BitmapMaterial = new BitmapFileMaterial("model3/images/texture8.jpg");
var material9:BitmapMaterial = new BitmapFileMaterial("model3/images/texture9.jpg");
var myMaterials:Object = {
material0: material0,
material1: material1,
material2: material2,
material3: material3,
material4: material4,
material5: material5,
material6: material6,
material7: material7,
material8: material8,
material9: material9
}
texture = new MaterialsList(myMaterials);

myMaterialsの中身の『material0: material0』ですが、
これは、『Colladaファイルのマテリアルの名前: 上で宣言しているBitmapMaterial型の変数material0』です。名前同じでわかりづらいですね、すみません。
で、「Colladaファイルのマテリアルの名前」はCollada(DAE)ファイルを調べればわかります。
ColladaってのはXMLで書かれているので、適当なテキストエディタで開くと、次のようになっています。

<library_images>
<image id="material0-image" name="material0-image">
<init_from>../images/texture0.jpg</init_from>
</image>
<image id="material1-image" name="material1-image">
<init_from>../images/texture1.jpg</init_from>
</image>
中略
<image id="material8-image" name="material8-image">
<init_from>../images/texture8.jpg</init_from>
</image>
<image id="material9-image" name="material9-image">
<init_from>../images/texture9.jpg</init_from>
</image>
</library_images>
<library_materials>
<material id="material0ID" name="material0">
<instance_effect url="#material0-effect"/>
</material>
<material id="FrontColorNoCullingID" name="FrontColorNoCulling">
<instance_effect url="#FrontColorNoCulling-effect"/>
</material>
<material id="material1ID" name="material1">
<instance_effect url="#material1-effect"/>
</material>
<material id="material2ID" name="material2">
<instance_effect url="#material2-effect"/>
</material>
中略
<material id="material8ID" name="material8">
<instance_effect url="#material8-effect"/>
</material>
<material id="material9ID" name="material9">
<instance_effect url="#material9-effect"/>
</material>
<material id="ForegroundColorID" name="ForegroundColor">
<instance_effect url="#ForegroundColor-effect"/>
</material>
</library_materials>

書き出したソフトによって記述に多少の違いはありますが、『<material id="material0ID" name="material0">』のnameがマテリアルの名前になります。

あとは、

dae.load("○○.dae", 作成したマテリアルリスト);

と書けばロードされます。
普通はCollada自体でもテクスチャを指定してありますが、これで渡したマテリアルリストのほうが優先されるっぽいです。

マテリアルの張り替えには「replaceMaterialByName」を使ってます。こんな感じで。

var material:BitmapMaterial = new BitmapFileMaterial("model3/images/texture" + Math.round(Math.random()*9) + ".jpg");
dae.replaceMaterialByName(material, "material0");



今回参考にさせてもらったサイト
http://pv3d.org/2008/12/26/switching-a-moviematerial-on-the-face-of-a-cube-replacematerialbyname/
http://d.hatena.ne.jp/matsu4512/20090701

2009年6月18日木曜日

[Pv3D]Papervision3Dをやってみた その3[AS3.0]


今回はCollada形式(DAE)のファイルをPv3Dで読み込んでみました。

完成品とソースコード

まずGoogleの3Dギャラリーから適当なファイルをダウンロード。
利用規約は読んだけど多分問題はないはず。

拡張子daeのファイルがあるので、それをソースフォルダに持っていき、あとは、
var dae:DAE = new DAE();
dae.load("model.dae");
scene.addChild(dae);

と書けば表示されます。
だたし、こちらで解説されているように"org/papervision3d/core/proto/MaterialObject3D.as"の"tiled"の値を"true"にしないと正しく表示されません。

これだけだったらあまり苦労はしません。
Flex Builder上で実行するのにはエラーはでません。しかし、生成したswfファイルを実行すると、「ローカルファイルシステムの SWF および信頼されているローカル SWF ファイルのみがローカルリソースにアクセスできます。」といったエラーが発生します。
これを解決するには、モデルとテクスチャのファイルの埋め込みが必要になります。
今回一番苦労したのがこれです。


テクスチャファイルの埋め込みと、そのマテリアルリストの作成はこんな感じです。
[Embed(source='model/images/texture0.jpg')]
private var texture0:Class;    // 画像埋め込み
var material0:Bitmap = new texture0() as Bitmap;
var myMaterials:Object = {
material_2_1_8_image: new BitmapMaterial(material0.bitmapData)
}

上の「material_2_1_8_image」の部分は、daeファイルをテキストエディタで開き、画像ファイル名を検索すると、このようになっています。
<image id="material_0_23_8_image" name="material_0_23_8_image">
<init_from>../images/texture0.png</init_from>
</image>

この"id"と"name"の値です。
自分が使用したdaeファイルは、この値が「material_0_23_8-image」というようになっていましたが、"-"を含むとASでエラーになるので、"_"に置換しています。


モデルデータの埋め込みとロードはこんな感じです。
[Embed(source="model/model.dae", mimeType="application/octet-stream")]
var daeFile:Class;
var daeObject:DAE = new DAE();
daeObject.load(XML(new daeFile()), new MaterialsList(myMaterials));


テクスチャの画像が多いと埋め込みが非常に面倒です。
鮭さんのほうで紹介されていたこちらを使わせてもらいました。便利便利。
今回の完成品ですが、一部のテクスチャの表示がうまくいってません。見逃してください。


今回の参考サイトはこちらです。
http://sakeprog.cocolog-nifty.com/sake/2008/08/papervision3d38_7285.html
http://blog.r3c7.net/?p=113
http://d.hatena.ne.jp/sakusan_net/20080702/1215003280


----------------------------------------
2009/10/18 追記
一部訂正しました。→こちら