Papervision3D - CurlingPlane.as
2008.6.29 日曜日CurlingPlane.as を使ってみた。
以前、Papervision3Dについて色々調べてたら、
note.xさんというところで面白そうなもの見つけたので自分も作ってみた。
参照:Papervision3Dメモ #29
と、言ってもこれ作ったの随分前だけど…フォルダの隅に埋もれてたので蔵出し。
AS3の話とかPapervision3Dの話とか一切無しでいきなりこんな物を出してくるこのブログの無節操ぶり。
var obj:CurlingPlane = new CurlingPlane(picData:BitmapData, curl:int);
// curl = planeの分割数
obj.rotate(0);
scene.addChild( obj );
// curl = planeの分割数
obj.rotate(0);
scene.addChild( obj );
で、planeオブジェクトを生成して、
obj.rotate(angle:number);
// angle = めくれの角度
// angle = めくれの角度
でページをめくる。
ページめくりを作る際、ページの裏表を表現するために、表向きのPlaneと裏返しのPlaneを貼り合わせて作る感じになるんだけど、
オリジナルの状態では裏返しのページを作る事が出来ないので、裏返しページ用に反転画像を作ったりする必要があったり、少々面倒。
公開元の、続きのエントリのコメント欄で、Planeを裏返しにする処理を追加する方法が記されているので、これを適用して使ってます。
- /*
- CurlingPlane.as
- Lee Felarca
- http://www.zeropointnine.com/blog
- v0.8.1 - 4-22-2007
- Mimics the curling of a sheet of paper, as if a page is being turned in a book.
- Source code licensed under a Creative Commons Attribution 3.0 License.
- http://creativecommons.org/licenses/by/3.0/
- Some Rights Reserved. Improvements made to this class are encouraged.
- HISTORY:
- v0.8.1 Removed sharedPresets() method, as it turned out to be little use.
- Added tesselation parameter to constructor.
- v0.8.0 First public release
- */
- package
- {
- import org.papervision3d.core.*;
- import org.papervision3d.core.proto.*;
- import org.papervision3d.core.geom.*;
- import org.papervision3d.materials.BitmapMaterial;
- import flash.display.BitmapData;
- import flash.geom.Matrix;
- public class CurlingPlane extends Mesh3D
- {
- public static const MAX_ROTATION:Number = 180;
- private var segmentsH:int;
- private var stripsU:Array = new Array();
- private var vStripWidths:Array = new Array();
- private var origVerts:Array = new Array();
- private var vertices:Array;
- private const DEGREE:Number = Math.PI / 180;
- // hard-coded magic numbers (!) :P
- private var boundary1:Number = 55;
- private var boundary2:Number = 130;
- private var boundary1Mod:Number = 1.35;
- private var boundary2Mod:Number = 0.6;
- private var segmentsW:int = 5;
- //public function CurlingPlane(bmp:BitmapData, numHorizontalStrips:int = 5)
- public function CurlingPlane(bmp:BitmapData, numHorizontalStrips:int = 5, invert:Boolean = false)
- {
- /*
- * PARAMETERS:
- *
- * bmp The BitmapData image to be mapped to the plane.
- *
- * numHorizontalStrips Number of horizontal strips to be tesselated. More strips = less
- * texture perspective distortion, slower performance. Can be set as low
- * as 1. The default value of 5 seems to work pretty well for medium-sized
- * images. (Note that the number of vertical strips is hardcoded to 5.)
- */
- segmentsH = Math.floor(numHorizontalStrips);
- if (segmentsH < 1) segmentsH = 1;
- // spaces segmentsW in a logarithmic progression
- for (var i:int = 0; i <= segmentsW; i++)
- stripsU[i] = Math.log(i+1) / Math.log(segmentsW+1);
- /////////////////////
- var myBitmapData:BitmapData
- if (invert) {
- var myMatrix:Matrix = new Matrix();
- myMatrix.rotate(Math.PI);
- myMatrix.translate(bmp.width, bmp.height);
- myBitmapData = new BitmapData(bmp.width, bmp.height);
- myBitmapData.draw(bmp,myMatrix);
- } else {
- myBitmapData = bmp;
- }
- /////////////////////
- super( new BitmapMaterial( myBitmapData ), new Array(), new Array(), null, null );
- this.material.smooth = true; // take note!
- vertices = this.geometry.vertices;
- var width:Number = bmp.width;
- var height:Number = bmp.height;
- var faces:Array = this.geometry.faces;
- var gridX :Number = segmentsW;
- var gridY :Number = segmentsH;
- var gridX1 :Number = gridX + 1;
- var gridY1 :Number = gridY + 1;
- var iW :Number = width / gridX;
- var iH :Number = height / gridY;
- // Vertices
- for( var ix:int = 0; ix < gridX1; ix++ )
- {
- for( var iy:int = 0; iy < gridY1; iy++ )
- {
- var idx:Number = Math.floor( ix / (segmentsH+1) );
- var x:Number = stripsU[idx] * width;
- var y :Number = iy * iH - (height/2);
- vertices.push( new Vertex3D( x, y, 0 ) );
- }
- }
- if(invert) {
- vertices.reverse();
- }
- // Faces
- var uvA :NumberUV;
- var uvC :NumberUV;
- var uvB :NumberUV;
- for( ix = 0; ix < gridX; ix++ )
- {
- for( iy= 0; iy < gridY; iy++ )
- {
- // Triangle A
- var a:Vertex3D = vertices[ ix * gridY1 + iy ];
- var c:Vertex3D = vertices[ ix * gridY1 + (iy+1) ];
- var b:Vertex3D = vertices[ (ix+1) * gridY1 + iy ];
- uvA = new NumberUV( stripsU[ix], iy / gridY );
- uvC = new NumberUV( stripsU[ix], (iy+1) / gridY );
- uvB = new NumberUV( stripsU[ix+1], iy / gridY );
- faces.push( new Face3D( [ a, b, c ], null, [ uvA, uvB, uvC ] ) );
- // Triangle B
- a = vertices[ (ix+1) * gridY1 + (iy+1) ];
- c = vertices[ (ix+1) * gridY1 + iy ];
- b = vertices[ ix * gridY1 + (iy+1) ];
- uvA = new NumberUV( stripsU[ix+1], (iy+1) / gridY );
- uvC = new NumberUV( stripsU[ix+1], iy / gridY );
- uvB = new NumberUV( stripsU[ix], (iy+1) / gridY );
- faces.push( new Face3D( [ a, b, c ], null, [ uvA, uvB, uvC ] ) );
- }
- }
- // store the (unrotated) verts
- for (var l:int = 0; l < vertices.length; l++) {
- origVerts[l] = new Vertex3D();
- origVerts[l].x = vertices[l].x;
- origVerts[l].y = vertices[l].y;
- origVerts[l].z = vertices[l].z;
- }
- // calc rectangle widths
- vStripWidths = new Array();
- for (var m:int = 0; m < segmentsW; m++) {
- vStripWidths[m] = stripsU[m+1] * width - stripsU[m] * width;
- }
- this.geometry.ready = true;
- }
- public function rotate( degree:Number) : void
- {
- if (degree<0) degree=0;
- if (degree>MAX_ROTATION) degree=MAX_ROTATION;
- applyVerts( calcVerts(degree) );
- }
- private function calcVerts( degRot:Number ) : Array
- {
- if (degRot<0) degRot=0;
- if (degRot>MAX_ROTATION) degRot=MAX_ROTATION;
- var myVerts:Array = new Array();
- var vStripRots:Array = new Array();
- var r:Number = -degRot * DEGREE;
- var rMod:Number;
- // Calculate rotation amounts for each rectangle and store in vStripRots
- // [A] Applies to all degrees
- rMod = boundary1Mod;
- // [B] Applies to all degrees > boundary1
- if (degRot > boundary1) {
- var a:Number = degRot - boundary1; // range: 0 to MAX_ROTATION - B1
- a = a / (boundary2 - boundary1) * boundary2Mod; // range: 0 to B2MOD
- rMod -= a; // range: B1MOD to B1MOD-B2MOD
- }
- // Recursively multiply vStripRots elements by rMod
- for (var i:int=0; i < segmentsW; i++) {
- vStripRots[i] = r;
- r *= rMod;
- }
- // [C] Applies to degrees > boundary2.
- // Grow vStripRots proportionally to MAX_ROT. (Note the 'additive' nature of these 3 steps).
- if (degRot >= boundary2) {
- for (var j:int=0; j < vStripRots.length; j++) {
- var diff:Number = MAX_ROTATION*DEGREE - Math.abs(vStripRots[j]);
- var rotMult:Number = degRot - boundary2; // range: 0 to 30
- rotMult = rotMult / (MAX_ROTATION - boundary2); // range: 0 to 1
- vStripRots[j] -= diff * rotMult; // range: __ to MAX_ROTATION
- }
- }
- // [2] Create myVerts[]
- for (var k:int=0; k < vertices.length; k++) {
- var idx:Number = Math.floor( k / (segmentsH+1) ) - 1;
- if (idx>=0) {
- myVerts[k] = new Vertex3D( vStripWidths[idx], origVerts[k].y, origVerts[k].z );
- } else {
- myVerts[k] = new Vertex3D( origVerts[k].x, origVerts[k].y, origVerts[k].z );
- }
- }
- // [3] Apply rotation to myVerts[]
- for (var l:int = segmentsH+1; l < myVerts.length; l++) {
- var idx2:Number = Math.floor( l / (segmentsH+1) ) - 1;
- myVerts[l] = rotateVertexY( myVerts[l], vStripRots[idx2] )
- }
- // [4] 'connect' the rectangles
- for (var m:int = (segmentsH+1) * 2; m < myVerts.length; m++) { // (first 2 edges are fine)
- myVerts[m].x += myVerts[ m - (segmentsH+1) ].x;
- myVerts[m].z += myVerts[ m - (segmentsH+1) ].z; // (y stays constant)
- }
- return myVerts;
- }
- private function applyVerts( v:Array ) : void
- {
- for (var i:int = 0; i < vertices.length; i++) {
- vertices[i].x = v[i].x;
- vertices[i].y = v[i].y;
- vertices[i].z = v[i].z;
- }
- }
- private function rotateVertexY( p:Vertex3D, angleY:Number ):Vertex3D
- {
- var x:Number = Math.cos(angleY) * p.x - Math.sin(angleY) * p.z;
- var z:Number = Math.cos(angleY) * p.z + Math.sin(angleY) * p.x;
- var y:Number = p.y;
- return new Vertex3D(x, y, z);
- }
- }
- }
※赤字は追加・変更した箇所
記されてなかったけど、flash.geom.Matrixをインポートしないとエラーになったので追記。
こうすると
var obj:CurlingPlane = new CurlingPlane(picData:BitmapData, curl:int, invert:Boolean );
// curl = planeの分割数
// invert = 裏返しにするかどうか(true:裏返し)
// curl = planeの分割数
// invert = 裏返しにするかどうか(true:裏返し)
という定義になり、
ページを作る際、
表向き用1枚+裏返し用1枚を組み合わせて1ページを作る形に。
一応ソースも置いておきます。
3d_test04.zip(Flash CS3用)
※パブリッシュにはPaperVision3Dが必要です。
FLEXやFlashDeveropに移行しちゃった方が作業早そうなんだけど、
仕事でデザイナー側のFlash担当と連携することが多いので、未だにFlashの方が使用頻度高い…。
