『 Papervision3Dでオブジェクトの原点をずらして変形する 』

2009 年 3 月 31 日

pv3d_origin_01
普通、Papervision3Dがデフォルトで用意している3Dオブジェクトは中心点がオブジェクトの中心にあるので、拡大縮小を掛けると上下左右前後同じように広がっていきます。

今回Planeを拡大させる際にx軸の負の方向には拡大せずに、x軸の正の方向へのみ伸びていく表現(上図の感じ)が必要だったのでメモ。要はオブジェクトの左端から中心点までの距離を変えずに、もう片方だけがウニョっと伸びていく表現がしたかったのです。

scaleXプロパティを使わずにやる必要があったので、かなり強引に実装しました。以後、直感的に読める(書ける)ようにx軸の負の方向を左側、x軸の正の方向を右側とします。

手順1.平行移動
pv3d_origin_02
3Dオブジェクト(object3dとする)の一番左側の頂点のx座標が0となるように、頂点全体を右側へ平行移動します。
移動幅をoffsetとすると

var offset:Number = beforeWidth / 2;

で表せます。オブジェクトの頂点情報はobject3d.geometry.vertices配列にVertex3Dクラスとして格納してあるので、for eachループで引っ張ってきます。

for each (var v:Vertex3D in object3d.geometry.vertices) {
    v.x += offset;
}

手順2.拡大縮小
pv3d_origin_03
いよいよ拡大縮小処理。全頂点のx座標を一律倍します。ウニョ。
頂点のx座標に適用する倍率をscaleとすると

var scale:Number = afterWidth / beforeWidth;

で表せます。先ほどと同じようにして頂点の座標をいじります。

for each (var v:Vertex3D in object3d.geometry.vertices) {
    v.x *= scale;
}

0には何を掛けても0なので、一番左側の座標は変わりません。これで、オブジェクトの左側の座標を原点から変えずに拡大縮小を実現できました。

手順3.平行移動再び
pv3d_origin_04
このままではオブジェクトの左端の座標がoffsetだけ右にずれてしまっているので、左端が変形前のx座標に揃うように全体を移動してあげます。これは手順1の逆操作をすればよいので

for each (var v:Vertex3D in object3d.geometry.vertices) {
    v.x -= offset;
}

これで、変形前と変形後で左側の座標と中心点を変えずに、オブジェクトを変形させることができました。
コードをまとめると、とてもシンプル。

var offset:Number = beforeWidth / 2;
var scale:Number = afterWidth / beforeWidth;
for each (var v:Vertex3D in object3d.geometry.vertices) {
    v.x = (v.x + offset) * scale - offset;
}

他にもいくつかやり方がありそうです。先に拡大縮小して、後で位置を合わせたり。紹介した方法は手順が多めですが、マトリクスを用いた代表的な変形方法とよく似ているし、分かりやすいので僕は好きです。

補足
マテリアルによっては、オブジェクトのサイズを変えると、テクスチャのクリッピング範囲も変更しないとうまく表示されない場合があるかも。特に、MovieMaterialなどをしている場合は注意。

object3d.material.rect = new Rectangle(0, 0, afterWidth, afterHeight);

のようにrectプロパティ(MovieClipのどの範囲をテクスチャとして使用するか)を適宜変更することで正しいアスペクト比でテクスチャが表示されるようになります。

なお、Vertex3Dにはextraというプロパティがあって、ユーザが自由に使えるようになっています。
ウニョウニョ何回も伸ばしたり戻したりしたい場合は、変形前後の頂点座標を最初に計算だけしておいて、各頂点のextraに突っ込んでおくと良いです。

var offset:Number = beforeWidth / 2;
var scale:Number = afterWidth / beforeWidth;
for each (var v:Vertex3D in object3d.geometry.vertices) {
    v.extra = {
        x0 : v.x, //変形前
        x1 : (v.x + offset) * scale - offset //変形後
    };
}

変形させるときは

for each (var v:Vertex3D in object3d.geometry.vertices) {
    v.x = v.extra.x1;
}

戻すときは

for each (var v:Vertex3D in object3d.geometry.vertices) {
    v.x = v.extra.x0;
}

こういった感じで。

