一个3D地球,学习一些新的绘图方法
fla代码如下:
// creates a sphere
function createSphere(radius:Number,
parallels:int,
meridians:int,
trianglePathOut:GraphicsTrianglePath,
vertices3DOut:Vector.<Number>):void {
if (parallels < 3) parallels = 3;
if (meridians < 3) meridians = 3;
meridians++; // texture edge meridian duplicated
var parallelStops:int = parallels-1; // for 决定 u
var meridianStops:int = meridians-1; // for determining v
// local variables
var r:Number; // radius
var x:Number, y:Number, z:Number; // coordinates
var p:int, pi:int, pa:Number; // parallel vars
var m:int, mi:int, ma:Number; // meridian vars
var u:Number, v:Number; // u, v of uvt
var n:int = -1; // vertices index
// horizontal
for (p=0; p<parallels; p++){
v = p/parallelStops;
pa = v*Math.PI - Math.PI/2;
y = radius*Math.sin(pa);
r = radius*Math.cos(pa);//不同高度y,半径不同
// vertical
for (m=0; m<meridians; m++){
u = m/meridianStops;
ma = u*Math.PI*2;
x = r*Math.cos(ma);
z = r*Math.sin(ma);
// vertices
vertices3DOut.push(x,y,z);
n++;
// trianglePathOut
trianglePathOut.uvtData.push(u, v, 1);
if (m != 0){ // not first meridian (texture edge)
if (p != parallelStops){ // not last parallel (no next parallel to connect)
trianglePathOut.indices.push(n, n+meridians, n+meridians-1);
trianglePathOut.indices.push(n, n+meridians-1, n-1);
}
}
}
}
}
// creates a darker version of a bitmap
function getDarkerBitmap(source:BitmapData, darkness:Number):BitmapData {
// put source in a Bitmap display object
// and darken it using a ColorTransform
var darker:Bitmap = new Bitmap(source);
darker.transform.colorTransform = new ColorTransform(darkness, darkness, darkness);
// draw() does not take in account of
// transformations on the object you draw
// so place it in a container
var container:Sprite = new Sprite();
container.addChild(darker);
// create a new BitmapData drawing
// the darker image in the container
var bmp:BitmapData = new BitmapData(source.width, source.height, source.transparent, 0);
bmp.draw(container);
return bmp;
}
// create globe shape
var globe:Shape = new Shape();
globe.x = stage.stageWidth/2;
globe.y = stage.stageHeight/2;
addChild(globe);
// 球的变量
var globeRadius:Number = 100;//球的半径
var globeParallels:int = 20;//y方向线条数
var globeMeridians:int = 20;//x方向线条数
var globeSpinSpeed:Number = 2;
var frontFace:BitmapData = new Erf(0,0);
var backFace:BitmapData = getDarkerBitmap(frontFace, .25);
var globeTransform:Matrix3D = new Matrix3D();
globeTransform.appendTranslation(0, 0, globeRadius*4); // push back in 3D space
var globeProjection:PerspectiveProjection = new PerspectiveProjection();
var globePerspective:Matrix3D = globeProjection.toMatrix3D();
//triangles和trianglesBack分别是两个网格,只是位置重合,绘制的顺序也相同即相同位置法向量相同
//它们分别删除正面或背面,这样在相同位置就只绘制其中一个网格,避开Z坐标排序
//类似双面贴图
// a GraphicsTrianglePath for the path
// data to be drawn in Flash
//GraphicsTrianglePath(vertices:Vector = null, indices:Vector = null, uvtData:Vector = null, culling:String = "none")
var triangles:GraphicsTrianglePath = new GraphicsTrianglePath(
new Vector.<Number>(), new Vector.<int>(),
new Vector.<Number>(), TriangleCulling.NEGATIVE);//剔除正面TriangleCulling.NEGATIVE
// a new GraphicsTrianglePath container using the
// same vector data as triangles is created to draw
// the backside of the sphere once the darker version
// of the bitmap is added to the drawing state
var trianglesBack:GraphicsTrianglePath = new GraphicsTrianglePath(
triangles.vertices, triangles.indices,
triangles.uvtData, TriangleCulling.POSITIVE);//剔除背面TriangleCulling.POSITIVE
// stroke for when mouse is pressed
// start with a NaN width (no stroke)
var stroke:GraphicsStroke = new GraphicsStroke(NaN, false, "normal", "none", "round", 3,
new GraphicsSolidFill(0xFF0000));//定义线条样式或笔触,定义线条实心填充
// create a vector of Number objects to store
// 3D locations of sphere coordinates
var vertices3D:Vector.<Number> = new Vector.<Number>();
// to be non-destructive, changes to the sphere
// coordinates are added to this vector
var vertices3DTransformed:Vector.<Number> = new Vector.<Number>();
// populate triangles and vertices3D and with
// sphere data; note that the triangles vector data
// is being modified but this also affects trianglesBack
// since it uses those same vectors
createSphere(globeRadius, globeParallels, globeMeridians, // in
triangles, vertices3D); // out
// IGraphicsData list of drawing commands, drawing back face
// first (dark), followed by front face (light)
var globeData:Vector.<IGraphicsData> = Vector.<IGraphicsData>([
stroke, //定义绘图线条样式
new GraphicsBitmapFill(backFace, null, false, true),//定义位图填充
trianglesBack, //定义绘图路径GraphicsTrianglePath
new GraphicsBitmapFill(frontFace, null, false, true),//定义位图填充
triangles //定义绘图路径GraphicsTrianglePath
]);
// rotate globe in frame loop
addEventListener(Event.ENTER_FRAME, draw);
function draw(event:Event):void {
// rotate the globe transform around the y axis
// using prepend allows this to be applied
// "before" the previous translation in z
globeTransform.prependRotation(-globeSpinSpeed, Vector3D.Y_AXIS);
// apply the transform to the globe vertices
// to make the globe points actually rotated
// (as well as pushed back in z)
globeTransform.transformVectors(vertices3D, // in
vertices3DTransformed); // out
// convert the 3D points to 2D points and update
// the T data in the UVT to coorectly account for
// the translation of 2D to 3D for the bitmaps
Utils3D.projectVectors(globePerspective, vertices3DTransformed, // in
triangles.vertices, triangles.uvtData); // out
// draw the triangles
globe.graphics.clear();
globe.graphics.drawGraphicsData(globeData);
}
// show outlines when pressing the mouse
stage.addEventListener(MouseEvent.MOUSE_DOWN, toggleStroke);
stage.addEventListener(MouseEvent.MOUSE_UP, toggleStroke);
function toggleStroke(event:MouseEvent):void {
stroke.thickness = (event.type == MouseEvent.MOUSE_DOWN) ? 1 : NaN;
}
function createSphere(radius:Number,
parallels:int,
meridians:int,
trianglePathOut:GraphicsTrianglePath,
vertices3DOut:Vector.<Number>):void {
if (parallels < 3) parallels = 3;
if (meridians < 3) meridians = 3;
meridians++; // texture edge meridian duplicated
var parallelStops:int = parallels-1; // for 决定 u
var meridianStops:int = meridians-1; // for determining v
// local variables
var r:Number; // radius
var x:Number, y:Number, z:Number; // coordinates
var p:int, pi:int, pa:Number; // parallel vars
var m:int, mi:int, ma:Number; // meridian vars
var u:Number, v:Number; // u, v of uvt
var n:int = -1; // vertices index
// horizontal
for (p=0; p<parallels; p++){
v = p/parallelStops;
pa = v*Math.PI - Math.PI/2;
y = radius*Math.sin(pa);
r = radius*Math.cos(pa);//不同高度y,半径不同
// vertical
for (m=0; m<meridians; m++){
u = m/meridianStops;
ma = u*Math.PI*2;
x = r*Math.cos(ma);
z = r*Math.sin(ma);
// vertices
vertices3DOut.push(x,y,z);
n++;
// trianglePathOut
trianglePathOut.uvtData.push(u, v, 1);
if (m != 0){ // not first meridian (texture edge)
if (p != parallelStops){ // not last parallel (no next parallel to connect)
trianglePathOut.indices.push(n, n+meridians, n+meridians-1);
trianglePathOut.indices.push(n, n+meridians-1, n-1);
}
}
}
}
}
// creates a darker version of a bitmap
function getDarkerBitmap(source:BitmapData, darkness:Number):BitmapData {
// put source in a Bitmap display object
// and darken it using a ColorTransform
var darker:Bitmap = new Bitmap(source);
darker.transform.colorTransform = new ColorTransform(darkness, darkness, darkness);
// draw() does not take in account of
// transformations on the object you draw
// so place it in a container
var container:Sprite = new Sprite();
container.addChild(darker);
// create a new BitmapData drawing
// the darker image in the container
var bmp:BitmapData = new BitmapData(source.width, source.height, source.transparent, 0);
bmp.draw(container);
return bmp;
}
// create globe shape
var globe:Shape = new Shape();
globe.x = stage.stageWidth/2;
globe.y = stage.stageHeight/2;
addChild(globe);
// 球的变量
var globeRadius:Number = 100;//球的半径
var globeParallels:int = 20;//y方向线条数
var globeMeridians:int = 20;//x方向线条数
var globeSpinSpeed:Number = 2;
var frontFace:BitmapData = new Erf(0,0);
var backFace:BitmapData = getDarkerBitmap(frontFace, .25);
var globeTransform:Matrix3D = new Matrix3D();
globeTransform.appendTranslation(0, 0, globeRadius*4); // push back in 3D space
var globeProjection:PerspectiveProjection = new PerspectiveProjection();
var globePerspective:Matrix3D = globeProjection.toMatrix3D();
//triangles和trianglesBack分别是两个网格,只是位置重合,绘制的顺序也相同即相同位置法向量相同
//它们分别删除正面或背面,这样在相同位置就只绘制其中一个网格,避开Z坐标排序
//类似双面贴图
// a GraphicsTrianglePath for the path
// data to be drawn in Flash
//GraphicsTrianglePath(vertices:Vector = null, indices:Vector = null, uvtData:Vector = null, culling:String = "none")
var triangles:GraphicsTrianglePath = new GraphicsTrianglePath(
new Vector.<Number>(), new Vector.<int>(),
new Vector.<Number>(), TriangleCulling.NEGATIVE);//剔除正面TriangleCulling.NEGATIVE
// a new GraphicsTrianglePath container using the
// same vector data as triangles is created to draw
// the backside of the sphere once the darker version
// of the bitmap is added to the drawing state
var trianglesBack:GraphicsTrianglePath = new GraphicsTrianglePath(
triangles.vertices, triangles.indices,
triangles.uvtData, TriangleCulling.POSITIVE);//剔除背面TriangleCulling.POSITIVE
// stroke for when mouse is pressed
// start with a NaN width (no stroke)
var stroke:GraphicsStroke = new GraphicsStroke(NaN, false, "normal", "none", "round", 3,
new GraphicsSolidFill(0xFF0000));//定义线条样式或笔触,定义线条实心填充
// create a vector of Number objects to store
// 3D locations of sphere coordinates
var vertices3D:Vector.<Number> = new Vector.<Number>();
// to be non-destructive, changes to the sphere
// coordinates are added to this vector
var vertices3DTransformed:Vector.<Number> = new Vector.<Number>();
// populate triangles and vertices3D and with
// sphere data; note that the triangles vector data
// is being modified but this also affects trianglesBack
// since it uses those same vectors
createSphere(globeRadius, globeParallels, globeMeridians, // in
triangles, vertices3D); // out
// IGraphicsData list of drawing commands, drawing back face
// first (dark), followed by front face (light)
var globeData:Vector.<IGraphicsData> = Vector.<IGraphicsData>([
stroke, //定义绘图线条样式
new GraphicsBitmapFill(backFace, null, false, true),//定义位图填充
trianglesBack, //定义绘图路径GraphicsTrianglePath
new GraphicsBitmapFill(frontFace, null, false, true),//定义位图填充
triangles //定义绘图路径GraphicsTrianglePath
]);
// rotate globe in frame loop
addEventListener(Event.ENTER_FRAME, draw);
function draw(event:Event):void {
// rotate the globe transform around the y axis
// using prepend allows this to be applied
// "before" the previous translation in z
globeTransform.prependRotation(-globeSpinSpeed, Vector3D.Y_AXIS);
// apply the transform to the globe vertices
// to make the globe points actually rotated
// (as well as pushed back in z)
globeTransform.transformVectors(vertices3D, // in
vertices3DTransformed); // out
// convert the 3D points to 2D points and update
// the T data in the UVT to coorectly account for
// the translation of 2D to 3D for the bitmaps
Utils3D.projectVectors(globePerspective, vertices3DTransformed, // in
triangles.vertices, triangles.uvtData); // out
// draw the triangles
globe.graphics.clear();
globe.graphics.drawGraphicsData(globeData);
}
// show outlines when pressing the mouse
stage.addEventListener(MouseEvent.MOUSE_DOWN, toggleStroke);
stage.addEventListener(MouseEvent.MOUSE_UP, toggleStroke);
function toggleStroke(event:MouseEvent):void {
stroke.thickness = (event.type == MouseEvent.MOUSE_DOWN) ? 1 : NaN;
}