My 3D Engine
---------------------------------------------------------------------
5〜6年前にAS2で書いた3D描画ライブラリを発掘したのでAS3用に修正して晒してみるテスト
---------------------------------------------------------------------
/**
* Copyright hycro ( http://wonderfl.net/user/hycro )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/66rS
*/
// ---------------------------------------------------------------------
// 5〜6年前にAS2で書いた3D描画ライブラリを発掘したのでAS3用に修正して晒してみるテスト
// ---------------------------------------------------------------------
package
{
import flash.display.Shape;
import flash.display.Sprite;
import flash.display.StageScaleMode;
import flash.events.Event;
import net.hires.debug.Stats;
public class My3DEngineTest extends Sprite
{
private const CUBE_NUM:uint = 300;
private var _scr:Screen;
private var _canvas:Shape;
private var _cubes:Vector.<Cube>
public function My3DEngineTest()
{
stage.frameRate = 60;
stage.scaleMode = StageScaleMode.NO_SCALE;
// 座標変換や描画を行うScreenのインスタンスを作成
_scr = new Screen();
// カメラの焦点距離を設定
_scr.setFocusValue(300);
// 注視点の設定
_scr.setLookingpoint(0, 0, 0);
// 描画用
_canvas = new Shape();
_canvas.x = stage.stageWidth / 2;
_canvas.y = stage.stageHeight / 2;
addChild(_canvas);
// 立方体モデルを作成
_cubes = new Vector.<Cube>();
for (var i:uint=0; i<CUBE_NUM; i++) {
var cube:Cube = new Cube(Model.POLYGON, 20);
cube.setPolygonColor(Math.random() * 0xffffff);
cube.translate((Math.random()-.5)*300, (Math.random()-.5)*300, (Math.random()-.5)*300);
_scr.addModel(cube);
_cubes.push(cube);
}
// 球体モデルを作成
//var sphere:Sphere = new Sphere(30, 10, 10);
//_scr.addModel(sphere);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
addChild(new Stats);
}
private var t:Number = 0;
private function onEnterFrame(evt:Event):void {
t += .01
// カメラの位置
_scr.setViewpoint(350*Math.cos(t), 350*Math.sin(t), 350*Math.sin(t));
// 光源の位置(平行光線の方向ベクトル)を設定
_scr.setLight(300*Math.cos(t), 300, 300*Math.sin(t));
_canvas.graphics.clear();
for each (var cube:Cube in _cubes) {
// 原点を中心に回転
cube.rotate(1, 1, 1);
// 移動
//cube.translate((Math.random()-.5)*10,(Math.random()-.5)*10,(Math.random()-.5)*10);
// 拡大縮小
//cube.scale(1+(Math.random()-.5)*.01,1+(Math.random()-.5)*.01,1+(Math.random()-.5)*.01);
}
// 透視投影でポリゴンを描画
_scr.drawPerspectivePolygon(_canvas.graphics);
// 投資投影でワイアーフレームを描画
//_scr.drawPerspectiveWire(_canvas.graphics);
// 平行投影でポリゴンを描画
//_scr.draw(_canvas.graphics);
}
}
}
//package simple3d {
import flash.display.*;
/*public*/ class Screen {
private var nViewpointX:Number, nViewpointY:Number, nViewpointZ:Number; // 視点
private var nLookingpointX:Number, nLookingpointY:Number, nLookingpointZ:Number; // 注視点
private var arModels:Array; // 配置するモデル
private var mLight:Light; // 拡散反射平行光線
private var nFocus:Number; // 焦点距離
// コンストラクタ
function Screen() {
setViewpoint(0, 0, 100);
setLookingpoint(0, 0, 0);
setFocusValue(200);
arModels = new Array();
mLight = new Light(1, 1 , 1);
}
// モデルを追加
public function addModel(model:Model):void {
arModels.push(model);
}
// 焦点距離を設定
public function setFocusValue( n:Number ):void {
nFocus = n;
}
// 視点座標を設定
public function setViewpoint( x:Number, y:Number, z:Number ):void {
nViewpointX = x;
nViewpointY = y;
nViewpointZ = z;
}
// 注視点座標を設定
public function setLookingpoint( x:Number, y:Number, z:Number ):void {
nLookingpointX = x;
nLookingpointY = y;
nLookingpointZ = z;
}
// 光源方向を設定
public function setLight( x:Number, y:Number, z:Number ):void {
mLight.arVertex[1][0] = x;
mLight.arVertex[1][1] = y;
mLight.arVertex[1][2] = z;
}
//透視投影でのワイヤーフレーム表示
public function drawPerspectiveWire(m:Graphics):void {
var nScrX:Number, nScrY:Number;
var i:int, j:int, k:int;
var mCurr:Model; // 現在描写しているモデル
var arViewVertex:Array; // 視点座標系での頂点の座標値
var arScrVertex:Array = new Array(); // スクリーン座標系での頂点の座標値
for (i=0; i<arModels.length; i++) {
mCurr = arModels[i];
// 視野変換
arViewVertex = viewingTransform(mCurr);
// 投影変換
for (j=0; j<mCurr.arVertex.length; j++) {
nScrX = nFocus*arViewVertex[j][0] / ( nFocus + arViewVertex[j][2] );
nScrY = nFocus*arViewVertex[j][1] / ( nFocus + arViewVertex[j][2] );
arScrVertex[j] = [nScrX, nScrY];
}
// 描画
m.lineStyle( mCurr.nEdgeSize, mCurr.nEdgeColor );
for ( j=0; j<mCurr.arEdge.length; j++ ) {
m.moveTo(arScrVertex[mCurr.arEdge[j][0]][0], -arScrVertex[mCurr.arEdge[j][0]][1]);
for (k=1; k<mCurr.arEdge[j].length; k++)
m.lineTo(arScrVertex[mCurr.arEdge[j][k]][0], -arScrVertex[mCurr.arEdge[j][k]][1]);
}
}
}
//透視投影でのポリゴン表示
public function drawPerspectivePolygon(m:Graphics):void {
var i:int, j:int, k:int;
var mCurr:Model; // 現在描写しているモデル
var arScrVertex:Array = new Array(); // スクリーン座標系での頂点の座標値
var nScrX:Number, nScrY:Number;
var arNormal:Array;
var nInnerProduct:Number;
var nViewVectX:Number, nViewVectY:Number, nViewVectZ:Number; // 視点から頂点へのベクトル
var nTotalZ:Number;
var nLightX:Number, nLightY:Number, nLightZ:Number, nLightLength:Number; // 光線の方向ベクトル
var nRef:Number;
var nCos:Number;
var nColor:Number, nR:Number, nG:Number, nB:Number;
var arViewVertex:Array, arPolygon:Array;
// カメラ座標系における光線の方向ベクトルを求める
mLight.arViewVertex = viewingTransform(mLight);
nLightX = mLight.arViewVertex[0][0] - mLight.arViewVertex[1][0];
nLightY = mLight.arViewVertex[0][1] - mLight.arViewVertex[1][1];
nLightZ = mLight.arViewVertex[0][2] - mLight.arViewVertex[1][2];
nLightLength = Math.sqrt( nLightX*nLightX + nLightY*nLightY + nLightZ*nLightZ );
// 視野変換を行い、奥にあるものから順に並ぶようにモデルをソートする
for ( i=0; i<arModels.length; i++ ) {
mCurr = arModels[i];
// 視野変換
mCurr.arViewVertex = viewingTransform(mCurr);
// 重心の z 座標を計算
nTotalZ = 0;
for ( j=0; j<mCurr.arViewVertex.length; j++ )
nTotalZ += mCurr.arViewVertex[j][2];
mCurr.nCenterZ = nTotalZ / mCurr.arViewVertex.length;
}
arModels.sortOn("nCenterZ", Array.NUMERIC );
for (i=arModels.length-1; i>=0; i--) {
mCurr = arModels[i];
arViewVertex = mCurr.arViewVertex;
arPolygon = mCurr.arPolygon;
// 投影変換
for (j=0; j<mCurr.arVertex.length; j++) {
nScrX = nFocus*arViewVertex[j][0] / ( arViewVertex[j][2] );
nScrY = nFocus*arViewVertex[j][1] / ( arViewVertex[j][2] );
arScrVertex[j] = [nScrX, nScrY];
}
// 各ポリゴンの法線ベクトルを求める
arNormal = calcNormal(mCurr);
for ( j=0; j<mCurr.arPolygon.length; j++ ) {
// 視点ベクトルを求める
nViewVectX = arViewVertex[ mCurr.arPolygon[j][0] ][0];
nViewVectY = arViewVertex[ mCurr.arPolygon[j][0] ][1];
nViewVectZ = arViewVertex[ mCurr.arPolygon[j][0] ][2] + nFocus;
// 法線ベクトルと視点ベクトルの内積を求める
nInnerProduct = arNormal[j][0]*nViewVectX + arNormal[j][1]*nViewVectY + arNormal[j][2]*nViewVectZ;
//m.lineStyle( mCurr.nEdgeSize, mCurr.nEdgeColor );
if ( nInnerProduct > 0 ) {
// 光線の方向ベクトルと法線ベクトルのなす余弦(cos)を計算
nCos = (arNormal[j][0]*nLightX + arNormal[j][1]*nLightY + arNormal[j][2]*nLightZ )
/ Math.sqrt(arNormal[j][0]*arNormal[j][0] + arNormal[j][1]*arNormal[j][1] + arNormal[j][2]*arNormal[j][2] ) / nLightLength;
if ( nCos < 0 ) nCos = 0;
nRef = 0.3 * nCos + 0.7;
// 反射率からポリゴンの色を計算
nR = Math.floor((mCurr.nPolygonColor >>> 16) * nRef);
nG = Math.floor(((mCurr.nPolygonColor & 0x00FFFF) >>> 8 ) * nRef);
nB = Math.floor((mCurr.nPolygonColor & 0x0000FF) * nRef);
nColor = nR * 0x010000 + nG * 0x000100 + nB;
m.lineStyle( mCurr.nEdgeSize, mCurr.nEdgeColor, 0 );
m.moveTo( arScrVertex[arPolygon[j][0]][0], -arScrVertex[arPolygon[j][0]][1]);
m.beginFill( nColor, 100);
for (k=1; k<mCurr.arPolygon[j].length; k++)
m.lineTo(arScrVertex[arPolygon[j][k]][0], -arScrVertex[arPolygon[j][k]][1]);
m.lineTo( arScrVertex[arPolygon[j][0]][0], -arScrVertex[arPolygon[j][0]][1]);
m.endFill();
}
}
}
}
//透視投影でモデルを表示
public function draw(m:Graphics):void {
var i:int, j:int, k:int;
var mCurr:Model; // 現在描写しているモデル
var arScrVertex:Array = []; // スクリーン座標系での頂点の座標値
var nScrX:Number, nScrY:Number;
var arNormal:Array;
var nInnerProduct:Number;
var nViewVectX:Number, nViewVectY:Number, nViewVectZ:Number; // 視点から頂点へのベクトル
var nTotalZ:Number;
var nLightX:Number, nLightY:Number, nLightZ:Number, nLightLength:Number; // 光線の方向ベクトル
var nRef:Number;
var nCos:Number;
var nColor:Number, nR:Number, nG:Number, nB:Number;
var arViewVertex:Array, arPolygon:Array;
// カメラ座標系における光線の方向ベクトルを求める
mLight.arViewVertex = viewingTransform(mLight);
nLightX = mLight.arViewVertex[0][0] - mLight.arViewVertex[1][0];
nLightY = mLight.arViewVertex[0][1] - mLight.arViewVertex[1][1];
nLightZ = mLight.arViewVertex[0][2] - mLight.arViewVertex[1][2];
nLightLength = Math.sqrt( nLightX*nLightX + nLightY*nLightY + nLightZ*nLightZ );
// 視野変換を行い、奥にあるものから順に並ぶようにモデルをソートする
for ( i=0; i<arModels.length; i++ ) {
mCurr = arModels[i];
// 視野変換
mCurr.arViewVertex = viewingTransform(mCurr);
// 重心の z 座標を計算
nTotalZ = 0;
for ( j=0; j<mCurr.arViewVertex.length; j++ )
nTotalZ += mCurr.arViewVertex[j][2];
mCurr.nCenterZ = nTotalZ / mCurr.arViewVertex.length;
}
arModels.sortOn("nCenterZ", Array.NUMERIC );
// 後方にあるモデルから順に描画
for (i=arModels.length-1; i>=0; i--) {
mCurr = arModels[i];
arViewVertex = mCurr.arViewVertex;
arPolygon = mCurr.arPolygon;
// 投影変換
for (j=0; j<mCurr.arVertex.length; j++) {
nScrX = nFocus*arViewVertex[j][0] / ( arViewVertex[j][2] );
nScrY = nFocus*arViewVertex[j][1] / ( arViewVertex[j][2] );
arScrVertex[j] = [nScrX, nScrY];
}
// 各ポリゴンの法線ベクトルを求める
arNormal = calcNormal(mCurr);
for ( j=0; j<mCurr.arPolygon.length; j++ ) {
if ( mCurr.nType == Model.NULL ) continue;
// 視点ベクトルを求める
nViewVectX = arViewVertex[ mCurr.arPolygon[j][0] ][0];
nViewVectY = arViewVertex[ mCurr.arPolygon[j][0] ][1];
nViewVectZ = arViewVertex[ mCurr.arPolygon[j][0] ][2];
// 法線ベクトルと視点ベクトルの内積を求める
nInnerProduct = arNormal[j][0]*nViewVectX + arNormal[j][1]*nViewVectY + arNormal[j][2]*nViewVectZ;
if ( nInnerProduct > 0 ) {
// 光線の方向ベクトルと法線ベクトルのなす余弦(cos)を計算
nCos = (arNormal[j][0]*nLightX + arNormal[j][1]*nLightY + arNormal[j][2]*nLightZ )
/ Math.sqrt(arNormal[j][0]*arNormal[j][0] + arNormal[j][1]*arNormal[j][1] + arNormal[j][2]*arNormal[j][2] ) / nLightLength;
if ( nCos < 0 ) nCos = 0;
nRef = 0.3 * nCos + 0.7;
// 反射率からポリゴンの色を計算
nR = Math.floor((mCurr.nPolygonColor >>> 16) * nRef);
nG = Math.floor(((mCurr.nPolygonColor & 0x00FFFF) >>> 8 ) * nRef);
nB = Math.floor((mCurr.nPolygonColor & 0x0000FF) * nRef);
nColor = nR * 0x010000 + nG * 0x000100 + nB;
// モデルのタイプに応じて線を描くか否かを決める
if ( mCurr.nType == Model.POLYGON ) {
m.lineStyle( mCurr.nEdgeSize, mCurr.nEdgeColor, 0 );
}
else
m.lineStyle( mCurr.nEdgeSize, mCurr.nEdgeColor, 100 );
m.moveTo( arScrVertex[arPolygon[j][0]][0], -arScrVertex[arPolygon[j][0]][1]);
m.beginFill( nColor );
for (k=1; k<mCurr.arPolygon[j].length; k++)
m.lineTo(arScrVertex[arPolygon[j][k]][0], -arScrVertex[arPolygon[j][k]][1]);
m.lineTo( arScrVertex[arPolygon[j][0]][0], -arScrVertex[arPolygon[j][0]][1]);
m.endFill();
}
}
}
}
// 視野変換を行う
private function viewingTransform(model:Model):Array {
var arViewVertex:Array = new Array();
var nCosA:Number, nCosB:Number,nSinA:Number, nSinB:Number, nSinA_SinB:Number, nSinA_CosB:Number, nCosA_CosB:Number, nCosA_SinB:Number, nTemp1:Number, nTemp2:Number;
var nX:Number, nY:Number, nZ:Number;
var i:int, j:int;
var arVertex:Array = model.arVertex;
var vlx:Number = nViewpointX - nLookingpointX;
var vly:Number = nViewpointY - nLookingpointY;
var vlz:Number = nViewpointZ - nLookingpointZ;
nTemp1 = Math.sqrt(vlx*vlx + vlz*vlz);
nTemp2 = Math.sqrt(vlx*vlx+ vly*vly + vlz*vlz);
nCosA = vlz / nTemp1;
nSinA = -vlx / nTemp1;
nCosB = nTemp1 / nTemp2;
nSinB = vly / nTemp2;
nSinA_SinB = nSinA * nSinB;
nSinA_CosB = nSinA * nCosB;
nCosA_SinB = nCosA * nSinB;
nCosA_CosB = nCosA * nCosB;
for ( i=0; i<arVertex.length; i++) {
nX = arVertex[i][0];
nY = arVertex[i][1];
nZ = arVertex[i][2];
arViewVertex[i] = [
nCosA*(nX - nViewpointX) + nSinA*(nZ - nViewpointZ),
nSinA_SinB*(nX - nViewpointX) + nCosB*(nY - nViewpointY) - nCosA_SinB*(nZ - nViewpointZ),
nSinA_CosB*(nX - nViewpointX) - nCosA_CosB*(nZ - nViewpointZ) - nSinB*(nY - nViewpointY)
];
}
return arViewVertex;
}
// カメラ座標系における各ポリゴンの法線ベクトルの計算
private function calcNormal(model:Model):Array {
var i:int;
var ax:Number, ay:Number, az:Number, bx:Number, by:Number, bz:Number, nx:Number, ny:Number, nz:Number, abs:Number;
var arNormal:Array = new Array();
var arViewVertex:Array = model.arViewVertex;
var arPolygon:Array = model.arPolygon;
var nPolygonLength:Number = model.arPolygon.length;
for (i=0; i<nPolygonLength; i++) {
ax = arViewVertex[arPolygon[i][2]][0] - arViewVertex[arPolygon[i][1]][0];
ay = arViewVertex[arPolygon[i][2]][1] - arViewVertex[arPolygon[i][1]][1];
az = arViewVertex[arPolygon[i][2]][2] - arViewVertex[arPolygon[i][1]][2];
bx = arViewVertex[arPolygon[i][1]][0] - arViewVertex[arPolygon[i][0]][0];
by = arViewVertex[arPolygon[i][1]][1] - arViewVertex[arPolygon[i][0]][1];
bz = arViewVertex[arPolygon[i][1]][2] - arViewVertex[arPolygon[i][0]][2];
nx = ay*bz - az*by;
ny = az*bx - ax*bz;
nz = ax*by - ay*bx;
//abs = Math.sqrt( nx*nx + ny*ny + nz*nz );
//arNormal[i] = [nx/abs, ny/abs, nz/abs];
arNormal[i] = [nx, ny, nz];
}
return arNormal;
}
}
//}
//package simple3d {
/*public*/ class Model {
public static const WIRE:uint = 0;
public static const POLYGON:uint = 1;
public static const WIRE_POLYGON:uint = 2;
public static const NULL:uint = 3;
public var arVertex :Array; // 頂点の座標
public var arEdge:Array; // 辺情報
public var arPolygon:Array; // 面情報
public var nCenterZ:Number; // 重心のZ座標
public var arViewVertex:Array; // 視野変換後の頂点の座標
public var nEdgeColor:Number; // 辺の色
public var nEdgeSize:Number; // 辺の太さ
public var nPolygonColor:Number; // ポリゴンの色
public var nType:Number; // ワイヤーフレーム表示かポリゴン表示かを指定
// コンストラクタ
public function Model(t:uint) {
nEdgeColor = 0x000000;
nPolygonColor = 0xffffff;
nEdgeSize = 0;
nType = t;
}
// 頂点情報の登録
public function setVertex(v:Array):void {
arVertex = v;
}
// 辺情報の登録
public function setEdge(e:Array):void {
arEdge = e;
}
// ポリゴン情報の登録
public function setPolygon(p:Array):void {
arPolygon = p;
}
// 辺の色の設定
public function setEdgeColor(nColor:Number):void {
nEdgeColor = nColor;
}
// ポリゴンの色の設定
public function setPolygonColor(nColor:Number):void {
nPolygonColor = nColor;
}
// 辺の太さの設定
public function setEdgeSize(nSize:Number):void {
nEdgeSize = nSize;
}
// ワイヤーフレーム表示 / ポリゴン表示の変更
public function setTYpe(t:Number):void {
nType = t;
}
// モデルを伸縮する
public function scale(x:Number, y:Number, z:Number):void {
var i:int;
var nLength:Number = arVertex.length;
var arTemp:Array = arVertex;
for (i=0; i<nLength; i++) {
arTemp[i][0] *= x;
arTemp[i][1] *= y;
arTemp[i][2] *= z;
}
}
// モデルを平行移動する
public function translate(x:Number, y:Number, z:Number):void {
var i:int;
var nLength:Number = arVertex.length;
var arTemp:Array = arVertex;
for (i=0; i<nLength; i++) {
arTemp[i][0] += x;
arTemp[i][1] += y;
arTemp[i][2] += z;
}
}
// モデルを回転移動する
public function rotate(a:Number, b:Number, c:Number):void {
var i:int;
var y1:Number, z1:Number, x2:Number;
var nLength:Number = arVertex.length;
var arTemp:Array = arVertex;
var sin_a:Number, cos_a:Number, sin_b:Number, cos_b:Number, sin_c:Number, cos_c:Number;
a = a * Math.PI / 180;
b = b * Math.PI / 180;
c = c * Math.PI / 180;
sin_a = Math.sin(a);
cos_a = Math.cos(a);
sin_b = Math.sin(b);
cos_b = Math.cos(b);
sin_c = Math.sin(c);
cos_c = Math.cos(c);
for (i=0; i<nLength; i++) {
y1 = arTemp[i][1] * cos_a - arTemp[i][2] * sin_a;
z1 = arTemp[i][1] * sin_a + arTemp[i][2] * cos_a;
x2 = arTemp[i][0] * cos_b + z1 * sin_b;
arTemp[i][2] = -arTemp[i][0] * sin_b + z1 * cos_b;
arTemp[i][0] = x2 * cos_c - y1 * sin_c;
arTemp[i][1] = x2 * sin_c + y1 * cos_c;
}
}
}
//}
//package simple3d {
/*internal*/ class Light extends Model {
public function Light(x:Number, y:Number, z:Number) {
super(0);
arVertex = [[0,0,0], [x, y, z]];
}
}
//}
//package simple3d {
/*public*/ class Cube extends Model {
public function Cube(type:int, size:Number) {
super(type);
size /=2;
arVertex = [
[-size, size, -size],
[size, size, -size],
[size, size, size],
[-size, size, size],
[-size,-size, -size],
[size,-size, -size],
[size,-size, size],
[-size,-size, size]
];
arEdge = [
[ 0, 1, 2, 3, 0, 4, 5, 6, 7, 4],
[1, 5],
[2, 6],
[3, 7]
];
arPolygon = [
[ 0, 1, 2, 3 ],
[ 4, 7, 6, 5 ],
[ 0, 4, 5, 1 ],
[ 3, 2, 6, 7 ],
[ 0, 3, 7, 4 ],
[ 1, 5, 6, 2 ]
];
}
}
//}
//package simple3d {
/*public*/ class Sphere extends Model {
public function Sphere(radius:Number, slices:Number, stacks:Number, type:uint=Model.POLYGON) {
super(type);
var i:int,j:int;
var y:Number = radius;
var x:Number, z:Number;
var arTemp:Array;
var r:Number
if ( slices < 1 ) slices = 1;
if ( stacks < 3 ) stacks = 3;
// 頂点データの作成
arVertex = [
[0, radius, 0]
];
for ( i=0; i<slices; i++) {
//y = y - 2*radius/(slices+1);
y = radius * Math.cos((Math.PI/(slices+1))*(i+1));
for (j=0; j<stacks; j++) {
r = Math.sqrt(radius*radius - y*y);
x = r*Math.cos((2*Math.PI/stacks)*j);
z = r*Math.sin((2*Math.PI/stacks)*j);
arVertex.push([x, y, z]);
}
}
arVertex.push([0, -radius, 0]);
arEdge = new Array();
arTemp = new Array();
// 辺データの作成
for ( i=0; i<slices; i++ ) {
for (j=i*stacks+1; j<=(i+1)*stacks; j++) {
arTemp.push(j);
}
arTemp.push(i*stacks+1);
arEdge.push(arTemp);
arTemp = new Array();
}
for (i=1; i<=stacks; i++) {
arTemp.push(0);
for (j=0; j<slices; j++) {
arTemp.push( i + j*stacks );
}
arTemp.push(arVertex.length - 1);
arEdge.push(arTemp);
arTemp = new Array();
}
// 面データの作成
arPolygon = new Array();
for ( i=1; i<=stacks; i++) {
if (i != stacks) arPolygon.push([0, i+1, i]);
else arPolygon.push([0, 1, i]);
}
for ( i=0; i<slices-1; i++ ) {
for ( j=1; j<=stacks; j++ ) {
if ( j != stacks ) arPolygon.push([i*stacks+j, i*stacks+j+1, (i+1)*stacks+j+1, (i+1)*stacks+j]);
else arPolygon.push([i*stacks+j, i*stacks+1, (i+1)*stacks+1, (i+1)*stacks+j]);
}
}
for ( i=(slices-1)*stacks+1; i<=stacks*slices; i++) {
if (i != stacks*slices) arPolygon.push([stacks*slices+1, i, i+1]);
else arPolygon.push([stacks*slices+1, i, (slices-1)*stacks+1]);
}
}
}
//}