您的位置:首页 > 产品设计 > UI/UE

《Essential Guide》读书笔记【3】【第4章】

2013-01-02 20:19 387 查看

1,什么是Mesh?它的由来?

书上解释说:A mesh is a collection of vertices made visible by a collection of elements that use those vertices. As a saving measure, vertices can be reused by multiple elements in a mesh; it is rarely necessary to build elements with unique vertex points. A
simple cube, for example, can have its shape defined by eight vertices, positioned at each corner. To create a solid object, these vertices are shared between twelve faces; six sides composed of two triangles each.

我们知道,away3d里面有基本的数据结构类,如Number3D,它不继承自任何flash原生类。再高级点的数据结构类是Vertex,再高级的是Segment和Face,他们的继承关系是:

Vertex ->ValueObject->flash.events.EventDispatcher

Face->Element->flash.events.EventDispatcher

Segment->Element->flash.events.EventDispatcher

像Vertex,Face,Segment类,都分别包含了在3D空间创建一个点,一个面,一段线的足够信息。只可惜它们不是Object3D的子类,因此不具备显示功能。我们看Mesh类的继承关系:

Mesh->Object3D->flash.events.EventDispatcher

它直接继承自ObjectD类,类似一个具备显示功能的数据容器,我们通过它自带的addFace(),addSegment()方法,往容器内添加数据信息,这样引擎就能识别这些数据结构并最终渲染到屏幕。到这儿就回答了什么是Mesh类。我们看看如何用Mesh类把一个Face实例显示到屏幕上:

TestMesh.as [Set Document Class]

package
{
import away3d.animators.VertexAnimator;
import away3d.containers.View3D;
import away3d.core.base.Face;
import away3d.core.base.Mesh;
import away3d.core.base.Segment;
import away3d.core.base.Vertex;
import away3d.materials.ColorMaterial;
import away3d.materials.WireColorMaterial;
import away3d.materials.WireframeMaterial;
import flash.display.Sprite;
import flash.events.Event;

/**
* ...
* @author wws
*/
[SWF(width='550',height='400',backgroundColor='0x000000')]
public class TestMesh extends Sprite
{
private var _view:View3D;
private var mesh:Mesh;
public function TestMesh()
{
_view = new View3D( { x:275, y:200 } );
//对Mesh容器内的元素,使用绿色的外框,背面为白色材质,正面为红色材质,不启用“背面隐藏”。
mesh=new Mesh( {back:new ColorMaterial(0xffffff), material:new WireColorMaterial(0xff0000,{wireColor:0xffff00}), bothsides: 1});
var v1:Vertex = new Vertex(100, 0, 1000);
var v2:Vertex = new Vertex( -100, 0, 1000);
var v3:Vertex = new Vertex( 0, 100, 1000);
var face1:Face=new Face(v1,v2,v3);//创建数据结构类Face的一个instance
_view.scene.addChild(mesh);
mesh.addFace(face1);//向mesh容器添加数据对象(通常叫做element),引擎会处理并最终渲染出mesh内部的element。
stage.addChild(_view);
stage.addEventListener(Event.ENTER_FRAME, render);
}

private function render(e:Event = null):void {
_view.render();
}

}

}
效果图:


再看Mesh类的由来,这其实也就是在回答另一个问题:Away3d为什么不内建继承自Object3D的数据类,而非要使用【抽象数据类+具备显示功能的数据容器类】这种方式呢?

这是因为通过把显示对象的数据信息抽象成Face,Element,Vertex等数据类,能降低内存消耗,表现在两方面:

一是书上说的“As a saving measure, vertices can be reused by multiple elements in a mesh; it is rarely necessary to build elements with unique vertex points”,这即是《3D编程大师》上说的“顶点列表+点索引机制”,举例说明:3D对象的相邻面会频繁的共享顶点,我们把所有的顶点单独存储到一个数组,而Face只用数组索引来描述,存储一个索引值的内存花费要比存储一个Vertex实例小得多。(最后多说一句:简单起见,我上面的代码并没有使用“顶点列表+点索引机制”)

二是网上看到的说法:多个Mesh实例可能共用某些数据结构类。

2,面法线指向与正面朝向的关系

这是因引擎而异的。