いじょです!(o’c_,`人)・:*:・

« 
» 

2 Responses to “Papervision3Dでオブジェクトの原点をずらして変形する”

  1. [...] 構造自体は普通にDisplayObject3Dの入れ子。回転座標系のためにalumicanさんの『 Papervision3Dでオブジェクトの原点をずらして変形する 』というエントリーを参考にさせていただきました。 [...]

  2. [...] package { import flash.display.*; import flash.events.*; import flash.filters.BlurFilter; //Tweener import caurina.transitions.*; //pv3d import org.papervision3d.cameras.*; import org.papervision3d.materials.*; import org.papervision3d.materials.special.*; import org.papervision3d.materials.utils.*; import org.papervision3d.lights.PointLight3D; import org.papervision3d.materials.shadematerials.FlatShadeMaterial; import org.papervision3d.objects.primitives.*; import org.papervision3d.objects.DisplayObject3D; import org.papervision3d.render.*; import org.papervision3d.scenes.*; import org.papervision3d.view.*; import org.papervision3d.view.stats.StatsView; import org.papervision3d.core.effects.view.ReflectionView; import org.papervision3d.core.proto.CameraObject3D; import org.papervision3d.core.geom.*; import org.papervision3d.core.geom.renderables.*; public class Main extends Sprite { private var r:ReflectionView; private var planeArr:Array = new Array(); private var _plane0:Plane;// bottom private var _plane1:Plane;// front private var _plane2:Plane;// left private var _plane3:Plane;// right private var _plane4:Plane;// back private var _plane5:Plane;// top private var _wire:Plane; private var _size:uint = 100; private var _segments:uint = 6; private var cubeFlg:Boolean = true; private var rot:Number = 0; private var duration:Number = 1; public function Main():void { r = new ReflectionView(); r.surfaceHeight = -40; r.viewportReflection.filters = [new BlurFilter(8, 8, 1)]; r.viewportReflection.alpha = 0.1; addChild(r); r.camera.target = DisplayObject3D.ZERO; r.camera.y = 300; var light:PointLight3D = new PointLight3D(false, false); light.x = 100; light.y = 200; light.z = 150; r.scene.addChild(light); var material:FlatShadeMaterial = new FlatShadeMaterial(light, 0xFFFFFF, 0xCCCCCC, 50); material.doubleSided = true; var wireMat:WireframeMaterial = new WireframeMaterial(0xFF0000, 0.1, 0); _wire = new Plane( wireMat, 300, 300, _segments, _segments ); _wire.rotationX = 90; r.scene.addChild(_wire); var adj:Number = _size * .5; _plane0 = new Plane( material, _size, _size, _segments, _segments ); _plane0.rotationX = -90; _plane0.y = 20; _plane1 = new Plane( material, _size, _size, _segments, _segments ); setRootPos( _plane1, adj ); _plane1.y = adj; _plane2 = new Plane( material, _size, _size, _segments, _segments ); setRootPos( _plane2, adj ); _plane2.x = -adj; _plane2.rotationZ = 90; _plane3 = new Plane( material, _size, _size, _segments, _segments ); setRootPos( _plane3, adj ); _plane3.x = adj; _plane3.rotationZ = -90; _plane4 = new Plane( material, _size, _size, _segments, _segments ); setRootPos( _plane4, adj ); _plane4.y = -adj; _plane4.rotationZ = 180; _plane5 = new Plane( material, _size, _size, _segments, _segments ); setRootPos( _plane5, adj ); _plane5.y = adj * 2; _plane1.rotationX = 90; _plane2.rotationX = 90; _plane3.rotationX = 90; _plane4.rotationX = 90; _plane5.rotationX = 90; r.scene.addChild( _plane0 ); _plane0.addChild( _plane1 ); _plane0.addChild( _plane2 ); _plane0.addChild( _plane3 ); _plane0.addChild( _plane4 ); _plane4.addChild( _plane5 ); for (var i:uint = 0; i < 6; i++) { planeArr.push(this["_plane" + i]); } addChild(btn); btn.buttonMode = true; btn.useHandCursor = true; btn.addEventListener(MouseEvent.CLICK, clickHandler); addEventListener(Event.ENTER_FRAME, enterFrameHandler); } private function clickHandler(e:MouseEvent):void { if (cubeFlg) { cubeFlg = false; btn.gotoAndStop(2); for (var i:uint = 1; i < 6; i++) { Tweener.addTween( planeArr[i], { rotationX: 0, time: duration, transition: "easeoutbounce" }); } } else { cubeFlg = true; btn.gotoAndStop(1); for (var i:uint = 1; i < 6; i++) { Tweener.addTween( planeArr[i], { rotationX: 90, time: duration }); } } } private function setRootPos( e:Plane, val:Number ):void { for each (var v:Vertex3D in e.geometry.vertices) { v.y += val; } } private function enterFrameHandler(e:Event):void { rot += 0.5; r.camera.x = 300 * Math.sin(rot * Math.PI / 180); r.camera.z = 300 * Math.cos(rot * Math.PI / 180); r.singleRender(); } } } クラス化の際に直せるとこは直します('A';) かなり参考にさせて頂きました ・「Papervision3D – #OpenCube」:nulldesign ・「Papervision3Dでオブジェクトの原点をずらして変形する」:blog.alumican.net [...]

Leave a Reply