away3d这样规定:The direction the normal vector points is calculated such that if we were to observe the face by looking along its normal vector (so that it is pointing away from us), the vertices making up the face would be arranged in a counterclockwise order。看出来面法线的指向与正面朝向是相反的。

再看《3D编程大师》里规定的:



从if(...),then poly is visible这句,看得出surface normal的指向与正面朝向是一致的。

引擎判断一个面是否为正面的步骤是:

1,根据组成面的顶点时针顺序,通过叉积得到面法线向量。(高等数学里的叉积运算常在右手坐标系,《3D编程大师》和away3d都使用左手坐标系)

2,接下来就因引擎而异了,像比《3D编程大师》里判定正面指向就是面法线朝向,away3d的判定正面指向是面法线朝向的反方向。

举个例子,就看上一节代码,face1的顶点排列是(100,0,1000),(-100,0,1000),(0,100,1000),顺时针排列,面法线逆向z轴正方向,因此face1正面朝向z轴正方向。我们从(0,0,-1000)处观察,只能看到白色的背影了。

3,Mesh+Element才是基础的显示方式

away3d的入门教程,基本上都会先创建一个sphere或cube,这容易给人错觉:似乎Primitive类是基础的显示对象类。其实不然。Primitive类只是包装了Mesh类,Primitive->AbstractPrimitive->Object3D-> flash.events.EventDispatcher。要想显示对象,Mesh+Element已经足够了。

下面就用mesh+face写一个obj文件解析类,以说明mesh的基础性。(因为obj格式我只了解到v元素和f元素,这个类的简陋程度可想而知)

ObjParser.as

//.obj file should be stored in unicode.
//atten:in .obj file,new line with a "\n",not "\r\n"
//atten:vertice index start by 1,not 0
package
{
import away3d.core.base.Face;
import away3d.core.base.Mesh;
import away3d.core.base.Vertex;
import away3d.materials.WireColorMaterial;
import flash.display.Sprite;
import flash.events.Event;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.net.URLLoaderDataFormat;
import flash.text.TextField;
/**
* ...
* @author wws
*/
public class ObjParser
{
private var vertices:Array;
private var scaler:Number;
public  var mesh:Mesh;
//默认材质是银灰色,坐标缩放比例为10
public function ObjParser(path:String,matColor:uint=0xcc0000,_scaler:Number=10)
{
vertices = new Array();
scaler = _scaler;
mesh = new Mesh( { material:new WireColorMaterial(matColor), bothsides:0 } );
var request:URLRequest = new URLRequest(path);
var urlLoader:URLLoader = new URLLoader(request);
urlLoader.dataFormat = URLLoaderDataFormat.TEXT;
urlLoader.addEventListener(Event.COMPLETE, completeHandler);
}

private function completeHandler(e:Event = null):void {
var srcStr:String = e.target.data;
readVertices(srcStr);
readFaces(srcStr);
}

private function readFaces(str:String):void {
//pattern_f寻找以f开头的面顶点描述行,
var pattern_f:RegExp =/\nf\s/;
//pattern_number匹配顶点索引值
var pattern_number:RegExp =/\s[0-9]+/g;
var fIndex:int = str.search(pattern_f) + 1;
trace("fIndex=" + fIndex);
pattern_number.lastIndex = fIndex;
while (str.charAt(fIndex) == 'f' && str.charAt(fIndex + 1) == ' ') {
var valueArr:Array = new Array();
var resultArr:Array = new Array();
for (var i:int = 0; i <= 3; i++) {
resultArr = pattern_number.exec(str);
valueArr[i] = Number(resultArr[0]);
}
var nextLeadingIndex:int = str.indexOf("\n", pattern_number.lastIndex) + 1;
fIndex = pattern_number.lastIndex = nextLeadingIndex;
//obj格式下,面以四边形存储,将其分解成两个三角形
var v1:Vertex = vertices[valueArr[0] - 1];
var v2:Vertex = vertices[valueArr[1] - 1];
var v3:Vertex = vertices[valueArr[2] - 1];
var v4:Vertex = vertices[valueArr[3] - 1];
mesh.addFace(new Face(v1,v2,v3));
mesh.addFace(new Face(v1,v3,v4));
}
trace("faces-read finished..");
trace("mesh.faces.length=" + mesh.faces.length+"mesh.vetices.length="+mesh.vertices.length);
}
private function readVertices(str:String):void {
//pattern_v寻找以v开头的顶点描述行,
var pattern_v:RegExp =/\nv\s/;
//pattern_number匹配顶点坐标值
var pattern_number:RegExp =/\s\S+\b/g;
var vIndex:int = str.search(pattern_v) + 1;
pattern_number.lastIndex = vIndex;
while (str.charAt(vIndex) == 'v'&&str.charAt(vIndex+1)==' ') {
var valueArr:Array = new Array();
for (var i:int = 0; i <= 2; i++) {
var arr:Array = pattern_number.exec(str);
valueArr[i] = Number(arr[0])*scaler;
}
vertices[vertices.length] = new Vertex(valueArr[0], valueArr[1], valueArr[2]);
pattern_number.lastIndex++;//point at 'v'
vIndex = pattern_number.lastIndex;
}
trace("vertice-read finished..");
trace("vertices.length=" + vertices.length);
}

}

}


Main.as[Set Document Class]

package
{
import away3d.containers.View3D;
import away3d.materials.Material;
import away3d.materials.WireColorMaterial;
import away3d.primitives.Cube;
import flash.display.Sprite;
import flash.events.Event;

/**
* ...
* @author wws
*/
[SWF(width='550',height='400')]
public class Main extends Sprite
{
private var _view:View3D;
private var _objParser:ObjParser;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}

private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
stage.addEventListener(Event.ENTER_FRAME, render);
// entry point

_view = new View3D({x:275,y:200});
_objParser = new ObjParser("D:/wws/3d/lib/zuqiu.obj", 0xcccccc, 20);
_view.scene.addChild(_objParser.mesh);
this.addChild(_view);

}

private function render(e:Event = null):void {
_view.render();
//Utils是个静态类,display方法功能旋转物体
Utils.display(_objParser.mesh);

}
}

}
效果图:



zuqiu.obj(点击下载)文件很大,完全加载后效果很漂亮,这个类只能加载一部分。

4,Mesh构造函数里的outline参数有用么

摘自文档:

Mesh()constructor
public function Mesh(init:Object = null)
Parameters
init:Object
(default =
null
)
 — [optional] An initialisation object for specifying default instance properties.
Init Parameters
outline:Material
material:Material
back:Material
bothsides:Boolean
(default = false)
我用下面的代码测试,死活得不到绿色(0x00ff00)的mesh外框:

TestMesh.as [Set Document Class]

package
{
import away3d.animators.VertexAnimator;
import away3d.containers.View3D;
import away3d.core.base.Face;
import away3d.core.base.Mesh;
import away3d.core.base.Segment;
import away3d.core.base.Vertex;
import away3d.materials.ColorMaterial;
import away3d.materials.WireColorMaterial;
import away3d.materials.WireframeMaterial;
import away3d.primitives.Cube;
import flash.display.Sprite;
import flash.events.Event;

/**
* ...
* @author wws
*/
[SWF(width='550',height='400',backgroundColor='0x00ffcc')]
public class TestMesh extends Sprite
{
private var cube1:Cube;
private var _view:View3D;
public function TestMesh()
{
_view = new View3D( { x:275, y:200 } );
//因为担心outline太细,我特意将thickness属性设为3
var mesh:Mesh = new Mesh( { outline:new WireframeMaterial(0x00ff00,{thickness:3}), back:new ColorMaterial(0xffffff), material:new WireColorMaterial(0xff0000,{wireColor:0xffff00}), bothsides: 1});
var v1:Vertex = new Vertex(100, 0, 1000);
var v2:Vertex = new Vertex( -100, 0, 1000);
var v3:Vertex = new Vertex(0, 100, 1000);
var v4:Vertex = new Vertex(0, -50, 0);
var segment1:Segment = new Segment(v1,v4);
var f1:Face = new Face(v2, v3, v1);
_view.scene.addChild(mesh);
mesh.addFace(f1);
mesh.addSegment(segment1);
stage.addChild(_view);
stage.addEventListener(Event.ENTER_FRAME, render);
}

private function render(e:Event = null):void {
_view.render();
}

}

}
效果图:



没有绿色边框...

我猜测是segment1影响了边框,于是把mesh.addSegment(segment1);这一行去掉,结果依然是光秃秃的白色三角形。

留作问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: